# RAG Cookbook

Recettes de génération augmentée par récupération — paramètres de chunking, configuration de récupération hybride, reranking, réécriture de requête, métriques d’eval avec chiffres travaillés et guide de débogage.

import { Steps } from '@prosefly/astro-components';

Le RAG ancre les réponses dans des chunks récupérés au moment de la requête. Adoptez-le quand le corpus est **grand ou changeant** et que vous avez besoin de **citations**. Préférez le contexte long pour un petit corpus stable qui tient dans la fenêtre, et le fine-tuning pour le **style/format**, pas pour les faits.

:::tip[Signal d’examen]
« Confiant mais faux après une mise à jour de documents » → suspectez d’abord la **récupération/l’indexation** (index périmé, dérive de chunk), pas le prompt ni le modèle. Changer le prompt est le distracteur.
:::

## When RAG vs alternatives

| Situation | Choix | Pourquoi |
| --- | --- | --- |
| Des milliers de docs, souvent mis à jour, citations obligatoires | RAG | Frais, ancré, auditable |
| Manuel de 50 pages, change rarement, tient dans une fenêtre de 1M | Contexte long | Aucune infra de récupération ; le plus simple |
| « Correspondre à notre ton / toujours ce format » | Fine-tuning ou few-shot | Comportement, pas faits |
| Le modèle doit décider quand/quoi récupérer | RAG agentique (récupération comme outil) | Reformulation itérative |

## Chunking recipes

| Stratégie | Paramètres (point de départ) | Idéal pour | Compromis |
| --- | --- | --- | --- |
| Fixed-size | 512 tokens, 50 tokens d’overlap | Prose uniforme | Coupe en pleine idée |
| Recursive (par séparateur) | 400–800 tokens, découpe sur `\n\n` → `\n` → phrase | Prose mixte | Tailles inégales |
| Semantic | Coupe aux creux de similarité d’embedding | Docs qui changent de sujet | Coût de calcul |
| Structural / document-aware | Un chunk par section/titre | Manuels, contrats, wikis | Nécessite une bonne structure |
| Parent-child (small-to-big) | Enfant 150–300 tok pour le match ; renvoie le parent 800–1200 tok | Hit précis + contexte large | Store à deux niveaux |
| Late chunking | Embed du doc complet, pooling par chunk | Chunks nécessitant un contexte au niveau doc | Nécessite un embedder long-contexte |

Règle empirique pour l’**overlap** : 10–20 % de la taille du chunk. Trop peu perd le contexte de frontière ; trop gonfle l’index et duplique les hits.

:::caution[Dérive de chunk]
Quand des documents sont ré-ingérés avec un chunker ou une taille différents, la qualité de récupération dérive silencieusement. Versionnez votre configuration de chunking et rejouez les evals de récupération après tout changement.
:::

## Hybrid retrieval config

Le dense (embeddings) capte la paraphrase ; le sparse (BM25) capte les identifiants exacts, les codes et les termes rares. Fusionnez-les, puis effectuez un reranking.

```yaml
retrieval:
  dense:
    model: text-embedding-large
    top_k: 40
  sparse:
    algorithm: bm25
    top_k: 40
  fusion:
    method: reciprocal_rank_fusion   # RRF
    k: 60                            # RRF constant
  rerank:
    model: cross-encoder-reranker
    top_n: 8                         # feed the LLM the top 8
  diversity:
    method: mmr                      # optional, reduce redundancy
    lambda: 0.5
```

**Reciprocal Rank Fusion** : score d’un doc = Σ sur les listes de `1 / (k + rank)`. Avec `k=60`, un doc classé n°1 dans les deux listes obtient `1/61 + 1/61 ≈ 0.0328` ; un doc classé n°1 en dense mais absent en sparse obtient `1/61 ≈ 0.0164`. Le RRF ne nécessite aucune calibration de score entre systèmes — il classe par position.

