Aller au contenu principal

Hallucination Prevention & Detection

Hallucination is the AI platform's highest-severity reliability risk. A model that invents a claim amount, a non-existent policy clause, or a fabricated French insurance regulation causes direct business harm — wrong payments, contract disputes, compliance failures.

This page covers the three-layer control architecture implemented for the HDI Seguros platform.


Risk Classification

Not every department carries the same hallucination risk. Controls are applied proportionally:

DepartmentRiskWhy
SinistresCRITICALInvented indemnity amounts → wrong payment to claimant
FinanceCRITICALFabricated ratios or provisions → financial misstatement
JuridiqueCRITICALNon-existent article cited → contract dispute or compliance failure
SouscriptionHIGHWrong coverage terms quoted → customer sold wrong product
ActuariatHIGHInvented statistical assumptions → bad pricing
AuditHIGHFabricated findings → audit report integrity
RéassuranceHIGHWrong treaty terms → uncovered exposure
CommercialMEDIUMWrong product info → sales error
IT / Transformation / RHLOWEasily verified or low-consequence

High-stakes departments (Sinistres, Finance, Juridique, Souscription, Actuariat, Audit, Réassurance) receive all three control layers. Low-stakes departments receive Layer 1 only.


Architecture Overview

User query (Open WebUI)


┌───────────────────────────────────────────────────────────┐
│ LAYER 1 — RETRIEVAL GROUNDING GATE │
│ Qdrant similarity score < 0.35 → refuse (400) │
│ "Aucun document pertinent trouvé" │
└───────────────────────┬───────────────────────────────────┘
│ similarity ≥ 0.35

┌───────────────────────────────────────────────────────────┐
│ LAYER 2 — DEPARTMENT SYSTEM PROMPT │
│ Model constrained: cite sources, say "I don't know" │
│ High-stakes depts → llama3.2:3b (dedicated model alias) │
└───────────────────────┬───────────────────────────────────┘


Ollama (inference)


┌───────────────────────────────────────────────────────────┐
│ LAYER 3 — CITATION CHECK HOOK (post-call) │
│ Scans for specific claims (€, %, articles, clauses) │
│ No citation found → Langfuse WARNING + queue for review │
│ Self-eval confidence = 1 → prepend French warning │
└───────────────────────┬───────────────────────────────────┘


Response to user

All three layers run inside LiteLLM as CustomLogger hooks — no additional network hops, no latency impact on the main inference path.


Layer 1 — Retrieval Grounding Gate

What it does

When Open WebUI queries Qdrant, it receives a maximum cosine similarity score alongside the retrieved chunks. This score is passed through to LiteLLM in the request metadata. If the score falls below 0.35 — meaning no document in the vector store closely matches the query — the call is refused before Ollama is invoked.

similarity < 0.35 → HTTP 400
"Aucun document pertinent trouvé pour votre question
(score de similarité 0.21 < 0.35).
Veuillez reformuler ou contacter votre référent Sinistres."

Why 0.35

Cosine similarity on nomic-embed-text embeddings (768 dimensions):

Score rangeMeaning
> 0.75High semantic match — document directly answers the query
0.50–0.75Related content — model can likely answer with grounding
0.35–0.50Weak match — context may be tangential
< 0.35No relevant document — model will hallucinate if allowed to proceed

0.35 is the empirically safe floor before free hallucination starts. It can be tuned per department once traffic data is available in Langfuse.

Why this is the most important control

A model that never reaches Ollama without a grounding document cannot hallucinate on that query. Layers 2 and 3 reduce hallucination probability; Layer 1 eliminates it for the "no context" case entirely.


Layer 2 — Department System Prompts

What it does

Each high-stakes department is mapped to a dedicated LiteLLM model alias with a system prompt that imposes hard constraints on the model's behavior.

Claims (Sinistres)

Tu es un assistant spécialisé pour la direction sinistres de HDI Seguros France.

RÈGLES ABSOLUES:
1. Réponds UNIQUEMENT à partir des documents fournis dans le contexte.
2. Si le contexte ne contient pas l'information demandée, réponds exactement:
"Cette information ne figure pas dans mes documents. Merci de contacter
votre référent sinistres."
3. N'invente JAMAIS: montants d'indemnisation, délais réglementaires,
références d'articles, clauses contractuelles, noms de garanties.
4. Pour toute valeur chiffrée (montant, délai, pourcentage), indique
toujours le document source.
5. En cas de doute sur l'interprétation d'une clause, dis-le explicitement.

Finance

RÈGLES ABSOLUES:
1. Réponds uniquement à partir du contexte documentaire fourni.
2. Toute donnée chiffrée (montant, ratio, provision, taux) doit être
accompagnée de sa source.
3. Ne jamais déduire ou extrapoler des montants financiers non
explicitement présents dans le contexte.
4. Pour les normes IFRS17, Solvabilité II ou règles fiscales: cite
l'article exact — sinon indique que tu n'as pas cette information.

