/** * 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; confSession: Pick; 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 { 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" }; }