## Reranking

Un reranker **cross-encoder** lit la requête et chaque candidat *ensemble*, offrant une précision bien meilleure que le bi-encoder utilisé pour la récupération de première étape — à un coût plus élevé. Récupérez largement (`top_k` 40–100), effectuez un reranking vers un petit `top_n` (5–10) pour le LLM.

| | Bi-encoder (récupération) | Cross-encoder (rerank) |
| --- | --- | --- |
| Encode | Requête et docs séparément (précalculé) | Requête+doc conjointement, par paire |
| Vitesse | Rapide, au moment de l’indexation | Lent, au moment de la requête |
| Précision | Bon rappel | Meilleure précision |
| Rôle | Première étape, top-k | Seconde étape, top-n |

## Query rewriting

| Technique | Ce qu’elle fait | À utiliser quand |
| --- | --- | --- |
| Rewriting / normalisation | Nettoyer, développer les abréviations, résoudre les pronoms | Relances de chat (« and the second one? ») |
| Multi-query | Générer plusieurs paraphrases, récupérer pour chacune, faire l’union | Questions vagues ou larges |
| HyDE | Générer une réponse hypothétique, l’embedder, récupérer par similarité | Requêtes éparses où le vocabulaire de la réponse diffère |
| Decomposition | Scinder une question en plusieurs parties en sous-requêtes | Questions composées |

## Eval metrics with worked numbers

Séparez la qualité de **récupération** de la qualité de **génération** — une bonne réponse peut masquer une mauvaise récupération et inversement.

| Métrique | Mesure | Formule / exemple |
| --- | --- | --- |
| Recall@k | Avons-nous récupéré le chunk pertinent dans le top-k ? | 8 des 10 requêtes avaient leur chunk gold dans le top-5 → Recall@5 = 0.80 |
| MRR | Rang du premier hit pertinent | Rangs 1, 3, 2 → (1 + 1/3 + 1/2)/3 = 0.611 |
| Precision@k | Fraction du top-k qui est pertinente | 2 pertinents sur top-5 → 0.40 |
| nDCG@k | Pertinence pondérée par le rang | Récompense les hits pertinents près du sommet |
| Faithfulness | La réponse est-elle étayée par le contexte récupéré ? | 47 des 50 affirmations ancrées → 0.94 |
| Answer relevance | La réponse répond-elle à la question ? | Grille humaine/LLM-juge |
| Context precision | Les chunks récupérés sont-ils réellement utilisés ? | Faible → récupération de bruit |

Comparaison travaillée — l’ajout d’un reranker sur une eval de 200 requêtes :

| Configuration | Recall@5 | MRR | Faithfulness | Answer relevance |
| --- | --- | --- | --- | --- |
| Dense seul | 0.71 | 0.52 | 0.86 | 0.83 |
| Hybride (RRF) | 0.83 | 0.61 | 0.90 | 0.86 |
| Hybride + reranker | 0.83 | 0.74 | 0.94 | 0.90 |

Le reranking bouge à peine le Recall@5 (mêmes candidats) mais relève nettement le MRR et la faithfulness en ordonnant le *bon* chunk en premier — le LLM le voit plus tôt.

## Debugging playbook

<Steps>
1. **Reproduire avec la trace.** Journalisez la requête, la requête réécrite, les IDs+scores des chunks récupérés, l’ordre après reranking et le prompt final. La plupart des bugs de « mauvaise réponse » sont visibles ici.
2. **Le chunk gold est-il récupéré du tout ?** Si pas dans le top-k → problème de récupération : vérifiez le chunking (dérive), les embeddings (mauvais modèle/dimension) ou la réécriture de requête.
3. **Récupéré mais mal classé ?** Ajoutez/ajustez le reranker ; réglez le `k` du RRF ; vérifiez la pondération hybride.
4. **Récupéré et en tête mais réponse fausse ?** C’est maintenant la génération : vérifiez le prompt (les chunks sont-ils réellement transmis ?), l’ordonnancement du contexte (docs d’abord) et la faithfulness (le modèle ignore-t-il le contexte ?).
5. **Confiant-mais-faux après une actualisation ?** Suspectez l’index : ré-embeddé avec un modèle différent, chunker changé ou vecteurs périmés. Rejouez les evals de récupération.
6. **Vérification par segment.** Ventilez les métriques par type/source de document ; un agrégat de 90 % peut masquer une source à 40 %.
</Steps>

