
Un RAG classique fait une seule recherche, puis répond. Si cette recherche est mauvaise — question ambiguë, mal formulée —, la réponse l'est aussi, et rien ne le rattrape. Le RAG agentique brise ce schéma figé : il transforme le retrieval en une boucle qui se corrige. Un graphe LangGraph cherche, évalue si le contexte récupéré suffit, et sinon reformule la question pour relancer une recherche — jusqu'à un contexte jugé suffisant ou un plafond atteint. Ce guide construit ce graphe nœud par nœud, et explique quand le RAG agentique vaut son surcoût. Public visé : développeur qui maîtrise le RAG de base et LangGraph, et veut un retrieval robuste.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Pourquoi un retrieval unique est un point de fragilité.
- Modéliser un RAG comme une boucle avec LangGraph.
- Faire évaluer par un nœud LLM si le contexte suffit.
- Reformuler la question pour relancer une recherche.
- Borner la boucle et savoir quand le RAG agentique vaut son coût.
Prérequis
Section intitulée « Prérequis »- Avoir un RAG fonctionnel.
- Connaître LangGraph et son modèle de graphe.
- Une instance Ollama avec
qwen2.5etnomic-embed-text.
Le point de fragilité du RAG classique
Section intitulée « Le point de fragilité du RAG classique »Le RAG des guides précédents est linéaire : on vectorise la question, on cherche, on injecte le contexte, on répond. Une chaîne, un seul passage.
Cette linéarité a un défaut. Si la recherche échoue — la question est vague, formulée autrement que les documents, ou trop large —, le contexte récupéré est mauvais, et le RAG répond mal sans le savoir. Aucune étape ne vérifie que le contexte tient la route avant de générer.
Le RAG agentique ajoute cette vérification, et la transforme en boucle. Au lieu de « cherche puis réponds », le schéma devient « cherche, juge, et si ce n'est pas bon, réessaie autrement ». Le retrieval gagne une capacité d'auto-correction — exactement ce qu'apporte un agent par rapport à un script.
Modéliser le RAG comme un graphe
Section intitulée « Modéliser le RAG comme un graphe »Une boucle qui se corrige se modélise mal avec une fonction linéaire — mais très bien avec un graphe d'états LangGraph. On reprend le modèle des guides sur les agents : un état partagé, des nœuds qui le transforment, des arêtes qui décident du parcours.
L'état porte tout ce que la boucle manipule :
from typing_extensions import TypedDict
class EtatRag(TypedDict): question: str # la question d'origine, jamais modifiée requete: str # la requête courante, éventuellement reformulée contexte: list[str] # les chunks récupérés suffisant: bool # le contexte permet-il de répondre ? tentatives: int # nombre de recherches effectuées reponse: str # la réponse finaleLa distinction entre question et requete est centrale. La question est ce que l'utilisateur a demandé — elle ne change jamais. La requete est ce avec quoi on cherche — elle, peut être reformulée. Garder les deux séparées permet de reformuler sans perdre l'intention initiale.
Les nœuds de la boucle
Section intitulée « Les nœuds de la boucle »Quatre nœuds composent le graphe. rechercher récupère les chunks les plus proches de la requete courante, et incrémente le compteur de tentatives.
def rechercher(etat: EtatRag) -> dict: """Récupère les chunks les plus proches de la requête courante.""" v_requete = EMBED.embed_query(etat.get("requete") or etat["question"]) # ... classement par similarité ... return { "contexte": [CORPUS[i] for i in classes[:2]], "tentatives": etat.get("tentatives", 0) + 1, }evaluer est le cœur du dispositif : un nœud LLM qui juge si le contexte récupéré suffit. Il utilise une sortie structurée — un booléen — pour produire un verdict exploitable par le graphe.
class Evaluation(BaseModel): suffisant: bool = Field( description="Vrai si le contexte permet de répondre à la question" )
def evaluer(etat: EtatRag) -> dict: """Juge si le contexte récupéré suffit à répondre à la question.""" verdict = LLM.with_structured_output(Evaluation).invoke([ ("system", "Tu juges si un contexte suffit à répondre à une " "question. Le contexte contient plusieurs passages, dont certains " "peuvent être hors sujet — c'est normal. Réponds suffisant=true " "si AU MOINS UN passage contient l'information pour répondre."), ("human", f"Question : {etat['question']}\n\nContexte :\n" + "\n".join(f"- {c}" for c in etat["contexte"])), ]) return {"suffisant": verdict.suffisant}reformuler entre en jeu quand le contexte est jugé insuffisant : il demande au modèle de réécrire la question autrement, pour donner une autre chance à la recherche. repondre, enfin, génère la réponse à partir du contexte retenu.
Boucler, et savoir s'arrêter
Section intitulée « Boucler, et savoir s'arrêter »Reste à câbler le parcours. Après evaluer, une fonction de routage décide : reformuler et rechercher encore, ou répondre maintenant.
MAX_TENTATIVES = 3
def router(etat: EtatRag) -> Literal["reformuler", "repondre"]: """Décide : reformuler et chercher encore, ou répondre maintenant.""" if etat["suffisant"] or etat["tentatives"] >= MAX_TENTATIVES: return "repondre" return "reformuler"Deux conditions mènent à repondre. Le contexte est suffisant : inutile de chercher plus. Ou bien le plafond de tentatives est atteint : on répond avec ce qu'on a, plutôt que de boucler sans fin. Ce MAX_TENTATIVES est obligatoire — sans lui, une question impossible ferait tourner le graphe indéfiniment.
Le graphe se construit autour de cette logique : reformuler renvoie vers rechercher, formant la boucle de correction.
constructeur.add_edge(START, "rechercher")constructeur.add_edge("rechercher", "evaluer")constructeur.add_conditional_edges("evaluer", router, ["reformuler", "repondre"])constructeur.add_edge("reformuler", "rechercher") # la boucleconstructeur.add_edge("repondre", END)À l'exécution, sur une question dont le premier retrieval suffit, le graphe converge immédiatement :
Recherches effectuées : 1Contexte suffisant : TrueRéponse : Pour garder les données d'un conteneur Docker, vous pouvezutiliser un volume...Une recherche, une évaluation positive, une réponse. Sur une question plus difficile, le graphe boucle — reformule, recherche à nouveau — jusqu'à converger ou atteindre le plafond. Le parcours s'adapte à la difficulté de la question.
Quand le RAG agentique vaut son coût
Section intitulée « Quand le RAG agentique vaut son coût »Le RAG agentique est plus robuste — mais il coûte plus cher. Chaque tour de boucle ajoute un appel d'évaluation, parfois un appel de reformulation, et une recherche de plus. Une question qui boucle trois fois consomme trois fois le travail d'un RAG classique.
Ce surcoût se justifie sur les questions complexes ou mal formulées — là où une recherche unique a de bonnes chances d'échouer, et où une seconde tentative reformulée change tout. Il ne se justifie pas sur des questions simples et directes : la boucle s'y déclenche pour rien, et alourdit la réponse.
La bonne approche est graduée. On commence par un RAG classique. Si la mesure — le recall, vu dans le guide sur le RAG en production — montre un retrieval qui échoue sur une part notable des questions, alors le RAG agentique devient pertinent. C'est la même règle que pour la recherche hybride ou le re-ranking : on mesure, puis on décide.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
| La boucle ne s'arrête jamais | MAX_TENTATIVES absent ou non testé | Vérifier la condition d'arrêt du routeur |
GraphRecursionError | Boucle non bornée | Borner les tentatives, vérifier le routeur |
| L'évaluateur rejette un bon contexte | Consigne trop stricte | Préciser qu'un passage pertinent suffit |
| La reformulation n'améliore rien | Question déjà optimale | Normal : le plafond fera répondre |
| Coût élevé sur questions simples | RAG agentique appliqué partout | Le réserver aux questions difficiles |
À retenir
Section intitulée « À retenir »- Un RAG linéaire échoue en silence quand sa recherche unique rate.
- Le RAG agentique transforme le retrieval en boucle qui se corrige.
- Un graphe LangGraph modélise cette boucle : nœuds rechercher, évaluer, reformuler, répondre.
- Le nœud
evaluerjuge la suffisance du contexte — sa consigne doit accepter un contexte partiellement pertinent. - Un plafond de tentatives est obligatoire pour borner la boucle.
- Le RAG agentique coûte plus : on le réserve aux questions difficiles, sur la foi d'une mesure.