Salta al contenuto
Torna alla home
2024-11-1510 min di lettura
AI AgentsResearchLLMProduction

Perché agenti autonomi allucinano: ciclo critico

Gli agenti planner-executor falliscono sulla verificabilità. Un critico con accesso alle fonti individua affermazioni non supportate e lacune di copertura.

Perché conta

Un agente di ricerca autonomo che allucina può essere peggiore di nessun agente. Produce output dall'aspetto autorevole, include citazioni plausibili e può essere parzialmente o interamente errato. Il ricercatore che riceve l'output non ha un modo efficiente per verificarlo. Spesso è proprio per questo che il lavoro è stato delegato a un agente.

Ho costruito agenti di ricerca che hanno fallito silenziosamente esattamente in questo modo. Il planner generava sotto-domande utili. L'executor recuperava documenti reali. Poi il passaggio di sintesi cercava di trasformare risultati parziali in un report coerente sotto pressione di contesto. Colmava le lacune con affermazioni prive di fonte.

L'affermazione non era presente in alcun documento recuperato. Proveniva dai dati di training del modello, poi compariva nel report come se fosse stata oggetto di ricerca.

Il fallimento è architetturale, non riguarda solo il prompt engineering. Una coppia planner-executor senza un passaggio di verifica non può distinguere in modo affidabile tra "trovato in una fonte" e "il modello è sicuro di questo". Aggiungere un critic con accesso indipendente alle fonti è una mitigazione strutturale. Ecco perché, e come si presenta in pratica.

1. Il pattern planner-executor e i suoi limiti

La coppia planner-executor è un blueprint comune per la ricerca autonoma. Un planner scompone un argomento di alto livello in sotto-domande. Un executor recupera e riassume le risposte a ogni sotto-domanda. Il planner sintetizza quindi gli output dell'executor in un report finale.

Questo funziona quando il corpus è ben definito, le sotto-domande hanno risposte indipendenti e i documenti recuperati contengono informazioni pertinenti sufficienti. Si interrompe quando il corpus è scarso, le sotto-domande sono ambigue o il passaggio di sintesi deve riconciliare fonti parziali e contraddittorie.

La modalità di fallimento della sintesi è quella pericolosa. Il planner riceve output dell'executor quali "La fonte A afferma X" e "La fonte B è ambigua rispetto a Y". Deve comunque produrre un report coerente. Con token limitati e molte fonti, il modello può colmare le parti ambigue con ciò che si aspetta sia la risposta, sulla base dei dati di training.

L'output suona coerente. Le affermazioni non sono verificabili.

Non mi aspetto che un ciclo critic risolva corpus scarsi o domande ambigue. Il suo compito è più ristretto: far emergere il fallimento. Segnala affermazioni che non possono essere ricondotte a fonti recuperate e fornisce al planner un segnale sufficiente per eseguire nuovamente la query o contrassegnare esplicitamente l'incertezza.

2. Cosa fa effettivamente un agente critic

Il critic non è un secondo sintetizzatore. Lo uso come agente di verifica con due capacità specifiche: accesso agli stessi documenti sorgente recuperati dall'executor e un vincolo rigoroso di grounding.

Il compito è intenzionalmente ristretto. Dato il riassunto dell'executor e i documenti sorgente, il critic contrassegna ogni affermazione come grounded (presente letteralmente o parafrasata in una fonte), inferred (deriva logicamente dalle fonti ma non è dichiarata direttamente) oppure unsupported (non trovata in alcuna fonte recuperata).

from pydantic import BaseModel
from typing import Literal

class ClaimVerification(BaseModel):
    claim: str
    status: Literal["grounded", "inferred", "unsupported"]
    source_url: str | None  # required when status == "grounded"
    confidence: float       # 0.0 to 1.0

class CriticOutput(BaseModel):
    verified_claims: list[ClaimVerification]
    overall_groundedness: float  # fraction of claims that are "grounded"
    flags: list[str]             # specific issues for the planner to act on