## Idées fausses courantes
| Idée reçue | Réalité | Pourquoi cela compte à l’examen |
| --- | --- | --- |
| « Plus de contexte aide toujours » | Le bruit abaisse la faithfulness ; effectuez un reranking vers un petit top-n | Distracteur sur-récupération |
| « La récupération dense couvre tout » | Elle manque les IDs/codes exacts ; ajoutez BM25 | Signal recherche hybride |
| « Une mauvaise réponse signifie corriger le prompt » | Vérifiez d’abord la récupération (dérive, index) | Distracteur cause racine |
| « Une faithfulness agrégée de 0.9 convient » | Une source peut échouer ; reportez par segment | Anti-pattern métrique agrégée |
| « Faire du fine-tuning pour ajouter de nouveaux faits » | Le fine-tuning est pour le style/format ; le RAG pour les faits | Distracteur RAG-vs-fine-tuning |
| « Le reranking améliore le rappel » | Il améliore l’ordonnancement/la précision, pas le rappel | Distracteur confusion de métrique |
| « Des chunks plus gros = meilleur contexte » | Ils diluent les matches ; utilisez parent-child | Distracteur chunking |

## Étude de cas guidée
Un assistant de support sur 40 000 articles de KB (mis à jour chaque semaine) s’est mis cette semaine à donner des réponses confiantes mais fausses. Les utilisateurs posent à la fois des questions en langage naturel et des codes d’erreur exacts. Quelle est la PREMIÈRE étape et la bonne architecture ?

<Steps>
1. **PREMIÈRE étape** — tirez les traces et vérifiez si le chunk gold est récupéré. Il est apparu que la ré-ingestion de cette semaine a changé le chunker (dérive) — un problème de récupération, pas de prompt.
2. **Chunking** — structural/parent-child pour les articles : chunks enfants pour les matches précis, sections parentes pour le contexte. Versionnez la configuration.
3. **Récupération** — hybride (dense + BM25) : les codes d’erreur nécessitent une correspondance de terme exacte que le dense manque.
4. **Reranking** — cross-encoder vers top-8 ; l’article gold était récupéré mais classé n°14 avant reranking.
5. **Eval** — recall@k, MRR, faithfulness, par source ; conditionnez les ré-ingestions à ces métriques.
6. **Citations** — attachez les spans pour que les réponses soient auditables (test de provenance).
</Steps>

Alternatives rejetées : réécrire le prompt système (la récupération était cassée), faire du fine-tuning sur la KB (les faits changent chaque semaine — c’est le rôle du RAG), la récupération dense seule (manque les codes d’erreur) et se fier au score agrégé (a masqué la source défaillante).

## À retenir
- RAG pour les corpus grands/changeants + citations ; contexte long pour un petit corpus stable ; fine-tuning pour le style, pas les faits.
- Chunkez pour correspondre à la forme des données et au motif de requête ; le parent-child équilibre hits précis et contexte ; versionnez la configuration.
- Hybride (dense + BM25) + reranking cross-encoder bat chacun seul ; le RRF fusionne sans calibration de score.
- Séparez les métriques de récupération (recall@k, MRR) des métriques de génération (faithfulness, answer relevance), et reportez par segment.
- Déboguez à partir de la trace : chunk gold récupéré ? classé ? transmis ? utilisé ? Suspectez d’abord l’index après une actualisation.
