RAG â Retrieval-Augmented Generation
Associe recherche documentaire (sur index vectoriel/hybride) + génération LLM guidée par des sources. Objectifs : réponses fondées, actualisées, traçables, avec citations.
Vector DB + BM25 Citations & Confiance Guardrails pré/post
1) Ingestion & Normalisation
Connecteurs
- Fichiers : PDF/HTML/DOCX/MD, images (OCR), ZIP.
- Datas : SQL/NoSQL (views RO), S3/GCS, BigQuery.
- APIs : Confluence/Notion/GitHub/Wiki, dépÎts Git.
Nettoyage
- Suppression boilerplate : nav/footer, pagination.
- Tableaux â texte (CSV/Markdown), images â OCR.
- Normaliser encodage, espaces, langue, PII.
Chunking (heuristiques)
| Type | Taille | Overlap | Notes |
|---|---|---|---|
| Prose | 400â800 tok | 10â20% | DĂ©couper aux fins de phrase |
| Tech/Code | 200â400 tok | 20â40% | PrĂ©server blocs de code/erreurs |
| FAQ/Contrats | par titres/sections | minime | Regex/balises (H2/H3/clauses) |
Garder title-aware (H2/H3) dans les métadonnées.
# Pseudo-code dâingestion
docs = connect_and_list(sources) # PDF/HTML/SQL/API/Git
for doc in docs:
text, meta = extract_clean(doc) # nettoyage + OCR + tablesâmd
chunks = smart_chunk(text, kind=meta.kind) # heuristiques ci-dessus
for ch in chunks:
yield {"text": ch.text, "meta": {**meta, "section": ch.title}}2) Embeddings
Choisir un modĂšle
- Multilingue si corpus FR/EN/ESâŠ
- Domaine (juridique, code, biomédical) si spécialisé.
- CoĂ»t/latence : batcher, cache dâembeddings.
Stockage recommandé
- Vecteur + métadonnées :
title, section, url, source_id, lang, permissions, updated_at, hash. - CompatibilitĂ© RLS (filtrage par permissions Ă la requĂȘte).
# Embedding dâun chunk
vec = embedder.encode(ch.text) # taille d en 512â4096
store.put({
"id": new_id(),
"text": ch.text,
"embedding": vec,
"title": meta.title, "section": meta.section,
"source_type": meta.type, "path": meta.path,
"lang": meta.lang, "permissions": meta.perms,
"updated_at": meta.updated_at, "hash": meta.hash
})3) Index (Vector DB)
Options
- Local : FAISS/SQLite (PoC, edge).
- On-prem : Milvus, Weaviate.
- Managé : Pinecone, Elastic Vector, Qdrant Cloud.
Schéma conseillé
id, text, embedding, source_type, source_id, path,
section, title, url, lang, tags[], permissions,
updated_at, hashParamÚtres clés
- Métrique : cosine / dot / L2 (alignée au modÚle).
- Index ANN : IVF/HNSW/PQ selon volume/latence.
- Compaction & TTL pour fraicheur.
# Construction dâindex (ex. HNSW)
index = VectorIndex(metric="cosine", method="hnsw", M=32, ef=200)
for row in store.scan(): index.add(row.id, row.embedding)4) Retrieval
ANN kNN
- Query â embedding â top-k vecteurs proches.
- Filtres : lang, tags, permissions (RLS), fraĂźcheur.
Hybride + re-ranking
Pipeline gagnant : vecteur k=50 âȘ BM25 k=50 â cross-encoder top=10.
- Robuste aux synonymes ET aux mots-clés exacts.
- Cross-encoder affine lâordre final (similaritĂ© question-passage).
Anti-patterns
- k trop petit â manque de rappel (recall).
- Pas de filtres perms/lang â fuites de donnĂ©es / bruit.
- Pas de rerank â passages hors sujet en tĂȘte.
# Retrieval hybride
qv = embedder.encode(query)
V = index.search(qv, k=50, filters={"lang":user.lang, "perms":user.perms})
B = bm25.search(query, k=50, filters={"perms":user.perms})
U = union(V, B)
R = cross_encoder.rerank(query, U)[:10]5) Context Builder
Sélection
- Prendre 4â12 passages selon budget tokens.
- Windowing : regrouper des passages adjacents.
- DiversitĂ© > redondance (Ă©viter 8 chunks du mĂȘme paragraphe).
Citations
- URL + ancre + numéros
[1],[2]⊠- Conserver
source_id/section/titlepour traçabilité.
Compression
- RĂ©-rĂ©sumer passages trop longs en 2â3 phrases.
- Objectif budget : 1/3 contexte, 2/3 génération.
# Construction du contexte
budget = tokens.max // 3
ctx, cites = [], []
for p in diverse(R): # diversité des sources
s = shrink(p.text, limit=200) # compression douce
if tokens.len(ctx)+tokens.len(s) > budget: break
ctx.append(s); cites.append(ref(p)) # ref = [n] + URL + section6) Génération (LLM Core)
Prompt structuré
### INSTRUCTION
Réponds précisément à la question en citant les sources [n].
### QUESTION
### CONTEXTE
# numérotés [1]..[N]
### CONTRAINTES
- Langue:
- Format sortie: JSON {"answer","citations[]","confidence"}Guardrails
- Pré-prompt : filtrer PII, secrets, demandes interdites.
- Post-génération : bloquer si pas de citation ou si confiance < seuil.
- Mode Answer or say I donât know si recall faible.
# Appel modĂšle + validation
raw = llm.generate(prompt, temperature=0.2, top_p=0.9, max_tokens=600)
out = validate_json(raw, schema={"answer":str,"citations":[int],"confidence":float})
if not out or out.confidence < 0.5 or not out.citations:
return {"answer":"Je ne sais pas.","citations":[], "confidence":0.0}7) Sortie & Télémétrie
Sortie
- Réponse + citations + confiance.
- PrĂ©server lâordre des citations utilisĂ©es.
Télémétrie
- Latence (P50/P95), tokens/req, coût/req.
- Hits/Miss (retrieval), couverture des sources.
Logs
- Prompts/outputs avec masquage PII.
- Audit des appels outils et accĂšs sources.
# Assemblage & métriques
resp = {"answer": out.answer, "citations": map_ids_to_urls(out.citations), "confidence": out.confidence}
metrics.push({"latency_ms":t(), "tokens_in":ti, "tokens_out":to, "cost":cost, "hits":len(R)})
return resp8) âïž StratĂ©gies clĂ©s (qui changent tout)
A. Chunking « sémantique »
- Title-aware (H2/H3) en métadonnées.
- Ăviter chunks trop petits (perte contexte) / gros (bruit).
- Overlap pour éviter coupes de phrase/tableau.
B. Hybride + re-ranking
Vecteur k=50 âȘ BM25 k=50 â cross-encoder top=10.
- Gere synonymes + exact-match.
- Améliore précision@k et groundedness.
C. Construction de contexte
- Diversité des sources; éviter duplicats.
- Citations obligatoires; identifiants stables.
- Budget tokens : viser 1/3 contexte, 2/3 génération.
D. FraĂźcheur
- Re-ingest Ă lâĂ©vĂ©nement (webhook Git/file watcher) + batch nocturne.
- StratĂ©gie delta : hash par chunk â pas de rĂ©index complet.
E. Sécurité & Permissions
- Champ permissions (RBAC) + filtres cĂŽtĂ© requĂȘte.
- Chiffrement au repos/en transit des indexes sensibles.
| Do | Donât |
|---|---|
| Rerank cross-encoder aprĂšs union hybride | Se fier au seul top-k vectoriel |
| Garder titres/sections dans le contexte | Context stuffing non structuré |
| Limiter max_tokens + compresser | Laisser le modĂšle âdigresserâ |
9) đ§Ș QualitĂ©, Ă©valuation, guardrails
KPIs
- Groundedness (justesse vs sources).
- Citations coverage (â„1 source pertinente/rĂ©ponse).
- Precision@k / Recall@k retrieval.
- Latency P95, Cost/answer.
Tests automatiques
- Jeux : (question, sources attendues, réponse réf.).
- Diff testing Ă chaque changement (embedding/index/prompt).
Guardrails
- Pré-prompt : filtrage PII/secrets/demandes interdites.
- Post : bloquer si pas de citation ou confiance < seuil.
- Mode Answer or say I donât know si score retrieval < seuil.
# Ăvaluation batch
res = []
for q in dataset:
a = rag_answer(q.text)
res.append(compare(a, q.reference, expected_sources=q.sources))
report = metrics(res, k=10) # precision@k, recall@k, groundedness
assert report.groundedness >= 0.810) đ§° Recette dâimplĂ©mentation (Python, style LangChain/LlamaIndex)
def rag_answer(query, user=None):
# 1) Pré-prompt guard (PII/secrets)
if violates_policy(query): return {"answer":"Je ne peux pas répondre.", "citations":[], "confidence":0.0}
# 2) Embedding de la requĂȘte + retrieval hybride
qv = embedder.encode(query)
V = index.search(qv, k=50, filters={"perms": user.perms, "lang": user.lang})
B = bm25.search(query, k=50, filters={"perms": user.perms})
U = union(V, B)
R = cross_encoder.rerank(query, U)[:10]
# 3) Context builder (diversité, windowing, compression, budget)
budget = tokens.max // 3
ctx, cites = build_context(R, budget=budget) # renvoie passages numérotés [1]..[N]
# 4) Prompt structuré (instruction + question + contexte + contraintes)
prompt = render("rag_prompt.txt", {"q": query, "passages": ctx, "lang": user.lang})
# 5) Appel LLM + validation + post-guard
raw = llm.generate(prompt, temperature=0.2, top_p=0.9, max_tokens=600)
out = validate_json(raw, schema={"answer":str,"citations":[int],"confidence":float})
if not out or out.confidence < 0.5 or not out.citations:
return {"answer": "Je ne sais pas.", "citations": [], "confidence": 0.0}
# 6) Télémétrie
metrics.push({"latency_ms": t(), "tokens_in": ti, "tokens_out": to, "hits": len(R)})
return {"answer": out.answer, "citations": map_ids_to_urls(out.citations), "confidence": out.confidence}Fichiers à prévoir
rag_prompt.txt(template du prompt).schema.json(contrat de sortie).connectors/*.py,chunkers/*.py,eval/*.py.
CI & Ops
- Batch nocturne + triggers Ă lâĂ©vĂ©nement (Git/file watcher).
- Goldens & tests de diff Ă chaque changement.
- Budgets/quotas utilisateur; alertes sur dérives KPI.
