Annexes · OpenAI
RAG Cookbook (OpenAI)
Recettes de retrieval-augmented generation pour la stack OpenAI — anatomie du pipeline, arbitrages de chunking, embeddings et vector stores, l’outil file search versus un store auto-géré, retrieval hybride et reranking, grounding, permissions, fraîcheur, évaluer le retrieval séparément de la génération, une taxonomie d’échecs, et un budget de coût et de latence.
Le RAG ancre les réponses dans des passages retrouvés au moment de la requête. Tournez-vous vers lui quand le corpus est grand ou changeant et que vous avez besoin de citations. Préférez le long contexte pour un petit corpus stable qui tient confortablement dans la fenêtre ; préférez le fine-tuning pour le style et le format, pas pour les faits. Ce cookbook reflète les objectifs du cours Academy Build with Retrieval-Augmented Generation ; c’est une préparation indépendante construite à partir des objectifs d’apprentissage publiés.
Signal d’évaluation
« Confiant mais faux après un rafraîchissement de documents » pointe d’abord vers le retrieval ou l’indexation — un index périmé, un chunker changé, re-embedé avec un modèle différent — pas vers le prompt ou le modèle. Réécrire le prompt est le distracteur.
Anatomie du pipeline
ingest ──► chunk ──► embed ──► store (build time, offline) │ query ──► rewrite ──► retrieve ──► rerank ──► assemble ──► generate ──► cite │ (query time, online)Deux moitiés échouent pour des raisons différentes. La moitié build-time (chunk, embed, store) échoue silencieusement : un mauvais chunker ou un modèle d’embedding changé dégrade toutes les requêtes futures. La moitié query-time (retrieve, rerank, generate) échoue visiblement, requête par requête. Instrumentez les deux, et évaluez-les séparément.
| Étape | Responsable de | Échec fréquent |
|---|---|---|
| Chunk | Découper les docs en unités retrouvables | Coupe en plein milieu d’une idée ; dérive après re-ingest |
| Embed | Transformer le texte en vecteurs | Modèle faux ou dépareillé entre index et requête |
| Store | Indexation et recherche du plus proche voisin | Vecteurs périmés ; métadonnées de permission manquantes |
| Retrieve | Récupérer des candidats | Rate les IDs exacts (dense seul) ; mauvais top-k |
| Rerank | Ordonner les candidats par pertinence | Absent, donc le passage en or est trop bas |
| Generate | Écrire la réponse ancrée | Ignore le contexte ; pas de citation |
RAG vs alternatives, quand choisir quoi
| Situation | Choisir | Pourquoi |
|---|---|---|
| Des milliers de docs, mis à jour souvent, avec citation obligatoire | RAG | Frais, ancré, auditable |
| Un manuel de 50 pages qui change rarement et tient dans la fenêtre | Long contexte | Aucune infrastructure de retrieval ; le plus simple |
| « Toujours répondre dans notre style / format maison » | Fine-tuning ou few-shot | Comportement, pas faits |
| Le modèle doit décider quand et quoi retrouver | RAG agentique (retrieval comme tool) | Reformulation itérative |
Les modèles GPT-5.6 et GPT-6 portent une grande fenêtre de contexte, ce qui tente les équipes de « tout coller ». Cela marche pour un petit corpus stable, mais pour un corpus grand ou changeant c’est plus cher par requête, plus dur à garder frais, et vous n’avez aucune citation à auditer.
Stratégies de chunking avec arbitrages
| Stratégie | Point de départ | Convient à | Arbitrage |
|---|---|---|---|
| Taille fixe | 512 tokens, chevauchement de 50 tokens | Prose uniforme | Coupe en plein milieu d’une idée |
| Récursif (par séparateur) | 400–800 tokens, découpe sur \n\n puis \n puis phrase | Prose mixte | Tailles inégales |
| Sémantique | Coupe là où la similarité d’embedding baisse | Docs qui changent de sujet | Coût de calcul à l’ingest |
| Structurel / document-aware | Un chunk par section ou titre | Manuels, contrats, wikis | Besoin d’une structure propre |
| Parent-child (small-to-big) | Enfant 150–300 tok pour le match, retourne le parent 800–1200 tok | Hit précis plus contexte large | Store à deux niveaux |
Règle empirique du chevauchement : 10–20 % de la taille du chunk. Trop peu perd le contexte de frontière ; trop gonfle l’index et produit des hits en double.
Dérive de chunk
Quand des documents sont re-ingérés avec un chunker ou une taille différents, la qualité du retrieval change silencieusement et les réponses deviennent faussement confiantes. Versionnez votre configuration de chunking et relancez les evals de retrieval après tout changement.
Embeddings et vector stores
Les embeddings transforment le texte en vecteurs de sorte que des passages sémantiquement proches se situent près les uns des autres. Deux règles dominent :
- L’index et la requête doivent utiliser le même modèle d’embedding et la même dimension. Mélanger les modèles est la corruption silencieuse classique : la similarité cosinus entre des vecteurs de deux modèles différents n’a aucun sens.
- Re-embedez tout le corpus quand vous changez de modèle. Un index à moitié migré retourne du bruit pour la moitié non migrée. Traitez un changement de modèle d’embedding comme une migration de schéma : tout ou rien, verrouillé sur un eval.
Le store contient les vecteurs plus des métadonnées (source, section, ACL, timestamp) et fait une recherche approximative du plus proche voisin. Sur la stack OpenAI, vous pouvez soit laisser la plateforme gérer cela pour vous, soit exécuter le vôtre.
L’outil file search vs un store auto-géré
| Outil file search (managé) | Vector store auto-géré | |
|---|---|---|
| Qui chunke et embede | La plateforme | Vous |
| Qui indexe et retrouve | La plateforme, appelée comme un tool | Votre base de données et votre code |
| Contrôle sur chunking / reranking | Limité aux options de l’outil | Total |
| Idéal quand | Vous voulez du retrieval rapide, avec moins d’infrastructure | Vous avez besoin de chunking personnalisé, de recherche hybride, de votre propre reranker ou de votre propre store de référence |
| Arbitrage | Moins de surface de réglage | Vous êtes propriétaire de la fraîcheur, des permissions et des evals de bout en bout |
La règle de décision : tournez-vous vers l’outil file search quand vous voulez des réponses ancrées sur vos fichiers sans monter une stack de retrieval, et vers un store auto-géré quand vous avez besoin d’un contrôle que l’outil n’expose pas — un schéma de chunking précis, du retrieval hybride, un cross-encoder reranker, ou un retrieval sensible aux permissions intégré à votre système d’identité.
Retrieval hybride et reranking
Le retrieval dense (embedding) attrape la paraphrase ; le retrieval sparse (BM25) attrape les IDs exacts, les codes d’erreur et les termes rares. Combinez-les, puis rerank.
retrieval: dense: { top_k: 40 } sparse: { algorithm: bm25, top_k: 40 } fusion: { method: reciprocal_rank_fusion, k: 60 } rerank: { model: cross-encoder, top_n: 8 }Reciprocal Rank Fusion : le score d’un document est la somme sur les listes de 1 / (k + rank). Avec k=60, un document classé n° 1 dans les deux listes score environ 1/61 + 1/61 = 0.033 ; classé n° 1 dans une liste et absent de l’autre score environ 1/61 = 0.016. La RRF n’a besoin d’aucune calibration de score entre les deux systèmes — elle fusionne par la position de rang seule.
Un cross-encoder reranker lit la requête et chaque candidat ensemble, donnant une bien meilleure précision que le retriever de première étape, à un coût par requête plus élevé. Retrouvez largement (top-k 40–100), puis rerank vers un petit top-n (5–10) pour le modèle.
| Retrieval de première étape | Cross-encoder rerank | |
|---|---|---|
| Encode | Requête et docs séparément, précalculé | Requête et doc conjointement, par paire |
| Vitesse | Rapide, à l’index-time | Lent, au query-time |
| Force | Rappel | Précision / ordonnancement |
| Rôle | Récupérer le top-k | Ordonner vers le top-n |
Grounding et mise en forme des citations
Le grounding n’est réel que si la réponse peut être tracée jusqu’à un passage. Deux disciplines :
- Contraignez la réponse au contexte retrouvé, et fournissez un sentinelle pour « non trouvé » :
Answer only from the passages below. If they do not contain the answer, reply NOT COVERED.La sentinelle permet au code de détecter une non-réponse au lieu de livrer une supposition. - Retournez des citations que le lecteur peut vérifier : un identifiant de source et une section ou un span pour chaque affirmation. Quand le modèle doit synthétiser à travers plusieurs passages, demandez-lui de citer chaque passage porteur plutôt qu’une citation globale à la fin.
Retrieval sensible aux permissions
Le retriever ne doit jamais faire remonter un passage que l’utilisateur demandeur n’est pas autorisé à voir. C’est une affaire d’ingest-et-retrieve, pas une affaire de prompt.
- Attachez les métadonnées d’accès à l’ingest — stockez l’ACL de chaque chunk (owner, group, sensibilité) à côté de son vecteur.
- Filtrez au query-time par l’identité de l’utilisateur demandeur — appliquez le filtre de permission dans le retrieval, avant que le modèle ne voie un candidat.
- Ne comptez jamais sur une instruction de prompt comme « ne révèle pas le contenu restreint » — un passage restreint non filtré dans le contexte est déjà une fuite, quoi que dise le prompt.
- Re-vérifiez lors des changements de permission — quand un utilisateur perd l’accès, ses futures requêtes doivent cesser de retourner ces chunks ; l’état de permission vit avec la donnée, pas avec la session.
Les permissions relèvent du retrieval, pas du prompting
Le piège est un énoncé où des données sensibles fuient et où le correctif tentant est une règle de system prompt plus stricte. Si un passage restreint a été retrouvé dans le contexte, le prompt est hors de propos — le correctif est un filtre de permission au moment du retrieval.
Fraîcheur
Un système RAG n’est aussi à jour que son index. Décidez, par corpus, du niveau de fraîcheur requis et construisez pour cela :
| Besoin de fraîcheur | Approche |
|---|---|
| Minutes (prix, tickets) | Retrouvez depuis la source de référence au query-time, ou streamez les mises à jour dans l’index |
| Heures à un jour | Re-ingest planifié ; versionnez l’index et échangez atomiquement |
| Change rarement | Reconstruction complète périodique ; verrouillez l’échange sur un eval de retrieval |
Quelle que soit la cadence, verrouillez les re-ingests sur un eval pour qu’un changement de chunker ou de modèle ne puisse pas dégrader silencieusement le retrieval, et stockez un timestamp de build pour que « confiant mais faux après un rafraîchissement » soit diagnosticable.
Évaluer le retrieval séparément de la génération
Une bonne réponse peut cacher un mauvais retrieval et vice-versa, alors mesurez-les à part.
| Métrique | Mesure | Exemple |
|---|---|---|
| Recall@k | Le passage en or était-il dans le top-k ? | 8 requêtes sur 10 l’avaient dans le top-5 → 0,80 |
| MRR | Rang du premier hit pertinent | Rangs 1, 3, 2 → (1 + 1/3 + 1/2)/3 = 0,61 |
| Precision@k | Fraction du top-k qui est pertinente | 2 du top-5 pertinents → 0,40 |
| Faithfulness | La réponse est-elle étayée par le contexte retrouvé ? | 47 affirmations sur 50 ancrées → 0,94 |
| Answer relevance | La réponse traite-t-elle la question ? | Rubric ou noté par modèle |
Comparaison travaillée sur un ensemble de 200 requêtes :
| Config | 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 ne bouge presque pas le Recall@5 (mêmes candidats) mais relève nettement le MRR et la faithfulness en plaçant le bon passage en premier, là où le modèle le lit le plus tôt. Reportez chaque métrique par segment (type de document, source, langue) — un agrégat de 0,90 peut cacher une source à 0,40.
Taxonomie d’échecs avec correctifs
| Symptôme | Cause probable | Correctif |
|---|---|---|
| Réponse fausse, passage en or non retrouvé | Dérive de chunk, mauvais modèle d’embedding, requête pauvre | Versionner le chunking ; aligner les modèles d’embed ; ajouter du query rewriting |
| Passage en or retrouvé mais mal classé | Pas de reranker ; mauvais poids de fusion | Ajouter un cross-encoder reranker ; régler le k de la RRF |
| Codes / IDs exacts ratés | Retrieval dense seul | Ajouter BM25 (hybride) |
| Retrouvé et bien classé mais réponse fausse | Modèle ignorant le contexte ; contexte non passé | Vérifier que le prompt inclut bien les chunks ; contraindre au contexte |
| Confiant mais faux après un rafraîchissement | Index périmé ou re-chunké | Relancer les evals de retrieval ; vérifier le timestamp de build |
| Une source constamment mauvaise | L’agrégat la cachait | Reporter par segment ; corriger l’ingest de cette source |
| Contenu restreint remonté | Pas de filtre de permission au retrieval | Filtrer par identité au query-time |
Exemple travaillé de budget coût et latence
Un assistant de support sur 40 000 articles, cible de latence de bout en bout sous 2 secondes au p95, gpt-5.6-terra pour la génération.
Per query budget (p95 target: 1900 ms) query rewrite (gpt-5.6-luna, low effort) ~120 ms hybrid retrieve (dense + BM25, top_k 40) ~140 ms cross-encoder rerank (40 -> 8) ~180 ms generate grounded answer (terra, 8 chunks) ~1200 ms citation assembly (code) ~20 ms -------- total (p95) ~1660 ms ✓ under 1900Leviers de coût, du moins cher au plus cher : réduisez le nombre de chunks fournis au modèle (8 → 5 avec un reranker plus fort) avant de toucher au tier de modèle ; utilisez gpt-5.6-luna pour l’étape de rewrite ; cachez le préfixe système stable sur l’API ; et réservez un modèle plus gros aux seules requêtes qu’un router marque comme difficiles. Le mauvais réflexe — « utiliser un plus gros modèle pour corriger les réponses fausses » — dépense de l’argent sur un problème de génération qui est d’habitude un problème de retrieval.
Idées reçues fréquentes
| Idée reçue | Réalité | Pourquoi c’est important à l’assessment |
|---|---|---|
| « Plus de contexte aide toujours » | Le bruit abaisse la faithfulness ; rerank vers un petit top-n | Distracteur de sur-retrieval |
| « Le retrieval dense couvre tout » | Il rate les IDs et codes exacts ; ajoutez BM25 | Signal de recherche hybride |
| « Réponse fausse veut dire corriger le prompt » | Vérifiez d’abord le retrieval après un rafraîchissement | Distracteur de cause racine |
| « Une règle de prompt garde les données restreintes dehors » | Les permissions s’imposent au retrieval, pas par le prompt | Piège de permission |
| « Fine-tuner pour ajouter de nouveaux faits » | Le fine-tuning est pour le style ; le RAG pour les faits | Distracteur RAG-vs-fine-tuning |
| « Le reranking améliore le rappel » | Il améliore l’ordonnancement et la précision, pas le rappel | Distracteur de confusion de métrique |
Déroulé de scénario
Une équipe fait tourner un assistant KB sur 40 000 articles rafraîchis chaque semaine. Cette semaine, il a commencé à donner des réponses faussement confiantes. 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 ?
- PREMIÈRE étape — récupérez les traces et vérifiez si le passage en or est même retrouvé. Il est apparu que le re-ingest de cette semaine a changé le chunker (dérive). C’est un problème de retrieval, donc réécrire le prompt gâcherait le cycle.
- Chunking — structurel ou parent-child pour les articles : chunks enfants pour les matchs précis, sections parentes pour le contexte ; versionnez la config et verrouillez les re-ingests sur un eval.
- Retrieval — hybride dense + BM25, parce que les codes d’erreur ont besoin du match de terme exact que le retrieval dense rate.
- Reranking — cross-encoder vers le top-8 ; l’article en or était retrouvé mais siégeait au rang 14 avant reranking.
- Permissions — filtrez par l’identité de l’utilisateur demandeur au query-time, puisque certains articles sont internes uniquement.
- Eval — recall@k, MRR et faithfulness, reportés par source, pour qu’une seule source en échec ne puisse pas se cacher derrière l’agrégat.
Alternatives rejetées : réécrire le system prompt (le retrieval était cassé), fine-tuner sur la KB (les faits changent chaque semaine — c’est le boulot du RAG), retrieval dense seul (rate les codes d’erreur), et se fier au score agrégé (il masquait la source en échec).
Points clés à retenir
- RAG pour les corpus grands ou changeants avec citations ; long contexte pour les petits corpus stables ; fine-tuning pour le style, pas les faits.
- Chunkez pour épouser la forme de la donnée ; parent-child équilibre les hits précis et le contexte ; versionnez la config et verrouillez les re-ingests.
- Gardez l’index et la requête sur le même modèle d’embedding ; re-embedez tout le corpus à tout changement de modèle.
- Choisissez l’outil file search pour la vitesse avec moins d’infrastructure, un store auto-géré quand vous avez besoin d’un chunking, d’un retrieval hybride, d’un reranker ou d’une intégration de permissions que vous contrôlez.
- L’hybride (dense + BM25) plus un cross-encoder reranker bat chacun seul ; la RRF fusionne sans calibration de score.
- Imposez les permissions et la fraîcheur au moment du retrieval, et évaluez le retrieval (recall@k, MRR) séparément de la génération (faithfulness), par segment.
Dernière mise à jour le 18 sept. 2026