tutorial/rag-bout-en-bout.md

RAG de bout en bout — de l'ingestion au retrieval

Ce tutoriel vous fait construire, brique par brique, un pipeline de RAG (Retrieval-Augmented Generation) qui répond à une question à partir d'une base documentaire, en citant ses sources.

À la fin, vous aurez suivi le trajet complet d'une question : de l'indexation hors-ligne de vos documents jusqu'à une réponse ancrée dans des passages réels.

Public visé : vous savez programmer, vous n'avez encore jamais câblé un RAG complet. Aucun prérequis sur les bases vectorielles.

Ce que vous allez assembler :

INDEXATION (une fois, hors-ligne)
  documents → découpage → embeddings → index vectoriel

REQUĂŠTE (Ă  chaque question)
  question → embedding → recherche kNN → passages + sources
           → prompt → modèle de génération → réponse + citations

Nous avancerons en cinq étapes. Suivez-les dans l'ordre : chacune produit un artefact que la suivante consomme.


Avant de commencer — le vocabulaire minimal

Trois notions suffisent pour démarrer :

  • Embedding : un vecteur de nombres qui reprĂ©sente le sens d'un texte. Deux textes proches par le sens ont des vecteurs proches.
  • Index vectoriel : une structure qui stocke ces vecteurs et sait retrouver rapidement les plus proches d'un vecteur donnĂ© (recherche kNN, k nearest neighbors).
  • Retrieval : l'Ă©tape qui, Ă  partir d'une question, va chercher dans l'index les passages les plus pertinents.

Le RAG, c'est simplement : retrouver les bons passages, puis demander au modèle de répondre uniquement à partir d'eux.


Étape 1 — Indexer vos documents

L'indexation se fait une seule fois (puis à chaque mise à jour du corpus). Elle se décompose en trois sous-étapes.

1.1 Découper (chunking)

Un document entier est trop gros pour être embeddé d'un bloc : le vecteur deviendrait « flou » et la recherche perdrait en précision. On découpe donc chaque document en passages (chunks) de taille modérée, avec un léger chevauchement entre passages consécutifs pour ne pas couper une idée en deux.

Conservez, pour chaque passage, ses métadonnées : identifiant du document d'origine, titre de section, position. Ce sont elles qui permettront de citer la source plus tard.

1.2 Calculer les embeddings

Envoyez chaque passage à votre modèle d'embeddings. Vous obtenez un vecteur par passage.

Point d'attention : beaucoup de modèles d'embeddings distinguent le rôle du texte encodé. Un même modèle peut attendre un préfixe différent selon qu'on encode un passage à indexer ou une question à rechercher. Respectez la convention de votre modèle (par exemple des préfixes de type passage: / query:) — sinon la qualité de recherche se dégrade silencieusement.

1.3 Stocker dans un index vectoriel

Insérez chaque paire (vecteur, métadonnées) dans votre index vectoriel. Pour un projet qui démarre, une petite base vectorielle embarquée suffit largement — inutile de déployer une infrastructure dédiée.

À la fin de l'étape 1, vous avez un fichier d'index contenant tous vos passages et leurs vecteurs. C'est votre base de connaissances interrogeable.


Étape 2 — Récupérer les passages pertinents (retrieval)

À chaque question, on rejoue trois opérations :

1. Embedder la question (rôle « requête »).
2. Chercher les k plus proches voisins dans l'index → identifiants de passages + score de distance.
3. Résoudre chaque identifiant vers son texte et ses métadonnées (document, section).

Astuce qualité déterminante : filtrez les passages dont le score de similarité est sous un seuil. Sans ce garde-fou, vous injectez du bruit — des passages hors-sujet que le modèle prendra pourtant pour du contexte fiable.

Commencez avec un k petit (3 Ă  5 passages) et ajustez.


Étape 3 — Construire le prompt

Vous assemblez maintenant un prompt en trois parties : une instruction système stable, le contexte (les passages récupérés), et la question de l'utilisateur.

[système]
Tu es un assistant documentaire. Réponds UNIQUEMENT à partir du contexte fourni.
Cite tes sources. Si le contexte ne contient pas la réponse, dis-le explicitement.

