File size: 5,013 Bytes
cc11e77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
 * confidenceGateBlock.ts — S511: extracted from agentLoop.ts
 *
 * Gestisce tre controlli pre-API per ogni iterazione del ReAct loop:
 *   1. ReasoningCore.decide() — cervello decisionale (stop/fix/plan/continue)
 *   2. Confidence short-circuit (S418-F2) — break se score ≥ 0.72 + step eseguiti
 *   3. Borderline confidence retry (S501) — inietta hint web_search se score 0.4-0.6
 *
 * Ritorna:
 *   - { action: "break" }    → il caller fa break dal for loop
 *   - { action: "continue" } → il caller fa continue (solo reasoning stop)
 *   - { action: "ok" }       → prosegui normalmente
 *
 * Muta `loopMessages` per reference (reasoning fix/plan e borderline hint).
 *
 * @module confidenceGateBlock
 */

import type { ApiMsg } from "./networkTools";
import type { AgentStep } from "../storage";
import { evaluateBorderlineRetry } from "../confidenceGate";
import type { ConfidenceSession } from "../confidenceEngine";
import type { ReasoningCore } from "../reasoningCore";

// ─── Interfaces ────────────────────────────────────────────────────────────────

export interface ConfidenceGateCtx {
  iter: number;
  isDirectAnswer: boolean;
  steps: AgentStep[];
  loopMessages: ApiMsg[];
  lastUserMsg: string;
  signal: AbortSignal | undefined;
  reasoning: Pick<ReasoningCore, "isExhausted" | "decide">;
  confSession: Pick<ConfidenceSession, "shouldClose" | "getScore">;
  onStatus: (msg: string) => void;
}

export type ConfidenceGateAction = "break" | "continue" | "ok";

export interface ConfidenceGateResult {
  action: ConfidenceGateAction;
}

// ─── Main export ───────────────────────────────────────────────────────────────

/**
 * Esegue i tre controlli di confidence pre-API:
 * reasoning.decide() → confidence short-circuit → borderline retry.
 */
export async function runConfidenceGate(ctx: ConfidenceGateCtx): Promise<ConfidenceGateResult> {
  const { iter, isDirectAnswer, steps, loopMessages, lastUserMsg, signal, reasoning, confSession, onStatus } = ctx;

  // ── 1. ReasoningCore.decide() — P0a-fix ─────────────────────────────────
  // DIRECT-FIX: disabilitato per task a risposta diretta (isDirectAnswer → _effectiveMaxIter=1)
  if (iter > 0 && !reasoning.isExhausted() && !isDirectAnswer) {
    try {
      const _rDecision = await reasoning.decide(signal);
      if (_rDecision.action === "stop") {
        onStatus("Ci siamo ✓");
        return { action: "break" };
      }
      if (_rDecision.action === "fix" && _rDecision.reason) {
        onStatus(`✏️ Mi correggo: ${_rDecision.reason.slice(0, 80)}`);
        loopMessages.push({ role: "user", content: `[REASONING CORE — FIX RICHIESTO]\n${_rDecision.reason}\nCorreggi l'approccio e riprova. Steps suggeriti: ${_rDecision.steps.slice(0, 3).join(", ") || "cambia strategia"}` } as ApiMsg);
      }
      if (_rDecision.action === "plan" && _rDecision.steps.length > 0) {
        onStatus(`🔄 Cambio approccio: ${_rDecision.steps[0].slice(0, 60)}`);
        loopMessages.push({ role: "user", content: `[REASONING CORE — NUOVO PIANO]\n${_rDecision.steps.map((s, i) => `${i + 1}. ${s}`).join("\n")}` } as ApiMsg);
      }
      // "continue" → nessuna azione aggiuntiva, il loop prosegue normalmente
    } catch { /* non-blocking — reasoning.decide() non blocca il loop */ }
  }

  // ── 2. Confidence short-circuit (S418-F2) ────────────────────────────────
  // DIRECT-FIX: shouldClose() applicato già a iter > 0 per task a risposta diretta
  const _trivialCloseReady = isDirectAnswer ? iter > 0 : iter > 1;
  if (_trivialCloseReady && steps.length >= 1 && confSession.shouldClose()) {
    onStatus("Risposta pronta ✓");
    return { action: "break" };
  }

  // ── 3. Borderline confidence retry (S501) ────────────────────────────────
  // Attivato una sola volta per loop (iter===1) per evitare loop infiniti.
  if (iter === 1 && !confSession.shouldClose()) {
    const _brDecision = (() => {
      try { return evaluateBorderlineRetry(lastUserMsg, confSession.getScore()); } catch { return null; }
    })();
    if (_brDecision?.needsWeb && _brDecision.suggestedQuery) {
      onStatus("Verifico un'ultima cosa…");
      const _brHint = `\n\n[BORDERLINE CONFIDENCE: score=${(confSession.getScore() * 100).toFixed(0)}%. ` +
        `Esegui web_search con query: "${_brDecision.suggestedQuery}" per supportare la risposta con dati verificati prima di rispondere.]`;
      loopMessages[0] = { ...loopMessages[0], content: String(loopMessages[0].content) + _brHint };
    }
  }

  return { action: "ok" };
}