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:
| Department | Risk | Why |
|---|---|---|
| Sinistres | CRITICAL | Invented indemnity amounts β wrong payment to claimant |
| Finance | CRITICAL | Fabricated ratios or provisions β financial misstatement |
| Juridique | CRITICAL | Non-existent article cited β contract dispute or compliance failure |
| Souscription | HIGH | Wrong coverage terms quoted β customer sold wrong product |
| Actuariat | HIGH | Invented statistical assumptions β bad pricing |
| Audit | HIGH | Fabricated findings β audit report integrity |
| RΓ©assurance | HIGH | Wrong treaty terms β uncovered exposure |
| Commercial | MEDIUM | Wrong product info β sales error |
| IT / Transformation / RH | LOW | Easily 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 range | Meaning |
|---|---|
| > 0.75 | High semantic match β document directly answers the query |
| 0.50β0.75 | Related content β model can likely answer with grounding |
| 0.35β0.50 | Weak match β context may be tangential |
| < 0.35 | No 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:
| Department | Model alias | Base model |
|---|---|---|
| Sinistres, Souscription | claims-assistant | llama3.2:3b |
| Finance, Actuariat, Audit, RΓ©assurance | finance-assistant | llama3.2:3b |
| Juridique | legal-assistant | llama3.2:3b |
| IT, RH, Commercial, Transformation | general-assistant | llama3.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β
| Situation | Action |
|---|---|
| Specific claim + citation present | citation_compliance = 1 score logged to Langfuse |
| Specific claim, no citation | citation_compliance = 0 + uncited-specific-claim WARNING event β annotation queue |
| No specific claim | No citation check (not needed) |
| Department not in high-stakes list | Hook 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 name | Type | Value | When |
|---|---|---|---|
retrieval_gate_blocked | Event (INFO) | β | Similarity < 0.35 β call refused |
citation_compliance | Score | 0 or 1 | Specific claim detected in high-stakes response |
uncited-specific-claim | Event (WARNING) | β | claim found, no citation β annotation queue |
low_self_confidence | Event (WARNING) | β | Self-eval confidence = 1 |
human_feedback | Score | 1 or β1 | User π/π 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β
| Gap | Why | Mitigation path |
|---|---|---|
| Factual accuracy against external ground truth | Citation check verifies whether a source was cited, not whether the source is correct | Langfuse golden datasets per dept (#43) |
| Temporal hallucination (outdated training data) | System prompt instructs model to flag document dates β doesn't eliminate | Document metadata: include effective date in every ingested chunk |
| Cross-document contradiction | Two retrieved docs disagree β model may pick the wrong one | Deferred; requires multi-document consistency check |
| Hallucination in image/multimodal input | Platform is text-only | Out 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"