Mantengo questo output strutturato perché il planner richiede un segnale di controllo stabile. Il critic non restituisce testo libero e non instrada il risultato nuovamente all'executor.

Il planner legge overall_groundedness e l'elenco flags, quindi sceglie il passaggio successivo. Se il grounding supera una soglia configurata, come 0.8 in un sistema in cui tale soglia è stata calibrata rispetto al rischio del task, il report può essere approvato. Se scende sotto tale soglia, il planner rimette in coda le sotto-domande segnalate con istruzioni esplicite di trovare fonti per le affermazioni non supportate.

Questo ciclo richiede anche una protezione max-iterations. Nelle mie implementazioni, preferisco un arresto rigido a un agente che continua a cercare finché non trova qualcosa di plausibile. Se il grounding non migliora dopo un piccolo numero fisso di cicli di re-query, il planner contrassegna esplicitamente le affermazioni a bassa confidenza nel report finale invece di farle passare silenziosamente.

3. Accesso indipendente alle fonti per il critic

Il critic deve avere accesso agli stessi documenti sorgente recuperati dall'executor. Non può basarsi solo sul riassunto di tali documenti prodotto dall'executor. È un requisito sul quale non transigo.

Se il critic vede soltanto il riassunto dell'executor, può verificare se il riassunto si contraddice. Non può verificare se un'affermazione è effettivamente presente nella fonte. Un riassunto può essere internamente coerente ed essere comunque errato.

# Wrong: critic sees only the summary
critic_input = {
    "summary": executor_output.summary,
    "task": "Verify the claims in this summary."
}
# Critic can check consistency but not source grounding

# Right: critic sees summary and original documents
critic_input = {
    "summary": executor_output.summary,
    "source_documents": executor_output.retrieved_docs,  # original text, not summaries
    "task": "For each claim in the summary, verify it against the source documents."
}
# Critic can map claims to specific passages

La conseguenza pratica riguarda la dimensione del contesto e il costo di retrieval. La context window del critic deve contenere il riassunto e gli estratti sorgente pertinenti, oppure deve essere abbinata a un passaggio di retrieval in grado di recuperare tali estratti in modo deterministico.

Per task di ricerca long-form con molte fonti, di solito gestisco questo aspetto in uno di due modi. Eseguo il critic su una sotto-domanda alla volta, oppure recupero passaggi sorgente solo per il passaggio del critic.

L'esecuzione per sotto-domanda mantiene il costo sotto controllo, ma può non rilevare affermazioni multi-fonte come "Le fonti A e B confermano entrambe che..." a meno che il contesto completo dell'affermazione non venga passato esplicitamente. Eseguire il critic su un corpus più ampio è più costoso, ma può rilevare contraddizioni tra fonti. Per esempio, può vedere quando due fonti indicano date diverse, cifre di fatturato diverse o interpretazioni regolatorie contrastanti per la stessa affermazione.

4. Riassunto ricorsivo e tracciamento delle citazioni

Gli agenti di ricerca autonomi recuperano frequentemente più contenuto di quanto entri in una singola context window. La risposta standard è il riassunto ricorsivo: riassumere il documento A, riassumere il documento B, quindi sintetizzare i riassunti.

Il riassunto ricorsivo è utile per la compressione. È rischioso per il tracciamento delle citazioni.

Quando il documento A viene riassunto, la versione condensata perde passaggi specifici. Il critic non può più ricondurre le affermazioni al testo esatto nel documento A. La catena probatoria è stata interrotta.

Un modo semplice per preservarla consiste nell'archiviare il documento originale insieme al riassunto, quindi passare entrambi al critic o rendere l'originale recuperabile tramite document ID e span.

class ExecutorResult(BaseModel):
    sub_question: str
    summary: str             # compressed, used for planning
    source_docs: list[str]   # original text, used for critic verification
    source_urls: list[str]

