W-02
Le RAG en pratique : ce qu’ENSET AI m’a appris, et où va la recherche documentaire
Comment ENSET AI répond aux questions à partir des PDF d’une école avec des citations, ce que j’améliorerais dans mon propre pipeline, et les idées qui arrivent, de la recherche hybride et du contextual retrieval à RAFT, Self-RAG et GraphRAG.
Les étudiants ne veulent pas une réponse tirée de la mémoire générale d’un modèle. Ils veulent une réponse tirée de leur cours : ce polycopié, ce TP, cette page. Et ils veulent pouvoir vérifier d’où elle vient.
C’est ce que fait ENSET AI. C’est une plateforme auto-hébergée pour mon école : étudiants et enseignants importent des PDF, discutent avec eux et reçoivent des réponses en streaming, avec les pages sources jointes. Elle repose sur le RAG, la génération augmentée par la recherche : au lieu d’espérer que le modèle connaisse la réponse, on retrouve d’abord les passages pertinents et on les lui donne avec la question.
Cet article explique comment fonctionne le pipeline, ce que j’améliorerais, et où je pense que le RAG va ensuite. Le code est public sur GitHub.
Le pipeline
Le RAG a deux moitiés : l’ingestion, quand un document arrive, et la recherche, quand une question arrive.
Ingestion : du PDF aux passages indexés
- Vérifier le fichier d’abord. Avant qu’un parseur ne touche un fichier importé,
scan_pdf_securityvérifie la signature%PDF-et cherche du contenu actif comme du JavaScript intégré ou des actions automatiques. Tout fichier suspect est bloqué. Ce n’est pas un antivirus, mais un système RAG lit chaque fichier qu’on lui donne : il ne doit pas analyser n’importe quoi à l’aveugle. - Découper en passages. Le texte est coupé en morceaux d’environ 1 000 caractères avec un chevauchement de 200, grâce au
RecursiveCharacterTextSplitterde LangChain. Le chevauchement évite de perdre une phrase coupée à la frontière. - Garder le numéro de page. Chaque passage porte le nom du document et son numéro de page. C’est ce qui rend les citations possibles ensuite.
- Vectoriser et stocker. Chaque passage devient un vecteur avec le modèle
all-MiniLM-L6-v2et est stocké dans sa propre collection par document dans Qdrant (ou Chroma en développement local).
Une collection par document était un choix délibéré. Supprimer un document revient à supprimer une collection, et la recherche d’un utilisateur ne peut toucher que les collections qu’il a le droit de voir.
Recherche : de la question à la réponse citée
Vérifier l’accès. Flask vérifie que l’utilisateur peut lire chaque document sélectionné avant la recherche. La confidentialité est appliquée à l’entrée, pas filtrée à la sortie. Une suite de tests dédiée le vérifie.
Reformuler la question, ou ne pas chercher. Une relance comme « et pour le deuxième ? » ne sert à rien comme requête de recherche. Un petit appel au LLM la reformule en requête autonome à partir des derniers messages. Le même appel peut répondre
__NO_SEARCH__pour une salutation ou une question générale, ce qui évite une recherche inutile.Chercher en parallèle. La requête est lancée en même temps sur la collection de chaque document sélectionné, et les meilleurs passages sont gardés (5 par défaut).
Construire un contexte balisé. Les passages sont entourés de balises auxquelles le modèle peut se référer :
<document id="1" source="network-security.pdf" page="12"> ...texte du passage... </document>Envoyer la réponse en streaming. La réponse arrive dans le navigateur via Server-Sent Events, avec les pages sources jointes pour que l’étudiant puisse les ouvrir.
Les choix d’architecture qui ont compté
- Une seule source de vérité. Les PDF et leurs métadonnées (dans MinIO et PostgreSQL) sont les vraies données. Les vecteurs sont dérivés : un script de réindexation les reconstruit. Changer de modèle d’embedding, ou perdre la base vectorielle, est un désagrément, pas une perte de données.
- L’assistant public est isolé. La page d’accueil propose un chatbot public optionnel. Ses points d’accès ne voient que le contexte qu’un administrateur a validé comme public, jamais d’identifiant de session ou de document, et il ne journalise que des métadonnées avec une IP hachée.
- Plusieurs fournisseurs de LLM. Une fabrique permet à l’administrateur de changer de modèle, avec un ordre de repli automatique quand un fournisseur atteint sa limite de débit.
Ce que j’améliorerais
Écrire cet article m’a fait relire mon propre pipeline avec un œil neuf. Voici ce que je changerais.
« Si ce n’est pas pertinent, utilise tes connaissances »
Le prompt dit au modèle : utilise les résultats s’ils sont pertinents, sinon ignore-les et réponds avec tes propres connaissances. C’est accommodant, mais une réponse peut alors sembler sourcée tout en venant de la mémoire du modèle. Une citation prouve seulement quels passages ont été retrouvés, pas que la réponse en vient.
Pour un outil d’étude, je pense maintenant qu’il faut être plus strict par défaut : répondre à partir des documents, et dire clairement quand ils ne contiennent pas la réponse.
Pas de seuil, et une seule forme de recherche
- Il n’y a pas de score de pertinence minimum. Les 5 meilleurs passages sont envoyés même quand les 5 correspondent mal.
- La recherche est uniquement vectorielle. Les embeddings sont excellents pour le sens et mauvais pour les chaînes exactes. Un code de cours comme
INF-4302, un nom de fonction ou un sigle peuvent échapper à la recherche sémantique alors qu’une simple recherche par mots-clés les trouverait immédiatement. - Les passages perdent leur contexte. Un passage qui dit « ce protocole échoue quand la clé est réutilisée » ne dit pas quel protocole. Hors contexte, il est difficile à retrouver.
Le texte retrouvé n’est pas fiable
Un PDF a un auteur. Si un document contient « ignore les instructions précédentes et… », ce texte arrive dans le contexte du modèle. C’est le même problème d’injection de prompt que j’ai décrit en sécurisant Agora. Ce qui sauve ENSET AI, c’est que le modèle ne peut qu’écrire une réponse : il n’a aucun outil à appeler. Dès qu’un système RAG peut agir, le texte retrouvé doit être traité comme hostile.
Où va le RAG
Le RAG de base (« vectoriser, récupérer les k meilleurs, les mettre dans le prompt ») est le point de départ de tout le monde. Voici la suite, et ce que j’adopterais.
1. Recherche hybride et reranking
Combiner la recherche vectorielle avec une méthode par mots-clés comme BM25, fusionner les deux listes, puis passer par un reranker : un modèle qui lit la question et chaque passage ensemble, et les note bien plus précisément que des embeddings. Chercher large, puis classer finement. Cela règle directement le problème des codes de cours, et c’est la première chose que j’ajouterais.
2. Le contextual retrieval
Proposé par Anthropic en septembre 2024 : avant de vectoriser un passage, un modèle écrit une ou deux phrases qui le situent dans son document (« Ce passage vient du chapitre TLS du cours de sécurité réseau et traite de la réutilisation des clés »). Cela règle le problème du « quel protocole ? ».
Leurs mesures : les embeddings contextuels réduisent les recherches ratées de 35 %, l’ajout d’un BM25 contextuel de 49 %, et l’ajout du reranking de 67 %. Les gains s’additionnent, c’est pourquoi 1 et 2 vont ensemble.
3. Une recherche adaptative et autocorrectrice
Toutes les questions ne demandent pas une recherche, et tous les résultats ne méritent pas confiance.
- Self-RAG entraîne le modèle à décider quand chercher et à juger si chaque passage soutient vraiment sa réponse.
- Corrective RAG (CRAG) ajoute un évaluateur qui note les documents retrouvés. S’ils sont mauvais, il reformule la requête, cherche ailleurs ou les écarte, au lieu de répondre à partir d’un mauvais contexte.
L’étape __NO_SEARCH__ d’ENSET AI en est une toute petite version. L’étape suivante naturelle est de noter aussi ce qui revient, et de dire « les documents ne couvrent pas ce sujet » plutôt que de forcer une réponse.
4. RAFT : apprendre au modèle à lire comme un étudiant
RAFT (Retrieval-Augmented Fine-Tuning, UC Berkeley, 2024) attaque le problème du côté du modèle. Il affine le modèle sur des questions accompagnées du bon document et de documents distracteurs, qui semblent liés mais n’aident pas. Le modèle apprend à citer mot pour mot le passage pertinent, à raisonner étape par étape et à ignorer les distracteurs.
L’article le compare à un examen à livre ouvert : le RAG donne le livre au modèle, RAFT lui apprend à s’en servir, y compris à savoir quelles pages sauter. Pour une école qui garde les mêmes cours d’une année à l’autre, affiner un petit modèle sur ses propres supports avec RAFT est une façon réaliste d’obtenir de meilleures réponses, pour moins cher qu’un grand modèle généraliste.
5. GraphRAG : des questions sur tout le corpus
La recherche des k meilleurs passages est bonne pour « que dit la page 12 sur X ? » et mauvaise pour « quels sont les grands thèmes de tout ce cours ? », car aucun passage isolé ne contient la réponse. GraphRAG (Microsoft) extrait des entités et leurs relations dans un graphe de connaissances, les regroupe en communautés et les résume. Les questions globales peuvent alors être traitées à partir de ces résumés plutôt qu’à partir de quelques passages pris au hasard.
6. Le RAG agentique, et pourquoi l’évaluation passe avant
Tout converge vers le RAG agentique : un modèle qui planifie plusieurs recherches, suit des pistes et combine des sources, au lieu d’une seule consultation. C’est puissant, et cela ramène toutes les questions de sécurité d’Agora, puisque le texte retrouvé peut désormais influencer des actions.
Mais la pièce la plus importante n’est pas une technique, c’est l’évaluation. Avant de changer quoi que ce soit, il faut un jeu fixe de vraies questions aux réponses connues, et des mesures comme le rappel de recherche (le bon passage est-il revenu ?) et la fidélité (la réponse est-elle soutenue par ce qui a été retrouvé ?). Sans cela, chaque « amélioration » est une supposition, et un changement qui dégrade discrètement les réponses peut passer inaperçu pendant des mois.
Ma feuille de route pour ENSET AI
Dans l’ordre :
- Construire un petit jeu d’évaluation à partir de vraies questions de cours : rappel et fidélité, exécutés en CI.
- Ajouter la recherche hybride et un reranker, et mesurer la différence.
- Des descriptions contextuelles des passages à l’ingestion.
- Un seuil de pertinence et un prompt plus strict : répondre à partir des documents, ou dire qu’ils ne couvrent pas le sujet.
- Plus tard : noter les passages retrouvés à la manière de CRAG, et expérimenter RAFT sur un petit modèle ouvert.
Ce que j’en retiens
Faire marcher une démo RAG prend un après-midi. La rendre digne de confiance, c’est le vrai travail : vérifier les accès avant la recherche, des citations qu’un étudiant peut contrôler, une frontière nette entre données privées et publiques, et de l’honnêteté sur ce qu’une citation prouve ou non.
Et les plus grands gains sont rarement là où on les attend. Pas un modèle plus gros, mais une meilleure recherche, un ancrage plus strict et un moyen de mesurer les deux. Mesurez avant d’optimiser.