Le problème
La demande paraît standard : produire un court résumé des changements de policy et ajouter les sources.
Dans les traces, c'est différent : sur un run, l'agent a renvoyé 7 citations,
mais la vérification a montré que 3 sources n'ont jamais été fetch et que 2 mènent à 404.
Pour l'utilisateur, la réponse semble fiable, mais elle n'est pas reproductible.
Le système ne tombe pas en panne.
Il renvoie simplement des citations plausibles sans preuve réelle.
Analogie : imagine un auditeur qui met dans son rapport des références vers "des dossiers en archive", mais personne n'a vu ces dossiers. Le document paraît professionnel jusqu'à ce que quelqu'un vérifie les sources. Les sources hallucinées dans les systèmes d'agents fonctionnent exactement comme ça.
Pourquoi ça arrive
Les sources hallucinées n'apparaissent généralement pas à cause d'une seule erreur de modèle, mais à cause d'un manque de contrôle strict des citations dans le runtime.
LLM a un fort biais vers des réponses "complètes", donc sans vérification stricte le modèle préfère inventer une citation plutôt que renvoyer une réponse sans source.
En production, c'est souvent ce schéma :
- l'agent génère des citations comme partie d'une réponse "complète" ;
- les snippets de recherche sont pris comme evidence alors que les pages n'ont pas été ouvertes ;
- les
source_idne sont pas liés aux snapshots d'evidence ; - sans vérification des citations, le runtime laisse passer des sources unfetched ou invalides ;
- si fail-closed n'est pas activé, les sources inventées arrivent jusqu'à l'utilisateur.
Dans la trace, on voit une hausse de citations_count
en parallèle d'une baisse de citation_validity_rate.
Le problème n'est pas une seule URL ratée.
Le runtime ne bloque pas les citations non vérifiées avant la réponse finale.
Pannes les plus fréquentes
En production, on observe surtout quatre patterns récurrents de sources hallucinées.
Citations URL non vérifiées (Unfetched URL citations)
L'agent cite une URL qui n'est jamais passée par http.get ou kb.read dans ce run.
Cause typique : les citations ne sont pas restreintes aux source_id de l'evidence store.
Snippet au lieu de preuve (Search-as-evidence)
La réponse contient des "sources" issues de la recherche, mais l'agent n'a aucune confirmation du contenu réel des pages.
Cause typique : les search results sont mélangés avec la couche evidence.
Dérive des citations entre étapes (Citation drift)
À une étape précoce la source était valide, mais après retry ou truncation la réponse finale pointe vers un autre document.
Cause typique : absence de lien stable claim -> source_id -> snapshot hash.
Pseudo-citations sans couverture des claims (Claim-source mismatch)
La réponse contient un bloc de citations, mais les claims clés n'ont pas de source correspondante.
Cause typique : la validation vérifie seulement "présence de liens", pas la couverture des claims.
Comment détecter ces problèmes
Les sources hallucinées se voient bien via la combinaison de métriques citation et retrieval.
| Métrique | Signal de sources hallucinées | Action |
|---|---|---|
citation_validity_rate | la part de citations validées baisse | activer une vérification fail-closed par source_id |
unfetched_source_rate | beaucoup d'URL unfetched dans les réponses | interdire les citations URL sans evidence snapshot |
source_404_rate | une partie des sources n'est pas accessible | vérifier le statut et l'URL canonique pendant le fetch |
claim_without_citation_rate | claims sans lien vers une source | ajouter un claim-level coverage check |
citation_stop_reason_rate | citations:invalid fréquent dans le runtime | vérifier la qualité du retrieval et la policy des tools |
Comment distinguer les sources hallucinées d'une réponse simplement imprécise
Toute imprécision de texte ne signifie pas source inventée. La question clé : peut-on reproduire techniquement la source pour chaque claim critique.
Normal si :
- chaque citation pointe vers un
source_idexistant dans l'evidence store ; - les métadonnées snapshot existent (URL, timestamp, hash) ;
- la vérification des claims montre que les sources couvrent les conclusions clés.
Dangereux si :
- la réponse contient des URL absentes de l'étape fetch ;
- les citations existent "pour la forme" mais ne couvrent pas les claims principaux ;
- les réponses ne sont pas reproductibles au niveau run (
run_id->source_id-> snapshot).
Comment stopper ces pannes
En pratique, cela ressemble à ceci :
- toutes les sources passent par l'evidence store (snapshot + hash + timestamp) ;
- le modèle renvoie les citations uniquement en
source_id, pas en URL arbitraires ; - le citation verifier vérifie que tous les
source_idexistent, ont été fetch et sont autorisés par la policy ; - si la vérification échoue, le runtime renvoie stop reason et safe fallback.
Guard minimal pour valider les citations :
from dataclasses import dataclass
import hashlib
import time
@dataclass(frozen=True)
class EvidenceMeta:
source_id: str
url: str
fetched_at: float
text_sha256: str
class EvidenceStore:
def __init__(self):
self.items: dict[str, EvidenceMeta] = {}
def add_snapshot(self, source_id: str, url: str, text: str) -> None:
self.items[source_id] = EvidenceMeta(
source_id=source_id,
url=url,
fetched_at=time.time(),
text_sha256=hashlib.sha256(text.encode("utf-8")).hexdigest(),
)
def has(self, source_id: str) -> bool:
return source_id in self.items
def verify_citations(cited_source_ids: list[str], store: EvidenceStore) -> str | None:
# cited_source_ids are expected to come from structured output
if not cited_source_ids:
return "citations:missing"
unknown = [sid for sid in cited_source_ids if not store.has(sid)]
if unknown:
return "citations:unknown_source_id"
return None
C'est un guard de base.
En production, on l'étend généralement avec claim-level coverage check,
allowlist des citation tools et stop reason séparé pour les URL unfetched.
verify_citations(...) est appelé avant le rendu final,
pour que l'utilisateur ne voie pas de sources invalides.
Où c'est implémenté dans l'architecture
En production, le contrôle des sources hallucinées est presque toujours réparti entre trois couches du système.
Tool Execution Layer gère le fetch d'evidence : statut de réponse, normalisation URL, snapshots et hash. Si cette couche ne stocke pas de preuve, les citations ne peuvent pas être vérifiées de façon fiable.
Agent Runtime contrôle structured output, citation verification, stop reasons et fallback fail-closed. C'est ici que la décision finale est prise avant affichage à l'utilisateur.
Memory Layer maintient le lien run-evidence :
run_id, source_id, retention et reproductibilité.
Sans cette couche, l'équipe ne peut pas faire un audit d'incident propre.
Auto-vérification
Vérification rapide avant release. Coche les points et regarde le statut ci-dessous.
C'est un sanity-check court, pas un audit formel.
Progression: 0/8
⚠ Il y a des signaux de risque
Il manque des contrôles de base. Fermez les points clés de la checklist avant release.
FAQ
Q : Peut-on juste demander au modèle "d'ajouter des sources" ?
R : Oui, mais ce n'est pas suffisant. Sans vérification runtime des citations, c'est du format, pas de la preuve.
Q : Les search results peuvent-ils servir de preuve ?
R : En général non. Search donne des candidats.
La preuve est seulement ce qui a été fetch et stocké comme snapshot.
Q : Faut-il stocker le texte complet de la source ?
R : Pas toujours. Le minimum pour audit : URL, timestamp, hash et source_id stable. Le texte complet est ajouté quand replay ou citations exactes sont nécessaires.
Q : Que montrer à l'utilisateur quand les citations sont invalides ?
R : Une stop reason explicite, ce qui est déjà vérifié, et une étape sûre : réponse partial sans sources non vérifiées ou nouveau run avec vérification.
Un incident de sources hallucinées ressemble rarement à un crash bruyant. C'est une perte de confiance silencieuse, visible le plus souvent seulement après vérification des sources. Les agents de production ont donc besoin non seulement de bonnes réponses, mais aussi d'une discipline stricte sur les citations.
Pages liées
Si ce problème apparaît en production, ces pages sont aussi utiles :
- Pourquoi les agents IA échouent - carte générale des pannes en production.
- Context poisoning - comment un contexte problématique pousse l'agent vers de mauvaises conclusions.
- Tool failure - comment des tools instables cassent la chaîne d'evidence.
- Agent Runtime - où appliquer structured output verification et stop reasons.
- Tool Execution Layer - où collecter snapshots et valider les sources.
- Memory Layer - où maintenir la reproductibilité des preuves entre runs.