Sept manières silencieuses dont la pollution contextuelle endommage les agents LangGraph
État obsolète, tas énormes d’outils, fragments RAG dupliqués, résumés avec perte d’informations et ensembles d’outils surdimensionnés : sept échecs qui ne génèrent aucune erreur, accompagnés de solutions et de méthodes pour les détecter.
Sources: 7 Ways Context Pollution Quietly Killed My LangGraph Agents
Imaginez un agent LangGraph qui informe un utilisateur que son projet est « sur la bonne voie pour une livraison en juillet ». Le calendrier indique août. Les journaux ne montrent aucune exception ni appel d’outil échoué, et rien n’est signalé en rouge. Selon tous les indicateurs suivis par l’équipe, l’agent fonctionne correctement, mais pendant trois jours il a trompé discrètement les gens avant que quiconque ne s’en aperçoive.
Ces incidents partagent une cause invisible : la pollution du contexte, et les sept cas présentés ci-dessous en sont tous issus. Chacun d’eux n’a généré aucun erreur. Il s’agit de scénarios fictifs et illustratifs, avec des noms d’entreprise et des chiffres inventés, mais les mécanismes en jeu sont ceux auxquels se heurtent régulièrement les concepteurs d’agents.
La pollution du contexte en deux minutes
Lorsqu’un agent raisonne, il ne voit pas votre application. Il ne voit qu’un long bloc de texte, la fenêtre de contexte, et rien d’autre. Imaginez-le comme un bureau : tout ce qui se trouve sur le bureau est utilisable, tandis que tout ce qui en est hors ne existe pas pour l’agent, même s’il se trouve dans votre base de données à quelques mètres de là.
Chaque tour, plusieurs éléments s’ajoutent sur ce bureau :
- l’invite du système, c’est-à-dire les instructions
- les définitions des outils, c’est-à-dire ce que l’agent peut appeler
- l’historique de la conversation, c’est-à-dire tout ce qui a été dit jusqu’à présent
- les résultats des outils, c’est-à-dire tout ce que vos API ont retourné
- l’état, qui correspond à ce que vos nœuds LangGraph stockent et transmettent
- les documents récupérés, si vous utilisez RAG
Les débutants entendent rarement cela : le bureau a une limite de taille, et l’attention n’est pas répartie uniformément sur lui. Le contenu peut être techniquement présent mais pratiquement ignoré. Il peut également être obsolète, dupliqué ou contradictoire, sans que le modèle ne s’y oppose. Il traite ces éléments inutiles sans aucun commentaire.
La pollution du contexte correspond à des matériaux obsolètes, encombrants ou contradictoires qui se trouvent dans la fenêtre d’interaction et détériorent le fonctionnement du système sans provoquer la moindre erreur. C’est là le danger : une panne vous fait face à 2 heures du matin, tandis que la pollution laisse simplement votre agent fonctionner de manière erronée à 20 % en permanence.
Chaque cas présenté ci-dessous est accompagné d’un symptôme, d’une cause racine, d’une solution et d’une méthode pour le détecter dans votre propre système, car une panne que vous ne pouvez pas repérer reviendra inévitablement.
Cas 1 : l’agent bloqué en juillet
Symptôme. Prenons un agent de reporting pour une application fictive, appelons-la Meridian Energy. Cet agent citait constamment des chiffres « de ce mois » qui appartenaient en réalité au mois précédent. Les utilisateurs en blâmaient le pipeline de données, ce qui était normal.
Cause racine. Au début de la session, un développeur avait stocké une seule fois la période de reporting en cours dans l’état LangGraph (une clé nommée current_period contenant 2026-07), et le point de contrôle l’avait enregistré. Plus tard, les utilisateurs ont repris des sessions anciennes, une fonctionnalité populaire, et le point de contrôle enregistré a ramené la valeur juillet. L’agent n’était pas confus ; il suivait son contexte, et son contexte indiquait juillet.
Réparation. Adoptez une règle qui redéfinit la conception de l’état : ne stockez jamais ce que vous pouvez recalculer facilement. La date actuelle, la période en cours, tout élément étiqueté « aujourd’hui » doit être calculé à nouveau au début de chaque exécution, plutôt que d’être conservé. Déplacer current_period hors de l’état sauvegardé vers un petit nœud qui le recalcule à chaque appel ne coûte qu’une ligne de code et élimine toute une catégorie d’erreurs.
Détection. En phase de développement, affichez le dictionnaire d’état au début de chaque exécution et vérifiez pour chacune des clés si elle pourrait être obsolète et si vous le remarqueriez. Toute clé contenant une date, un statut ou le mot « actuel » est suspecte.
Cas 2 : un dump d’outil de 8 000 tokens qui a noyé les instructions
Symptôme. L’agent a commencé à ignorer les règles de mise en forme. L’instruction système exigeait d’ouvrir chaque réponse par un tableau récapitulatif. Il le faisait au début, mais parfois il ne le faisait plus. De manière aléatoire, ce qui constitue le pire type de bug.
Cause racine. Un outil appelé API intégrait la réponse JSON brute dans le contexte. Pour les petites requêtes, cela représentait 400 tokens. Pour les grandes, ce chiffre dépassait 8 000 tokens de JSON imbriqué, rempli d’ID, de métadonnées, de champs nuls et de détails de pagination dont personne n’avait besoin. En s’insérant au milieu du contexte, ce flux de données transformait des instructions claires en un murmure inaudible au milieu du bruit. L’attention se portait alors sur ce mur de données JSON, et les règles de mise en forme disparaissaient pratiquement.
Le détail désagréable, c’est que cela ne se produisait qu’avec des réponses volumineuses. Les petites requêtes fonctionnaient normalement, ce qui a fait que lier le « comportement anormal de l’agent » au « fait que l’utilisateur a posé une question complexe » a pris des semaines.
Réparation. Les outils doivent renvoyer des faits, et non des données brutes. Ajoutez une couche de transformation légère à l’intérieur de chaque outil qui extrait les quelques champs nécessaires à l’agent, les structure de manière compacte et élimine le reste. Un tel outil est passé d’environ 8 000 tokens à environ 300.
# Before: return response.json() # 8,000 tokens of noise
# After:
def summarize_response(data: dict) -> dict:
return {
"project": data["name"],
"status": data["status"],
"owner": data["owner"]["displayName"],
"due": data["dueDate"],
"open_risks": len(data.get("risks", [])),
} # ~300 tokens of signal
Détection. Enregistrez le nombre de tokens de chaque sortie d’outil. Un outil qui peut retourner plus de tokens que l’intégralité du prompt système constitue une source de pollution susceptible de générer une réponse volumineuse.
Cas 3 : une correction qui n’a jamais remplacé l’original
Symptôme. Un utilisateur demande des informations sur la région du Nord-Est, puis se corrige immédiatement en demandant celles du Nord-Ouest. L’agent répond correctement concernant le Nord-Ouest. Après quatre échanges, sans qu’on le lui demande, il revient à la région du Nord-Est.
Cause racine. L’historique des conversations est par défaut en mode ajout uniquement, de sorte que l’instruction erronée ainsi que la correction restent définitivement dans le contexte. Généralement, le modèle prend en compte correctement le message le plus récent, mais ce n’est pas toujours le cas ; à mesure que la conversation s’allonge, les deux versions deviennent des faits tout aussi plausibles. L’agent ne se souvenait pas à tort, il se souvenait de trop de choses.
Réparation. Il existe deux options.
- Résoudre, ne pas accumuler. Lorsqu’on repère une correction, il ne faut pas simplement l’ajouter. Il convient d’écrire la nouvelle valeur dans un bloc de faits structurés dans l’état (région définie sur Northwest) que l’agent considère comme fiable, afin de ne pas avoir à discuter à nouveau du texte chaotique à chaque tour.
- Modifier l’historique. LangGraph permet de tronquer ou de réécrire les messages avant qu’ils ne reviennent dans le contexte, ce qui permet de supprimer simplement les instructions obsolètes.
La leçon générale est que l’historique d’une conversation est un journal et non une base de connaissances. Ne faites pas en sorte que l’agent doive déduire la vérité à partir d’un texte à chaque tour.
Détecteur. Ajoutez un cas de autocorrection à vos évaluations : l’utilisateur dit X, puis le corrige en Y, avant de poser cinq autres questions. Si l’agent revient à X, vous avez ce bug.
Cas 4 : RAG a renvoyé une seule réponse cinq fois et a caché la vraie
Symptôme. Les utilisateurs posaient des questions auxquelles la documentation répondait clairement, mais l’agent fournissait une réponse vague et incomplète. Les journaux de récupération indiquaient six extraits obtenus, ce qui laissait penser que le système de récupération fonctionnait correctement.
Cause racine. Le corpus contenait des quasi-dupliques : un paragraphe de politique avait été copié dans cinq documents différents, comme le font souvent les entreprises réelles. La recherche de similarité a fonctionné parfaitement et a renvoyé cinq extraits presque identiques, ce qui a évincé l’unique extrait différent contenant la vraie réponse. Cinq places à la table ont été attribuées au même « invité ».
Réparation. Éliminer les doublons au moment de la récupération. Même une méthode rudimentaire fonctionne bien : obtenir 20 candidats, supprimer tout élément trop similaire à un déjà choisi et conserver l’ensemble le plus diversifié. Si votre stockage de vecteurs prend en charge la rélevance marginale maximale, c’est exactement cette même idée intégrée. La qualité des réponses aux questions de politique s’améliore immédiatement sans modification du modèle ni de la prompt.
Détecteur. Enregistrer les éléments récupérés et les examiner chaque semaine. Si une phrase apparaît régulièrement deux fois dans un lot, vous payez pour de la redondance au détriment de la pertinence.
Cas 5 : le résumé qui a ignoré la règle importante
Symptôme. Afin de maintenir un coût abordable pour les sessions longues, une équipe a introduit une synthèse itérative : tous les N tours, les messages anciens sont compressés. C’est la méthode standard et fortement recommandée. Par la suite, un agent a commencé à intégrer des versions préliminaires dans les rapports officiels, bien que l’utilisateur ait demandé « exclure les versions préliminaires » onze tours auparavant.
Cause racine. Le système de synthèse s’est comporté comme tel. Il a conservé l’essentiel du récit (l’utilisateur voulait des rapports régionaux et avait parlé de la mise en page) tout en éliminant ce qu’il considérait comme secondaire. Or, « exclure les versions préliminaires » n’était pas secondaire : c’était une contrainte, et les contraintes ne peuvent être reformulées ; elles soit restent intactes, soit disparaissent. De tous les sept échecs, celui-ci mérite le plus d’inquiétude, car la synthèse semblait être une solution évidente lors des démonstrations.
Réparation. Séparer la mémoire en deux parties avant toute compression.
- Le récit peut être résumé de manière agressive.
- Les contraintes, décisions et préférences sont regroupés dans une liste structurée qui n’est jamais résumée. Seuls de nouveaux éléments y sont ajoutés ou des éléments existants sont explicitement supprimés, et toute la liste est intégrée au contexte à chaque tour.
L’instruction de résumé peut le préciser directement : avant de résumer, copiez mot pour mot toutes les instructions, contraintes et décisions dans la liste des contraintes.
Détection. Pendant le développement, comparez la liste des contraintes avant et après chaque exécution de résumé. Une contrainte qui n’apparaît que dans un paragraphe de résumé signifie que le compresseur altère vos règles.
Cas 6 : un traceback qui a poussé l’agent à douter d’un outil fonctionnel
Symptôme. Un outil a connu un temps d’attente temporaire une fois, puis a fonctionné correctement lors de la requête suivante. Cependant, pendant le reste de la session, l’agent a continué à indiquer aux utilisateurs qu’il était impossible d’accéder au système de rapportage, alors même qu’il y parvenait avec succès. Naturellement, les utilisateurs ont fait confiance à ces affirmations plutôt qu’aux données.
Cause racine. Le trace complet de l’erreur, un TimeoutError après 30 secondes, a été ajouté à l’historique et n’a jamais été supprimé. Pour le modèle, l’affirmation « le système de rapportage est peu fiable » est devenue un fait établi, influençant ainsi toutes les réponses ultérieures. Une erreur temporaire s’est transformée en une contamination permanente.
Réparation. Traitez les erreurs comme des événements et non comme des faits. Après une tentative de réessai réussie, remplacez l’erreur dans l’historique par une note d’une seule ligne telle que "(erreur temporaire, résolue lors du réessai)". Si un outil échoue définitivement, fournissez à l’agent un statut structuré et clair, et non une trace d’exécution. Les traces d’exécution doivent figurer dans les journaux, jamais dans le contexte.
Détection. Recherchez dans les contextes en temps réel les chaînes de caractères Error, Traceback et Exception. Chaque correspondance représente soit un incident en cours, soit un élément résiduel qui pollue les résultats ultérieurs, et tous deux doivent être supprimés.
Cas 7 : vingt-cinq outils dans une seule fenêtre
Symptôme. Au fil de plusieurs mois, l’outil est devenu plus lent et plus cher, ce qui était prévisible avec l’ajout continu de fonctionnalités. Il a également commencé à sélectionner le mauvais outil pour les demandes simples, comme de fournir un export de conformité alors que quelqu’un voulait simplement un résumé du projet. Rien n’était cassé ; tout était simplement devenu plus complexe.
Cause racine. L’ensemble d’outils comptait désormais 25 outils, chacun avec une longue description et un schéma JSON, ce qui représentait environ 12 000 tokens avant même que l’utilisateur n’entre un mot. À chaque interaction, le modèle évaluait 25 options, dont beaucoup avaient des descriptions similaires telles que « obtenir les données du projet », « récupérer les détails du projet » et « chercher des informations sur le projet ». Choisir un outil est une tâche de raisonnement, mais l’équipe en avait fait un examen à choix multiples avec 25 réponses similaires.
Réparation. En deux parties.
- Éliminez de manière agressive. Fusionnez les outils qui se chevauchent, réduisant ainsi le nombre de 25 à 11, et réécrivez chaque description en répondant à une seule question : quand le modèle doit-il choisir cet outil plutôt que ses voisins ?
- Chargez les outils en fonction du contexte. Chaque nœud n’a pas besoin de tous les outils. Dans LangGraph, il est facile de lier uniquement l’ensemble pertinent à chaque nœud ; ainsi, un nœud de génération de rapports peut voir quatre outils au lieu de 25.
Le coût en tokens par tour a diminué d’environ un tiers, et les choix d’outils erronés ont presque disparu.
Détection. Comptez uniquement les tokens présents dans vos définitions d’outils. S’ils dépassent la longueur du prompt système ainsi que celle d’une conversation moyenne, votre agent passe un examen avec un livre ouvert dont la plupart des pages sont incorrectes.
Ce que les sept ont en commun
Lorsque l’on relit ces sept points, un schéma unique apparaît : aucun composant n’était endommagé et rien n’était manquant. La pollution résidait systématiquement dans des contenus en excès, obsolètes ou répétitifs.
C’est pourquoi le débogage classique échoue dans ce cas. Les ingénieurs sont formés à rechercher des absences : la vérification null manquante, l’exception non gérée. La pollution contextuelle exige plutôt une compétence inverse : apprendre à chercher ce qui ne devrait pas s’y trouver.
Si vous devez adopter une seule habitude, faites-en celle de lire le contexte brut de votre agent. Évitez les journaux d’activité et le tableau de bord des traces, et lisez plutôt le texte réel que le modèle reçoit lors d’une exécution en production. C’est l’heure la plus précieuse pour quiconque développe des agents, et presque personne ne l’utilise.
Liste de contrôle en soixante secondes avant déploiement
- Y a-t-il une valeur d’état qui correspond à une date, un statut ou quelque chose appelé « actuel » ? (Cas 1)
Sept questions. Toute réponse du type « probablement, mais on n’est pas sûr » constitue un incident en attente d’une date de résolution.