Mettre un RAG en production : chunking, embeddings et reranking
- 01La qualité d'un RAG se joue d'abord sur le découpage et la qualité des données, pas sur le LLM.
- 02Une recherche hybride (vecteurs + mots-clés) puis un reranker améliore nettement la pertinence.
- 03Sans évaluation chiffrée du retrieval (Recall@k), tu optimises à l'aveugle : mesure d'abord la récupération.
À la fin de ce tuto, tu sauras faire passer un RAG du prototype qui impressionne en démo au système qui répond juste sur des milliers de documents réels. On couvre les quatre leviers qui comptent — chunking, recherche hybride, reranking, mesure — avec le code des étapes clés. Prérequis : avoir fait le tuto « Le RAG expliqué ».
Durée : 35 min.
Le constat de départ
Un RAG de démonstration se monte en une après-midi. Un RAG qui répond juste sur des milliers de documents réels, c'est un autre métier. Le modèle de langage est rarement le maillon faible : c'est la récupération (retrieval) qui fait ou défait la qualité. Si le bon passage n'est pas remonté, le meilleur LLM du monde ne peut pas répondre. Tous les leviers ci-dessous visent donc le retrieval.
Levier 1 — Le chunking, premier facteur de qualité
Un mauvais découpage condamne tout le reste. Quatre principes :
- Découpe sur la structure, pas au caractère près : par section, paragraphe ou titre, pour ne pas couper une idée en deux.
- Ajoute du chevauchement (overlap) entre chunks, pour ne pas perdre le contexte aux frontières.
- Enrichis chaque chunk de métadonnées : source, date, titre de section. Elles servent au filtrage et à la citation.
- Adapte la taille au contenu : un contrat juridique ne se découpe pas comme une FAQ.
def chunk_by_paragraph(text, max_chars=1200, overlap=150):
paras, chunks, cur = text.split("\n\n"), [], ""
for p in paras:
if len(cur) + len(p) > max_chars and cur:
chunks.append(cur.strip())
cur = cur[-overlap:] # chevauchement : on garde la fin du précédent
cur += "\n\n" + p
if cur.strip():
chunks.append(cur.strip())
return chunks
Levier 2 — Recherche hybride : vecteurs + mots-clés
La recherche purement vectorielle rate les correspondances exactes (références produit, codes, noms propres). La recherche par mots-clés (BM25) rate le sens. La recherche hybride combine les deux et fusionne les scores : c'est presque toujours supérieur à l'une ou l'autre seule.
# Pseudo-code de fusion : on récupère par les deux voies, on combine les rangs
def hybrid_search(query, k=20):
vec_hits = vector_store.search(embed(query), k=k) # sémantique
kw_hits = bm25_index.search(query, k=k) # mots-clés exacts
# Reciprocal Rank Fusion : chaque doc gagne 1/(rang+60) par liste
scores = {}
for rank, doc in enumerate(vec_hits): scores[doc.id] = scores.get(doc.id, 0) + 1/(rank+60)
for rank, doc in enumerate(kw_hits): scores[doc.id] = scores.get(doc.id, 0) + 1/(rank+60)
return sorted(scores, key=scores.get, reverse=True)[:k]
La plupart des bases vectorielles sérieuses (Qdrant, Weaviate, pgvector + extension) proposent l'hybride en natif — tu n'as pas toujours à l'écrire à la main.
Levier 3 — Le reranking, le levier sous-estimé
La recherche initiale est rapide mais grossière : elle remonte 20-50 candidats approximatifs. Un reranker (un modèle dédié, type cross-encoder) repasse ensuite sur ces candidats pour les réordonner finement, et on ne garde que le top 3-5.
retrieval large (k=30, rapide) → rerank (réordonne finement) → top-k serré (k=4)
Ce second filtre améliore nettement la pertinence pour un coût modéré. C'est souvent le meilleur rapport gain/effort une fois le chunking correct.
def rerank(query, candidates, top_k=4):
# un cross-encoder note chaque paire (query, passage) ; coûteux mais précis
scored = [(reranker.score(query, c.text), c) for c in candidates]
scored.sort(reverse=True, key=lambda x: x[0])
return [c for _, c in scored[:top_k]]
Levier 4 — Mesurer, sinon tu optimises à l'aveugle
Sans métriques, chaque « amélioration » est une intuition. Construis un petit jeu de questions-réponses de référence (voir le tuto sur les évals) et mesure :
- Recall@k : la bonne info est-elle dans les k morceaux récupérés ? C'est la métrique reine — si le retrieval rate, le LLM ne peut pas rattraper.
- Pertinence des passages remontés.
- Fidélité de la réponse aux sources (pas d'invention).
def recall_at_k(eval_set, k=4):
hits = 0
for case in eval_set:
retrieved = hybrid_search(case["question"], k=k)
if case["doc_id_attendu"] in retrieved:
hits += 1
return hits / len(eval_set)
Isole l'évaluation du retrieval de celle de la génération. Recall@k te dit si la récupération marche ; la fidélité te dit si le LLM exploite bien ce qu'on lui donne. Mesurer les deux séparément te dit quel maillon corriger.
Les détails de production qui font la différence
- Citations : fais pointer chaque réponse vers ses sources (via les métadonnées des chunks), pour la confiance et l'audit.
- Mise à jour : prévois la réindexation quand les documents changent.
- Cas vide : si rien de pertinent n'est trouvé, réponds « je ne sais pas » plutôt que d'inventer (cf. le prompt cadré du tuto RAG de base).
- Cache : mets en cache les embeddings et les requêtes fréquentes pour le coût et la latence.
- Sécurité : un document indexé peut contenir une prompt injection (voir le tuto sécurité). Traite les passages récupérés comme des données non fiables.
À retenir
En production, le RAG se gagne sur le retrieval, pas sur le LLM. Soigne le chunking et la qualité des données, passe en recherche hybride, ajoute un reranker pour resserrer le top-k. Surtout, mesure : un jeu d'éval distinguant retrieval et génération t'évite d'optimiser à l'aveugle. Et gère les détails — citations, réindexation, cas vide, sécurité — qui transforment un prototype en système de confiance.
Articles liés
Fine-tuning, RAG ou prompting : quel levier pour adapter un modèle
Une méthode reproductible pour choisir entre prompting, RAG et fine-tuning : diagnostiquer le vrai problème, puis appliquer le bon levier sur un cas concret.
Observabilité d'un système LLM en production : tracer, mesurer, monitorer
Instrumenter ses appels LLM pour suivre coût, latence et taux d'échec, et repérer une régression avant que les utilisateurs ne la subissent.
Le RAG expliqué : faire répondre une IA sur vos propres documents
Comprendre le RAG en le construisant : on indexe des documents, on les vectorise, et on fait répondre une IA dessus — avec un exemple de code Python complet et minimal que tu peux faire tourner ce soir.