Il critic legge il riassunto per comprendere il contesto dell'affermazione. Poi cerca il passaggio specifico nel testo originale.

Questo aumenta i dati archiviati per ogni risultato dell'executor, cosa accettabile quando il corpus è abbastanza piccolo da essere conservato direttamente. Se la dimensione del corpus rende ciò impraticabile, incorporerei o indicizzerei gli originali ed eseguirei la ricerca delle citazioni al momento del critic invece di passarli inline. La parte importante è lo stato persistente: il sistema deve conservare abbastanza materiale sorgente originale da supportare il recovery deterministico della catena probatoria.

5. Map-reduce per sotto-domande indipendenti

Quando il task di ricerca si scompone in sotto-domande indipendenti — trend di mercato, panorama competitivo, contesto regolatorio — il planner può inviare l'executor in parallelo (map) e aggregare risultati verificati (reduce).

Il passaggio map invia simultaneamente tutte le sotto-domande. Il passaggio reduce sintetizza gli output verificati dell'executor in un report finale. A seconda delle dimensioni del contesto e dei vincoli di costo, il critic può essere eseguito su ogni risultato mappato, sulla sintesi finale oppure su entrambi.

Un passaggio critic finale mantiene la verifica collegata all'output finale. Se il passaggio reduce modifica la formulazione, unisce affermazioni o introduce conclusioni cross-source, tali affermazioni devono comunque essere verificate rispetto al corpus recuperato.

La modalità di fallimento da evitare nel passaggio reduce è il cross-question drift. Il planner può sintetizzare tra sotto-domande senza rilevare che la stessa affermazione è apparsa in più risultati dell'executor con valori in conflitto. "La dimensione del mercato è $5B" da una sotto-domanda e "La dimensione del mercato è $3B" da un'altra devono essere segnalate, non mediate. L'elenco flags del critic fa emergere questi conflitti.

6. Gestire esplicitamente le lacune di copertura

Un agente di ricerca che lavora con fonti scarse richiede una gestione esplicita delle lacune di copertura. Il ciclo critic deve gestire anche i casi in cui le fonti recuperate sono insufficienti per la domanda.

Il segnale consiste in affermazioni ripetutamente non supportate. Se il critic contrassegna costantemente le affermazioni come unsupported nei cicli di re-query con termini di ricerca diversi, il fallimento può riguardare la copertura del corpus, non la qualità della query. In quel caso, preferisco riportare esplicitamente la lacuna di copertura invece di produrre una risposta dall'aspetto plausibile.

class ResearchReport(BaseModel):
    findings: list[ClaimVerification]
    coverage_gaps: list[str]   # topics where sources were insufficient
    confidence: float          # overall groundedness across all findings
    generated_at: str          # ISO timestamp

Il campo coverage_gaps è obbligatorio. Faccio in modo che il planner lo compili, anche se il valore è un elenco vuoto. Uno schema di report senza un campo obbligatorio per le lacune di copertura può consentire che le lacune vengano omesse.

Le lacune di copertura esplicite sono più utili di risposte fiduciosamente errate. Un utente che vede "Lacuna di copertura: panorama regolatorio nell'UE dopo 2023 — fonti disponibili solo fino a Q2 2023" sa quale ricerca aggiuntiva è necessaria. Un utente che riceve una risposta allucinata non ha alcun segnale che qualcosa non vada finché la validazione downstream non fallisce.

Il frame della categoria

Gli agenti di ricerca autonomi che producono output verificabili richiedono tre componenti: un planner che scompone e sintetizza, un executor che recupera e riassume preservando le fonti, e un critic che verifica le affermazioni rispetto ai documenti originali.

Il ciclo critic non elimina gli errori di grounding. Rende visibili e azionabili le affermazioni non supportate. Il planner può quindi preservare la distinzione tra affermazioni supportate da fonti, inferenze giustificate e lacune di copertura invece di appiattirla in falsa confidenza.

