Un chatbot d’assistance client qui invente des politiques de remboursement. Un assistant juridique qui cite des articles de loi inexistants. Un copilote interne qui confond deux produits et envoie le mauvais tarif à un prospect.
Ce n’est pas un bug. C’est un défaut d’architecture.
Ce qu’on appelle “hallucination” est en réalité de la confiance mal calibrée
Les LLMs ne savent pas ce qu’ils ne savent pas. Quand vous leur posez une question sur votre politique interne de remboursement, ils ne répondent pas “je ne sais pas”. Ils répondent avec ce qui ressemble à une réponse correcte, assemblé à partir de patterns appris sur des milliards de tokens.
Le problème n’est pas que le modèle est stupide. Le problème est qu’il n’a pas accès à vos données. Alors il improvise.
La solution s’appelle RAG — Retrieval-Augmented Generation. Et si vous déployez de l’IA en production sans comprendre ce pattern, vous jouez à la roulette russe.
Comment fonctionne RAG concrètement
L’idée est élégante dans sa simplicité. Plutôt que d’espérer que le modèle “sache” la réponse, vous lui donnez la réponse — ou du moins les documents pertinents — au moment de la requête.
Question utilisateur
↓
[1. Vectorisation de la question]
↓
[2. Recherche sémantique dans votre base documentaire]
↓
[3. Récupération des N passages les plus pertinents]
↓
[4. Injection dans le contexte du LLM]
↓
Réponse ancrée dans vos données réelles
En pratique, voilà une implémentation minimale avec pgvector (PostgreSQL) :
import psycopg2
from anthropic import Anthropic
import numpy as np
# Hypothèse : vous utilisez un modèle d'embedding
# (OpenAI text-embedding-3-small, ou équivalent open source)
def retrieve_relevant_chunks(
query: str,
embedding_fn,
conn,
top_k: int = 5,
similarity_threshold: float = 0.75
) -> list[dict]:
"""
Recherche sémantique dans la base documentaire.
Retourne les chunks les plus proches de la requête.
"""
query_embedding = embedding_fn(query)
with conn.cursor() as cur:
cur.execute("""
SELECT
content,
source_doc,
page_number,
1 - (embedding <=> %s::vector) AS similarity
FROM document_chunks
WHERE 1 - (embedding <=> %s::vector) > %s
ORDER BY similarity DESC
LIMIT %s
""", (query_embedding, query_embedding, similarity_threshold, top_k))
return [
{
"content": row[0],
"source": row[1],
"page": row[2],
"score": float(row[3])
}
for row in cur.fetchall()
]
def rag_answer(query: str, context_chunks: list[dict]) -> str:
"""
Génère une réponse ancrée dans les documents récupérés.
"""
client = Anthropic()
# Construction du contexte documentaire
context = "\n\n---\n\n".join([
f"[Source : {c['source']}, p.{c['page']}]\n{c['content']}"
for c in context_chunks
])
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
system="""Tu es un assistant expert.
Réponds UNIQUEMENT en te basant sur les documents fournis.
Si la réponse n'est pas dans les documents, dis-le explicitement.
Cite toujours la source précise (document + page).
Ne fabrique jamais d'information.""",
messages=[{
"role": "user",
"content": f"""Documents de référence :
{context}
Question : {query}"""
}]
)
return response.content[0].text
Les trois erreurs fatales en RAG
Erreur 1 : Le chunking naïf
Découper les documents tous les 512 tokens, c’est la première chose qu’on fait. Et la première chose à corriger. Un chunk qui commence au milieu d’une phrase perd son contexte. Un chunk qui sépare une question de sa réponse rend la recherche inutile.
Bonne pratique : chunking sémantique par paragraphe, avec overlap de 20%, et enrichissement avec le titre de section parent.
Erreur 2 : La confiance aveugle dans le retrieval
La recherche vectorielle retourne toujours quelque chose. Même quand la question n’a rien à voir avec vos documents. Implémentez un score de confiance minimum et une logique de fallback explicite : “Je n’ai pas trouvé d’information pertinente dans notre base. Voici ce que je sais de manière générale, mais vérifiez auprès de [source].”
Erreur 3 : Indexer sans re-indexer
Vos documents évoluent. Votre index doit évoluer avec. Mettez en place un pipeline d’ingestion continu avec détection de changement (hash MD5 + date de modification). Un RAG sur des données périmées est pire qu’un RAG absent — il donne une fausse confiance.
Au-delà du RAG : le grounding multi-source
Les systèmes les plus robustes en production ne se contentent pas d’une base documentaire unique. Ils combinent :
- Base documentaire interne (politiques, procédures, produits)
- Données structurées (base de données via NL2SQL)
- Contexte conversationnel (historique de session)
- Données temps réel (APIs métier)
C’est ce qu’on appelle une architecture agentic — le modèle choisit quelle source consulter selon la nature de la question. Plus complexe à déployer, mais c’est là que se trouvent les systèmes vraiment fiables.
La vérité sur la fiabilité des LLMs en production
Un LLM bien architecturé avec RAG n’hallucine pas moins. Il hallucine différemment — et de façon détectable. Quand vous citez vos sources, l’utilisateur (ou votre pipeline de validation) peut vérifier. Quand vous ne citez pas, vous êtes dans le brouillard.
La fiabilité ne vient pas du modèle. Elle vient de l’architecture autour du modèle.
C’est la différence entre un outil expérimental et un système de production.
Vous avez un projet RAG en cours ou en réflexion ? Échangeons sur l’architecture.