Mon expérience pratique avec RAG : Comment les documents se transforment en réponses d'IA fiables
Comment un PDF se transforme-t-il en une réponse d'IA fiable et basée sur des sources ? Dans cet article, je montre comment, pendant ma formation continue au MIT, j'ai mis en œuvre une pipeline RAG complète en Python – du découpage (chunking) et des embeddings à la recherche vectorielle, en passant par le prompting contrôlé et la citation des sources. Il ne s'agit pas de contenus de cours protégés, mais des principes techniques, des facteurs de qualité et des enseignements pratiques derrière une solution RAG robuste.
Dans le cadre de ma formation continue en Applied AI & Data Science au MIT, j'ai mis en œuvre une pipeline complète de génération augmentée par récupération (Retrieval-Augmented-Generation), abrégée en RAG, en Python. Il ne s'agissait pas seulement pour moi de connecter un grand modèle linguistique (Large Language Model) à un document. Je voulais comprendre comment les différents composants techniques interagissent et comment générer des réponses qui soient traçables, circonscrites et aussi étroitement alignées que possible sur les sources disponibles.
Par respect pour les contenus de cours protégés, je ne décrirai ici ni le cas d'usage concret, ni les documents utilisés, les énoncés de problèmes ou les prompts complets. L'article se concentre plutôt sur le déroulement technique général et sur mes enseignements personnels tirés de l'implémentation.
Pourquoi RAG est-il nécessaire ?
Un grand modèle linguistique possède une vaste connaissance générale, mais ne connaît pas automatiquement les documents internes d'une entreprise. Il peut également inventer des informations ou faire des déclarations obsolètes s'il lui manque la base nécessaire.
RAG complète donc le modèle linguistique par un processus de recherche en amont :
- Les contenus pertinents sont recherchés dans une source de connaissance.
- Seuls ces contenus sont fournis au modèle en tant que contexte.
- Le modèle formule une réponse basée sur ces éléments.
Le modèle linguistique n'est pas réentraîné. Au lieu de cela, il reçoit, au moment de l'exécution, exactement les informations pertinentes pour une question spécifique.
Pour moi, ce fut une prise de conscience importante : un système RAG n'est pas simplement un chatbot avec un téléchargement de documents. C'est une pipeline de traitement de documents, de recherche sémantique, de construction de contexte, de prompting et de génération de texte contrôlée.
1. Mise en place de l'environnement technique
L'implémentation a été réalisée en Python. Plusieurs composants spécialisés ont été combinés pour le traitement des documents, la tokenisation, la création d'embeddings et la recherche vectorielle.
Les éléments centraux étaient :
pypdfpour la lecture de documents PDF,- LangChain pour la structure des documents et la division du texte,
tiktokenpour le découpage (chunking) basé sur les tokens,- Modèles Gemini pour les embeddings et la génération de texte,
- ChromaDB comme base de données vectorielle persistante,
- un client compatible OpenAI pour les appels de modèle.
Cette architecture m'a montré qu'une solution RAG ne se compose normalement pas d'un seul framework. La qualité résulte plutôt de la bonne interaction de plusieurs composants.
2. Lecture et uniformisation des documents
La pipeline a été conçue pour pouvoir traiter aussi bien un seul PDF qu'un répertoire contenant plusieurs fichiers PDF.
Pour les fichiers individuels, le texte a été extrait page par page, puis fusionné. Pour plusieurs fichiers, les documents pouvaient être chargés via un Directory Loader.
Le résultat de cette phase n'était pas encore une base de connaissances consultable. Il s'agissait initialement uniquement de texte extrait et non structuré.
Dès ce stade, il est apparu clairement à quel point la qualité des documents sources est importante. Si un PDF ne peut pas être lu correctement, les titres, les tableaux ou les textes contenus dans les images peuvent être entièrement manquants. Un système RAG ne peut trouver que des informations qui ont été correctement extraites et indexées au préalable.
3. Découpage (Chunking) basé sur les tokens avec chevauchement
Un document complet ne peut généralement pas être transmis de manière significative comme un seul contexte au modèle linguistique. Il serait trop volumineux, trop coûteux et trop imprécis pour la recherche sémantique.
C'est pourquoi le texte a été divisé en sections plus petites, appelées chunks.
Dans le notebook, un séparateur de texte récursif a été utilisé, qui ne comptait pas seulement les caractères ou les mots, mais s'orientait sur la tokenisation réelle d'un modèle linguistique. La taille maximale du chunk et le chevauchement entre les sections adjacentes étaient explicitement configurés.
Le chevauchement est important car les informations pertinentes commencent souvent à la limite d'un chunk et se poursuivent dans la section suivante. Sans chevauchement, le lien sémantique pourrait être rompu.
En même temps, il en résulte un dilemme :
- Des chunks trop petits peuvent perdre des liens importants.
- Des chunks trop grands contiennent beaucoup de texte non pertinent.
- Des chevauchements trop importants créent des doublons inutiles et augmentent les coûts.
- Des chevauchements trop petits peuvent séparer des contenus liés.
Le découpage (chunking) n'est donc pas un simple prétraitement technique, mais l'une des décisions de qualité les plus importantes d'un système RAG.
4. Représentation sémantique par les embeddings
Après le découpage, les différentes sections de texte ont été transformées en embeddings.
Un embedding est une représentation numérique du contenu sémantique d'un texte. Les textes sémantiquement similaires doivent être aussi proches que possible dans l'espace vectoriel, même s'ils n'utilisent pas les mêmes mots.
Les embeddings ont été générés non pas individuellement, mais par lots (batches). Cela réduit le nombre d'appels API et rend le traitement de grandes quantités de documents plus efficace.
Chaque chunk a également reçu un identifiant unique. Cela a permis de suivre ultérieurement quelles sections ont été utilisées pour une réponse.
Pour moi, il était particulièrement intéressant de noter qu'une recherche RAG ne cherche pas principalement des termes identiques. Une question d'utilisateur peut être formulée différemment du document et pourtant trouver la section correspondante si les deux sont sémantiquement similaires.
5. Stockage dans une base de données Chroma persistante
Les sections de texte et les embeddings générés ont été stockés dans une base de données vectorielle Chroma.
La collection a été configurée avec la Similarité cosinus. Cette mesure de similarité évalue la direction de deux vecteurs et est fréquemment utilisée pour identifier des textes sémantiquement similaires.
La base de données a été stockée de manière persistante. Ainsi, l'ensemble du processus d'embedding n'a pas eu besoin d'être exécuté à chaque redémarrage.
Cela présente deux avantages immédiats :
- des temps de démarrage et de traitement plus courts,
- des coûts réduits grâce aux appels d'embedding évités.
Dans un système de production, une stratégie contrôlée pour les mises à jour devrait également être mise en œuvre. Si des documents sont modifiés, il doit être clair quels chunks sont nouvellement créés, mis à jour ou supprimés. Dans le notebook, l'accent a été mis initialement sur la construction et la compréhension de la pipeline complète.
6. Également intégrer la question de l'utilisateur
Une fois la base de connaissances préparée, une question d'utilisateur pouvait être traitée.
Pour cela, la question a été traduite en un vecteur à l'aide du même modèle d'embedding. Ce vecteur de requête a ensuite été comparé aux vecteurs de documents stockés.
La base de données vectorielle a renvoyé les sections de texte les mieux adaptées. Dans le notebook, un nombre limité des chunks les plus pertinents a été récupéré.
Entre autres, les éléments suivants ont été renvoyés :
- les IDs de chunk,
- les textes associés,
- les métadonnées possibles,
- les distances calculées.
La phase de récupération était ainsi terminée. À ce stade, le modèle linguistique n'avait pas encore généré de réponse. La pipeline avait seulement déterminé le contexte probablement le plus pertinent.
7. Construire le contexte de manière contrôlée
Les chunks trouvés ont ensuite été assemblés pour former un contexte commun.
Chaque section a reçu son ID comme en-tête visible. Les chunks ont été clairement séparés les uns des autres afin que leurs limites restent reconnaissables dans le prompt.
Cette structure avait deux fonctions :
- Le modèle pouvait mieux distinguer les informations fournies les unes des autres.
- Les sources utilisées pouvaient être renvoyées ultérieurement sur la base des IDs de chunk.
Le prompt complet était composé d'une instruction système, du contexte récupéré et de la question de l'utilisateur elle-même. Le contexte et la question étaient séparés l'un de l'autre par des marqueurs clairement définis.
Cette séparation est importante car le contenu des documents ne doit pas être interprété accidentellement comme une instruction système. Dans les environnements de production, cette zone devrait être sécurisée davantage contre les injections de prompt et les contenus de documents manipulés.
8. Limiter délibérément les hallucinations
Le modèle a reçu l'instruction claire de répondre exclusivement sur la base du contexte fourni.
Si l'information recherchée n'était pas contenue dans le contexte, il ne devait pas inventer une réponse plausible, mais indiquer explicitement que la réponse n'était pas connue.
De plus, les réponses ont été délibérément demandées de manière concise et liée aux sources. Un paramètre déterministe a été utilisé pour la génération et la longueur maximale de la sortie a été limitée.
Ces mesures peuvent réduire les hallucinations :
- contexte clairement délimité,
- instruction de fondation stricte,
- réponse de repli définie,
- température basse ou déterministe,
- longueur de réponse limitée,
- renvoi des sources utilisées.
Cependant, ces mesures ne peuvent pas empêcher complètement les hallucinations. Un LLM peut mal interpréter un texte existant ou relier de manière erronée différentes sections entre elles. C'est pourquoi l'évaluation reste une composante nécessaire de toute solution RAG sérieuse.
9. Retourner la réponse et les sources ensemble
La fonction finale a combiné la récupération et la génération en un seul flux :
- Recevoir la question,
- Créer l'embedding de requête,
- Récupérer les chunks pertinents,
- Générer le contexte,
- Assembler le prompt,
- Générer la réponse,
- Retourner les IDs de chunk utilisés.
En résultat, non seulement le texte de la réponse, mais aussi les sources utilisées et les détails complets de la récupération ont été affichés.
Je trouve cette séparation particulièrement importante. Une pure réponse d'IA est difficilement vérifiable. Si, en revanche, il est visible sur quelles sections de document elle est basée, les utilisateurs ou les processus en aval peuvent comprendre l'affirmation.
L'attribution de source n'est donc pas seulement une fonction de confort, mais un élément essentiel de la confiance, de la gouvernance et de l'assurance qualité.
Ce que j'ai appris de l'implémentation
Grâce au notebook, j'ai compris que la qualité d'un système RAG est déterminée par plusieurs facteurs interdépendants :
- la qualité des documents sources,
- l'extraction du texte,
- la taille et le chevauchement des chunks,
- le modèle d'embedding utilisé,
- la stratégie de récupération,
- le nombre de chunks récupérés,
- la construction du contexte,
- la clarté du prompt,
- les paramètres de génération,
- l'évaluation des réponses.
Un modèle linguistique puissant ne peut pas compenser de manière fiable une pipeline de récupération faible. Si le mauvais contexte est trouvé, même une réponse linguistiquement parfaite peut être factuellement incorrecte.
Inversement, une bonne recherche sémantique seule ne suffit pas. Les contenus trouvés doivent être transmis au modèle de manière structurée afin qu'il les utilise correctement et limite sa réponse à ces derniers.
Ce que j'ajouterais pour un développement productif
L'implémentation du notebook a fourni le cœur technique complet d'une pipeline RAG. Pour un système de production, j'ajouterais à cette approche d'autres composants :
- métadonnées structurées et filtres de métadonnées,
- re-ranking des chunks trouvés,
- ensembles de données d'évaluation automatisés,
- mesure de la précision de récupération (Retrieval Precision) et de la fidélité (Faithfulness),
- versioning des documents et des index,
- vérification des rôles et des autorisations,
- protection contre l'injection de prompt,
- mise en cache des questions récurrentes,
- journalisation (logging), traçage (tracing) et surveillance des coûts (cost monitoring),
- processus définis pour la mise à jour de la base de connaissances,
- tests de régression automatisés pour les questions typiques.
Mon expérience en qualité logicielle et en automatisation des tests influence fortement ma vision de RAG. Une réponse qui semble convaincante lors d'une démonstration n'est pas encore un système d'IA robuste. Seuls des tests reproductibles, des sources traçables, des cas d'erreur contrôlés et un monitoring en font une solution utilisable à long terme.
Ma conclusion
Mon travail pratique avec RAG m'a montré comment transformer des documents non structurés en une base de connaissances sémantiquement consultable. J'ai moi-même mis en œuvre le processus complet : de la lecture et du découpage (chunking) basé sur les tokens aux embeddings et au stockage vectoriel persistant, en passant par la recherche sémantique, la construction contrôlée du prompt et la sortie de réponses basées sur les sources.
Ce qui a été particulièrement précieux pour moi, c'est d'apprendre RAG non pas comme une fonction de bibliothèque prête à l'emploi, mais comme une architecture traçable. Cela me permet d'évaluer, d'échanger et d'optimiser délibérément les différents composants.
La conclusion centrale pour moi est la suivante :
Un bon système RAG ne doit pas répondre le plus possible. Il doit trouver la bonne information, limiter sa réponse à cette base et montrer de manière transparente d'où elle provient.
L'article s'abstrait délibérément des contenus protégés des études de cas et décrit exclusivement le déroulement technique général et les enseignements que j'en ai tirés.