[contexte]
Passage 1 (source: <document>#<section>) : "..."
Passage 2 (source: <document>#<section>) : "..."

[question]
{question de l'utilisateur}

Deux principes Ă  retenir :

  • L'instruction « uniquement Ă  partir du contexte » est le cĹ“ur du garde-fou anti-hallucination. Sans elle, le modèle comble les trous avec ses connaissances gĂ©nĂ©rales.
  • Le bloc système + contexte est stable d'une requĂŞte Ă  l'autre : c'est un bon candidat au cache de prompt de votre fournisseur, pour rĂ©duire coĂ»t et latence sur les parties rĂ©pĂ©tĂ©es.

Étape 4 — Générer la réponse

Envoyez le prompt au modèle de génération. Ici, la génération est confiée à Claude.

Deux réglages comptent pour un RAG factuel :

  • Bornez la longueur de sortie (max_tokens) pour Ă©viter les rĂ©ponses qui dĂ©bordent.
  • PrivilĂ©giez une rĂ©ponse factuelle et concise. Formulez-le dans l'instruction système (par exemple « rĂ©ponds de façon concise, sans broder »), et laissez le raisonnement adaptatif du modèle dĂ©cider de la profondeur. Sur cette gĂ©nĂ©ration de modèles, on ne règle plus la variabilitĂ© par un paramètre de tempĂ©rature : c'est la consigne qui pilote le ton.

Exemple minimal d'appel (SDK Python) :

import anthropic

client = anthropic.Anthropic()  # lit la clé depuis l'environnement

response = client.messages.create(
    model="<modèle-claude>",  # ex. la version Claude de votre choix
    max_tokens=1024,
    system=(
        "Tu es un assistant documentaire. Réponds UNIQUEMENT à partir du "
        "contexte fourni. Cite tes sources. Si le contexte ne contient pas "
        "la réponse, dis-le. Sois concis, ne brode pas."
    ),
    messages=[{"role": "user", "content": prompt_avec_contexte_et_question}],
)

reponse = next(b.text for b in response.content if b.type == "text")

Ne codez jamais votre clé d'API en dur : laissez le client la lire depuis une variable d'environnement.

Point crucial : conservez, à côté de la réponse générée, la liste des sources des passages que vous avez injectés. Vous en aurez besoin à l'étape suivante.


Étape 5 — Restituer avec citations

Affichez la réponse accompagnée de ses sources (document + section), idéalement cliquables. C'est ce qui transforme une réponse « magique » en réponse vérifiable.

Deux cas à gérer proprement :

  • Question couverte par le corpus → rĂ©ponse + sources rĂ©ellement utilisĂ©es.
  • Question hors corpus → message clair du type « cette information n'est pas dans la documentation ». C'est le comportement attendu : mieux vaut avouer l'absence que d'inventer.

Vérifier que ça marche

Testez avec trois questions :

  1. Une question dont la réponse est clairement dans un document → vous devez obtenir une réponse juste avec la bonne source.
  2. Une reformulation de la même question (mots différents, même sens) → la recherche sémantique doit retrouver le même passage.
  3. Une question hors sujet → vous devez obtenir le message « pas dans la documentation », pas une réponse inventée.

Si le test 3 produit une réponse inventée, votre garde-fou (instruction système + seuil de score) est trop faible.


Erreurs fréquentes

  • Chunks trop gros → vecteurs « flous », mauvais rappel. Calibrez taille et chevauchement.
  • Pas de seuil de score → le modèle reçoit des passages hors-sujet et brode.
  • PrĂ©fixes d'embedding oubliĂ©s (rĂ´le passage vs. requĂŞte) → qualitĂ© dĂ©gradĂ©e sans erreur visible.
  • Citer sans vĂ©rifier → restituez toujours les sources rĂ©ellement injectĂ©es, pas une liste approximative.
  • Instruction système molle → si elle n'impose pas « uniquement Ă  partir du contexte », le modèle hallucine.

Ce que vous avez construit

Un pipeline RAG complet : indexation hors-ligne (découpage → embeddings → index), puis, à chaque question, récupération sémantique, construction de prompt sous garde-fous, génération et restitution sourcée.

Le même moteur de recherche peut ensuite alimenter d'autres surfaces (chatbot, agent) : la mécanique de retrieval reste identique, seule la façade change.

Assistant documentaire

Posez une question sur la documentation. Les réponses citent leurs sources — un clic ouvre le document à gauche.