Ancrer les Réponses dans les Sources et Réduire les Hallucinations
Le problème des hallucinations en production
Les hallucinations — ces réponses factuellement incorrectes mais formulées avec assurance — représentent le risque principal des LLM en production. Un chatbot de support qui invente une procédure, un assistant juridique qui cite un article de loi inexistant, un outil médical qui recommande un mauvais dosage : les conséquences peuvent être graves.
Le grounding (ancrage) consiste à contraindre le modèle à fonder ses réponses exclusivement sur des sources vérifiées. Combiné au système de citations de Mistral, il crée un filet de sécurité robuste.
Stratégie 1 : System prompt strict
Le system prompt est votre première ligne de défense. Soyez explicite sur ce que le modèle doit et ne doit pas faire :
system_prompt = """
Vous êtes un assistant documentaire spécialisé.
RÈGLES ABSOLUES :
1. Répondez UNIQUEMENT à partir des sources fournies via les références.
2. Si l'information n'est pas dans les sources, répondez :
"Cette information ne figure pas dans les sources disponibles."
3. Ne complétez JAMAIS avec vos connaissances générales.
4. Citez chaque affirmation avec la référence correspondante.
5. Si deux sources se contredisent, mentionnez les deux versions.
"""
Ce prompt ne garantit pas l’absence d’hallucinations, mais réduit considérablement leur fréquence. Le modèle Mistral, entraîné pour le grounding, respecte ces consignes avec un taux de conformité élevé.
Stratégie 2 : Validation post-génération
Même avec un bon prompt, vérifiez programmatiquement que la réponse s’appuie sur les sources :
from mistralai.models import TextChunk, ReferenceChunk
def validate_grounding(response, min_citations=1):
"""Vérifie que la réponse est ancrée dans les sources."""
content = response.choices[0].message.content
text_length = 0
citation_count = 0
cited_refs = set()
for chunk in content:
if isinstance(chunk, TextChunk):
text_length += len(chunk.text)
elif isinstance(chunk, ReferenceChunk):
citation_count += 1
cited_refs.update(chunk.reference_ids)
# Vérifications
checks = {
"has_citations": citation_count >= min_citations,
"citation_count": citation_count,
"unique_sources": len(cited_refs),
"text_length": text_length,
"citations_per_1000_chars": round(
citation_count / max(text_length / 1000, 1), 1
)
}
checks["is_grounded"] = checks["has_citations"]
return checks
result = validate_grounding(final_response, min_citations=2)
if not result["is_grounded"]:
print("ALERTE : Réponse potentiellement non ancrée !")
print(f" Citations trouvées : {result['citation_count']}")
Stratégie 3 : Détection de réponses hors périmètre
Identifiez les cas où le modèle a dépassé les sources fournies :
def detect_ungrounded_claims(response_text, source_snippets):
"""Détecte les phrases qui ne correspondent à aucun snippet."""
import re
# Découper la réponse en phrases
sentences = re.split(r'[.!?]+', response_text)
sentences = [s.strip() for s in sentences if len(s.strip()) > 20]
# Concaténer tous les snippets en un seul texte
all_sources = " ".join(source_snippets).lower()
suspicious = []
for sentence in sentences:
# Extraire les mots-clés significatifs (> 5 chars)
keywords = [
w.lower() for w in sentence.split()
if len(w) > 5
]
# Vérifier la couverture
matches = sum(1 for kw in keywords if kw in all_sources)
coverage = matches / max(len(keywords), 1)
if coverage < 0.3: # Moins de 30 % de mots-clés trouvés
suspicious.append({
"sentence": sentence,
"coverage": round(coverage, 2)
})
return suspicious
Stratégie 4 : Pipeline de confiance à plusieurs niveaux
En production, combinez les approches dans un pipeline cohérent :
class GroundedRAG:
"""Pipeline RAG avec ancrage et validation."""
def __init__(self, client, model="mistral-large-latest"):
self.client = client
self.model = model
def query(self, question, references, strict=True):
"""Exécute une requête RAG avec validation d'ancrage."""
# 1. Générer la réponse avec citations
response = self._generate_with_citations(
question, references
)
# 2. Valider l'ancrage
grounding = validate_grounding(response, min_citations=1)
# 3. En mode strict, rejeter les réponses non ancrées
if strict and not grounding["is_grounded"]:
return {
"status": "rejected",
"reason": "Réponse insuffisamment ancrée dans les sources",
"grounding_score": grounding
}
# 4. Retourner la réponse validée
return {
"status": "ok",
"response": response,
"grounding_score": grounding,
"confidence": self._compute_confidence(grounding)
}
def _compute_confidence(self, grounding):
"""Calcule un score de confiance basé sur l'ancrage."""
score = 0.0
if grounding["has_citations"]:
score += 0.4
if grounding["unique_sources"] >= 2:
score += 0.3
if grounding["citations_per_1000_chars"] >= 1.0:
score += 0.3
return round(score, 2)
Métriques de grounding à monitorer
En production, suivez ces indicateurs :
- Taux de citations — Pourcentage de réponses contenant au moins une citation
- Densité de citations — Nombre moyen de citations par 1000 caractères
- Diversité des sources — Nombre moyen de sources distinctes par réponse
- Taux de rejet — Pourcentage de réponses rejetées pour ancrage insuffisant
- Score de confiance moyen — Évolution du score de confiance dans le temps
Points clés à retenir
- Le grounding contraint le modèle à répondre uniquement à partir de sources vérifiées
- Le system prompt strict est la première ligne de défense contre les hallucinations
- La validation post-génération vérifie programmatiquement l’ancrage de chaque réponse
- Combinez ces stratégies dans un pipeline de confiance à plusieurs niveaux
- Monitorez les métriques de grounding en continu pour détecter les dégradations