L'obiettivo è rendere ogni affermazione riconducibile a un source URL, contrassegnata esplicitamente come inferred oppure resa visibile come lacuna di copertura. Il sistema non dovrebbe creare silenziosamente una quarta categoria.

Perché questo resti affidabile nel tempo, ho bisogno anche di eval riproducibili: task fissi, snapshot delle fonti congelati dove possibile, classificazioni delle affermazioni attese e regression check per le affermazioni non supportate che in precedenza erano sfuggite. Senza questo livello, un ciclo critic può sembrare corretto in una demo e andare in deriva man mano che cambiano prompt, modelli, impostazioni di retrieval o contenuto delle fonti.

Se stai costruendo automazione di ricerca per workflow di compliance, legali o finanziari, scrivimi e posso esaminare l'architettura di verifica rispetto ai controlli descritti qui. Posso aiutare a valutare dove la preservazione delle fonti, l'accesso del critic, il reporting delle lacune di copertura e la valutazione si inseriscano nel design del sistema.

FAQ

Perché un agente di ricerca planner-executor allucina durante la sintesi?

Vedo il fallimento pericoloso nel passaggio di sintesi. Il planner riceve output dell'executor parziali o ambigui e deve produrre un report coerente sotto pressione di contesto. In questa situazione, il modello può colmare le lacune con affermazioni provenienti dai dati di training. Tali affermazioni possono quindi apparire come se fossero state trovate nelle fonti recuperate.

Cosa verifica un agente critic in un workflow di ricerca autonomo?

Uso il critic come agente di verifica ristretto, non come secondo sintetizzatore. Riceve il riassunto dell'executor e i documenti sorgente. Per ogni affermazione, restituisce uno stato: grounded, inferred oppure unsupported. Restituisce anche output strutturato con flags rivolti al planner e un punteggio di grounding complessivo.

Perché il critic deve vedere i documenti sorgente originali?

Non lascio che il critic si basi solo sul riassunto dell'executor. Un riassunto può essere internamente coerente ed essere comunque errato. Il critic necessita dei documenti originali recuperati per ricondurre le affermazioni a passaggi sorgente specifici.

In che modo il riassunto ricorsivo dovrebbe preservare il tracciamento delle citazioni?

Conservo il documento originale insieme al riassunto compresso, oppure lo archivio in un indice in grado di recuperare successivamente lo span pertinente. Il riassunto è utile per la pianificazione, ma può perdere i passaggi esatti necessari per la verifica. Il critic legge il riassunto per il contesto dell'affermazione, quindi verifica il testo originale per il passaggio di supporto.

Cosa dovrebbe accadere quando le affermazioni restano non supportate dopo il re-query?

Tratto le affermazioni ripetutamente non supportate nei cicli di re-query con termini di ricerca diversi come una probabile lacuna di copertura, non soltanto come un fallimento della query. Il planner dovrebbe riportare esplicitamente la lacuna. Non dovrebbe far passare una risposta dall'aspetto plausibile con falsa confidenza.

Condividi questo articolo

Articoli correlati

  • Cinque pattern di function calling che hanno retto in produzione

    L'uso dei tool è dove i sistemi LLM falliscono più spesso: cinque pattern robusti in produzione e gli anti-pattern sostituiti.

    25 nov 202413 min di lettura
    #LLM#Function Calling#OpenAI#Production
  • RAG: chunking e re-ranking prima degli embedding

    La maggior parte delle pipeline RAG fallisce nel chunking o nel re-ranking prima della qualità degli embedding: un framework diagnostico.

    20 dic 202412 min di lettura
    #RAG#Retrieval#LLM#Production
  • Lo stato come API: LangGraph dopo tre riscritture

    Lo schema di stato è la scelta chiave in LangGraph: tre iterazioni sul modello e perché i canali con reducer sono la primitiva giusta.

    8 gen 202512 min di lettura
    #LangGraph#LLM#Multi-Agent#Orchestration