Juridique

RÈGLES ABSOLUES:
1. Tu n'es PAS un conseil juridique — précise-le si on te demande
une opinion de droit.
2. Cite uniquement les textes explicitement présents dans les documents.
3. Ne jamais paraphraser un article de loi sans l'avoir dans ton contexte.
4. Signale toujours si la législation a pu évoluer depuis la date du
document source.

Model alias routing

The LiteLLM router maps each department's virtual key to its model alias:

DepartmentModel aliasBase model
Sinistres, Souscriptionclaims-assistantllama3.2:3b
Finance, Actuariat, Audit, Réassurancefinance-assistantllama3.2:3b
Juridiquelegal-assistantllama3.2:3b
IT, RH, Commercial, Transformationgeneral-assistantllama3.2:1b

High-stakes departments always use llama3.2:3b — the 3B model is measurably more conservative and citation-aware than the 1B model on insurance-domain queries.


Layer 3 — Citation Check Hook

What it does

After Ollama returns a response, the RetrievalGroundingHook post-call handler scans the text for specific factual claims and checks whether those claims are accompanied by a source citation.

Specific claim patterns detected

r"\d[\d\s]*[.,]\d{2}\s*€" # monetary: 12 500,00 €
r"\d+\s*%" # percentages: 85 %
r"article\s+[lra]\s*[\d\-]+" # French law: Article L111-1
r"clause\s+\d+" # contract clauses
r"(délai|deadline)\s+(de\s+)?\d+\s*(jour|mois|an)" # time limits
r"(couvert|garanti|exclu)\s+(jusqu|à hauteur|pour)" # coverage assertions

Citation markers accepted

selon, d'après, source, document, article, clause, contrat, police, avenant, annexe, référence, voir, cf., conformément

Outcomes

SituationAction
Specific claim + citation presentcitation_compliance = 1 score logged to Langfuse
Specific claim, no citationcitation_compliance = 0 + uncited-specific-claim WARNING event → annotation queue
No specific claimNo citation check (not needed)
Department not in high-stakes listHook skipped entirely

The citation check is non-blocking — it never rejects a response. It surfaces the trace for human review so domain experts can verify the claim and, if wrong, add the correct version to the Langfuse golden dataset (see issue #43 — evaluation framework).

Self-evaluation confidence

When a specific claim is detected in a high-stakes response, a lightweight secondary call to llama3.2:1b asks the model to rate its own confidence (1–3). If the model reports confidence 1 ("I invented this"), the response is prepended with:

⚠️ Je ne dispose pas de documents suffisants pour répondre avec certitude
à cette question. Veuillez vérifier auprès de votre référent métier.

This visible warning is the last-resort signal to the user that the answer should not be acted on without verification.


What Gets Logged to Langfuse

Every hallucination-related event is a first-class Langfuse object:

Event / Score nameTypeValueWhen
retrieval_gate_blockedEvent (INFO)Similarity < 0.35 → call refused
citation_complianceScore0 or 1Specific claim detected in high-stakes response
uncited-specific-claimEvent (WARNING)claim found, no citation → annotation queue
low_self_confidenceEvent (WARNING)Self-eval confidence = 1
human_feedbackScore1 or −1User 👍/👎 in Open WebUI (issue #43 evaluation framework)

Filter in Langfuse: Traces → Filter by score citation_compliance = 0 to see all uncited claims in the last 7 days.


Grafana — Hallucination Risk Panel

The AI Observability dashboard includes a hallucination risk panel:

Panel: Hallucination Risk by Department (24h)
├── Heatmap: ragas_faithfulness per department
├── Bar: uncited_specific_claim_total per department
├── Line: retrieval_gate_refused_total per department
└── Gauge: avg citation_compliance per department

Alert rule: uncited_specific_claim for finance or juridique > 5 events in 1 hour → Alertmanager fires.


What These Controls Do Not Cover

GapWhyMitigation path
Factual accuracy against external ground truthCitation check verifies whether a source was cited, not whether the source is correctLangfuse golden datasets per dept (#43)
Temporal hallucination (outdated training data)System prompt instructs model to flag document dates — doesn't eliminateDocument metadata: include effective date in every ingested chunk
Cross-document contradictionTwo retrieved docs disagree — model may pick the wrong oneDeferred; requires multi-document consistency check
Hallucination in image/multimodal inputPlatform is text-onlyOut of scope

Operational Checks

# Check how many calls the retrieval gate blocked today
kubectl logs -n ai deployment/litellm --since=24h \
| grep "retrieval_gate_blocked" | wc -l

# Check uncited-claim events in Langfuse (last 24h)
# → Langfuse UI → Traces → Filter: event.name = "uncited-specific-claim"

# Check citation_compliance score distribution
# → Langfuse UI → Scores → citation_compliance → group by department

# Verify hooks are loaded at LiteLLM startup
kubectl logs -n ai deployment/litellm | grep -E "RetrievalGroundingHook|loaded"