Pourquoi les agents à long terme ont besoin d’un harness : comment les agents avancés gèrent l’état et le contexte.
Pourquoi les agents à long horizon ont besoin d’un harnais : comment LangChain Deep Agents lutte contre la dégradation du contexte grâce à quatre piliers, un middleware, des backends de système de fichiers et des sous-agents isolés.
Sources: The Deep Agents Architecture, Part 1: Understanding the Agent Harness
La conception la plus basique d’un agent repose sur une boucle d’appels d’outils. Le modèle lit la demande, choisit un outil, le exécute, lit le résultat, choisit à nouveau et continue ainsi jusqu’à ce qu’il cesse d’appeler des outils. LangChain est clair quant aux limites de cette conception : de tels agents sont « superficiels », incapables de planifier et d’agir sur des tâches longues et complexes. Ce n’est pas un défaut propre à un seul framework ; c’est plutôt le résultat du fait de ne laisser aucun intermédiaire entre le modèle et un contexte qui ne cesse de s’agrandir.
Le meilleur indicateur que le plafond est structurel provient de trois équipes qui n’ont jamais collaboré. Anthropic l’a découvert en développant Claude Code, OpenAI l’a constaté avec Deep Research, et Butterfly Effect l’a rencontré à tel point que Manus a dû être reconstruit quatre fois. Chacune a abouti aux mêmes quatre composants : un outil de planification, la délégation à des sous-agents, l’accès au système de fichiers et une instruction détaillée. Des efforts indépendants menés par des entreprises distinctes pour aboutir à une même architecture suggèrent que le problème en était la cause, et non les préférences personnelles de quiconque.
La dégradation du contexte est le véritable mécanisme
Il est tentant de dire que les exécutions prolongées des agents échouent parce que la fenêtre de contexte se remplit. Ce n’est pas le mécanisme réel.
Un modèle engagé dans une longue conversation prend de pires décisions que le même modèle lors d’une conversation courte, même si la majeure partie de sa fenêtre de traitement reste disponible. L’attention réservée à un token donné diminue à mesure que le nombre de tokens environnants augmente, et ce rapport signal/bruit qui se détériore affecte déjà fortement la qualité de la sortie bien avant que la fenêtre ne soit pleine. Cet effet est documenté, et non basé sur des anecdotes : les modèles ont en effet tendance à récupérer moins bien les informations situées au milieu d’un contexte long, préférant les tokens proches du début et de la fin.
Cela change la nature de la solution à trouver. Si le problème venait de la capacité insuffisante, une fenêtre plus grande suffirait à y remédier. En réalité, le problème réside dans une dilution de l’attention : le modèle reste capable, mais les informations dont il a besoin sont noyées sous tout ce qui a été traité précédemment. Il s’agit donc d’un problème d’organisation des données et non d’un problème de taille, et un espace supplémentaire dans la fenêtre ne résout rien à cet égard.
Le fossé que ni LangChain ni LangGraph n’ont été conçus pour combler
LangGraph se concentre sur une exécution fiable : points de contrôle, flux en continu, révision humaine et persistance entre threads. Son véritable équivalent est Temporal, et non d’autres frameworks d’agents. Il garantit que l’agent survit à une panne, mais ne dit rien sur le fait que l’agent pense encore clairement après quatre heures. LangChain se situe à un niveau supérieur et fournit des abstractions : le cycle d’appel d’outils, du contenu structuré ainsi que des middleware de personnalisation. Néanmoins, le cycle par défaut construit à partir de ces abstractions est celui décrit précédemment, de nature superficielle. La fiabilité et l’abstraction sont réelles et nécessaires, mais aucune ne permet de savoir si le contexte de travail du modèle reste propre.
Cela peut être vérifié en examinant la manière dont les composants sont connectés. Les dépendances déclarées de Deep Agents sont langchain, langchain-core, langchain-anthropic, langchain-google-genai et langsmith. LangGraph n’apparaît pas sur cette liste ; il y figure uniquement parce que LangChain en dépend. La structure est strictement imbriquée : Deep Agents dépend de LangChain, qui dépend de LangGraph, et il ne s’agit pas de trois éléments indépendants assemblés ensemble. Aucun niveau, ni au niveau du temps d’exécution ni au niveau du framework, ne gère jamais l’hygiène du contexte, car la couche censée s’en occuper se trouve au-dessus des deux autres.
La thèse : un problème d’état, pas un problème de raisonnement
Les tâches complexes relèvent, pour une catégorie spécifique et bien définie de complexité, d’un problème de gestion d’état et non d’un problème de raisonnement.
Il existe une manière simple de déterminer le type de problème qu’implique une tâche. Imaginez un collègue compétent qui perd toute sa mémoire tous les dix minutes. Si ce collègue échoue dans sa tâche, celle-ci concernait la gestion d’états, et des notes, ainsi que la délégation de tâches et une organisation adaptée à cette perte de mémoire, permettront de résoudre le problème. Si une personne qui gardait tout le problème en tête échoue néanmoins, des notes ne serviront à rien, car la difficulté résidait depuis le début dans le raisonnement lui-même.
Chaque mécanisme ajouté par un harnais, qu’il s’agisse d’un système de fichiers pour le transfert de charges, de sous-agents pour l’isolation ou d’un outil de planification pour suivre les progrès, traite le cas de l’amnésie. Aucun d’eux ne rend une étape de raisonnement plus intelligente. La gestion de l’état n’est donc pas une explication concurrente du raisonnement. C’est plutôt la base qui permet au raisonnement de s’accumuler au fil de plusieurs étapes sans être réinitialisé à chaque fois. Un modèle qui raisonne correctement à chaque étape peut encore échouer dans une tâche de deux cents étapes si l’étape 150 ne peut pas connaître ce qui a été décidé à l’étape 12. Le raisonnement n’était pas erroné ; il n’a simplement jamais disposé de continuité pour fonctionner.
Cette base ne suffit pas à elle seule, et prétendre le contraire affaiblirait le reste de l’argumentation. Persister avec une stratégie inefficace alors que l’histoire complète et correcte est clairement visible constitue une véritable faille de raisonnement, et une gestion impeccable de l’état ne peut y remédier. De même, un modèle dont la propre reconstitution des événements s’éloigne de ce qui s’est réellement passé présente un problème de raisonnement, même lorsque le compte rendu exact est intact et accessible. La version juste de cette affirmation est la suivante : pour les échecs que vise un système de gestion, c’est-à-dire les tâches qui se détériorent parce qu’elles survivent à un contexte initial propre, la gestion de l’état constitue le levier principal, tandis que la qualité du raisonnement représente le reste plus difficile que les structures de soutien ne peuvent atteindre. Les sections suivantes décrivent ce que trois équipes ont développé pour actionner ce levier.
Quatre piliers avec de nombreuses composantes
Qu’est-ce que Deep Agents
Deep Agents est distribué en tant que package Python open source sous licence MIT, installable via pip, et est actuellement à la version 0.7.5. On trouve autour de lui une version en TypeScript, une interface CLI en terminal nommée deepagents-code, ainsi qu’une édition hébergée et gérée encore en bêta privée. Il n’est pas un concurrent de LangChain et LangGraph. Comme indiqué précédemment, il dépend directement de LangChain et ne prend en charge LangGraph que par ce biais. L’appel à create_deep_agent() redirige vers create_agent() de LangChain, ce qui génère un graphe LangGraph ; par conséquent, toute durabilité offerte à l’agent provient de ce que LangChain lui transmet.
Les quatre piliers et l’ensemble technologique qui les sous-tendent
Selon LangChain, quatre éléments permettent à un agent d’être « profond » : un outil de planification, la délégation à des sous-agents, l’accès au système de fichiers et une instruction détaillée. Harrison Chase a proposé ce cadre après avoir remarqué une structure similaire dans Claude Code, Deep Research et Manus. En tant que modèle mental, il fonctionne bien, car ces quatre catégories marquent réellement la différence entre un cycle superficiel et un agent capable d’accomplir une tâche complexe.
La description initiale était provisoire, et non le plan final. La fonction create_deep_agent() assemble chaque capacité à partir d’un ensemble toujours plus vaste de middleware qui peuvent être combinés librement. Cet ensemble comprend des fichiers, des sous-agents, des outils de résumé, des correctifs pour les appels d’outils, diverses variantes de mise en cache des prompts liées à des fournisseurs spécifiques, de la mémoire, des compétences et l’approbation humaine, ainsi que tout niveau personnalisé écrit par un développeur. Les piliers définissent ce que l’agent peut faire. La pile de middleware est le moyen par lequel ces capacités sont réellement générées, et elle évolue constamment.
Seules deux parties de la pile sont structuralement obligatoires, et cela compte plus que tout autre critère de quantité. FilesystemMiddleware est nécessaire car il applique les règles de permission qui contrôlent l’accès aux fichiers, ce que commente une note source comme une garantie de sécurité. SubAgentMiddleware est requis car c’est ce que l’outil task utilise. En omettre l’une ou l’autre provoque une erreur et n’entraîne pas de fonctionnement silencieusement dégradé. Tout le reste est optionnel et n’est assemblé que sur demande.
Tout ceux qui travaillent dessus actuellement doivent savoir que deux des quatre catégories initiales sont désormais optionnelles. À partir de la version v0.7.0, l’outil de planification (write_todos) ne fait plus partie de l’ensemble par défaut, et l’invite système de base est fournie vide, conformément à l’avis de suppression. Chacun d’eux nécessite une seule ligne de configuration pour être rétabli. Une simple appel à create_deep_agent() permet d’obtenir directement l’accès aux fichiers et la délégation aux sous-agents, tandis que la planification et une invite détaillée ne sont fournies qu’à la demande.
Les profils d’exploitation, la couche de réglage absente des modèles plus récents
En dessous du middleware se trouve un mécanisme de réglage distinct appelé profil de harnais. Un profil ajoute du texte d’invite système supplémentaire et supprime certains outils, selon la chaîne de modèle précise transmise à l’agent. Ne le confondez pas avec ProviderProfile, qui définit la manière dont l’objet modèle est construit. Un HarnessProfile influence le comportement après construction.
La recherche nécessite la clé provider:model enregistrée avec précision, un enregistrement global servant de solution de repli en l’absence de celle-ci. Par défaut, des profils existent pour la famille Codex d’OpenAI ainsi que pour trois générations de Claude : Haiku 4.5, Sonnet 4.6 et Opus 4.7. Le profil Sonnet 4.6 va au-delà des simples conseils généraux de formulation des demandes. Il suit de si près les directives publiées par Anthropic que les commentaires dans le code le confirment, et il omet délibérément les avertissements orientés vers Opus concernant l’empressement et l’utilisation intensive de sous-agents, jugés irrelevants pour Sonnet.
Un modèle publié une génération après les modèles enregistrés ne reçoit rien. Lorsqu’on vérifie avec le résolveur, claude-sonnet-5, anthropic:claude-sonnet-5 et anthropic ne renvoient chacun aucun profil. En l’absence de correspondance exacte, et sans entrée Anthropic à l’échelle du fournisseur à laquelle se référer (seuls les profils liés à des versions spécifiques existent), rien ne correspond. Cette absence de résultat reste complètement silencieuse. Selon la documentation même du résolveur, la seule ligne de journal est une note de débogage émise lorsque l’on utilise un fallback au niveau du fournisseur. Si les deux recherches échouent, aucune journalisation n’est effectuée.
Un agent construit sur le modèle le plus récent d’une famille en constante évolution reçoit donc exactement les mêmes paramètres d’ajustement qu’un agent basé sur un modèle que le framework n’a jamais connu, c’est-à-dire aucun, sans aucun signe indiquant que c’est bien ce qui s’est produit.
Comment cela fonctionne
Une abstraction unique sous tout
Tout ce middleware repose sur un mécanisme unique appartenant à LangChain et non à Deep Agents. LangChain 1.0 a ajouté des points où le code peut s’exécuter au démarrage ou à la fin d’une exécution, avant et après chaque appel de modèle, ainsi qu’autour de chaque appel d’outil, ce qui permet à un seul agent bouclé de supporter des architectures très différentes. Les hooks avant s’exécutent dans l’ordre où ils ont été enregistrés, tandis que les hooks après s’exécutent dans l’ordre inverse, ce qui reflète la manière dont les frameworks web imbriquent leur middleware. Un hook d’enveloppement peut réessayer une appel, le servir depuis un cache ou l’intercepter complètement. Les configurations de superviseur, les essaims, les boucles de réflexion et les agents profonds se révèlent tous être une seule boucle avec des hooks différents attachés, et create_deep_agent() n’est qu’un choix spécifique parmi eux.
Fichiers : deux backends, deux garanties
Les fichiers peuvent se trouver en deux endroits, et le choix du backend actif détermine ce que signifie réellement l’écriture par un agent.
StateBackend, qui est la valeur par défaut, stocke les fichiers dans un canal d’état de LangGraph : des données de graphe avec leurs propres règles de fusion, et non sur le disque. Il ne fonctionne que pendant l’exécution d’un graphe en temps réel. Le fait que le contenu survive à une seule invocation dépend de la présence à la fois d’un point de contrôle et d’un ID de thread stable. Sans eux, chaque appel commence à zéro, ce qui explique pourquoi une simple appel àcreate_deep_agent()affiche toujours un répertoire de travail vide.FilesystemBackendeffectue des lectures et des écritures réelles sur le disque, ce qui apporte un stockage fiable mais aussi des risques réels. Son documentation indique clairement qu’il convient au développement local ainsi qu’aux processus CI/CD, et qu’il n’est pas adapté aux systèmes exposés aux utilisateurs, car l’agent peut lire tout ce à quoi il a accès, y compris les informations secrètes, et ses modifications ne peuvent pas être annulées. Avec la valeur par défautvirtual_mode=True, l’accès reste limité au répertoire racine, la navigation est refusée tant pour les chemins bruts que pour ceux résolus, et les vrais chemins absolus sont cachés dans les résultats générés par l’agent afin que l’emplacement du répertoire racine ne soit pas révélé.
Un seul système d’autorisation couvre les deux cas. Une règle associe une opération à un motif de chemin et à l’un de trois modes : autoriser, refuser ou interrompre. La suppression est évaluée avec prudence en fonction des règles d’écriture. Une suppression récursive est refusée si une règle de refus recoupe des éléments situés en dessous de la cible, et non seulement la première règle qui correspond, de sorte qu’une autorisation large accordée précocement ne peut pas autoriser l’effacement de quelque chose que une règle plus restrictive écrite ultérieurement est censée protéger.
Subagents, isolés par conception
On accède aux subagents de la même manière que à n’importe quel outil. Lorsque le modèle appelle task, un agent complètement distinct est lancé, équipé de son propre modèle, d’outils et de middleware. La configuration de son modèle accepte soit une chaîne de caractères provider:model, soit un objet de modèle déjà créé ; ainsi, placer un subagent sur un fournisseur différent de celui du parent ne nécessite qu’une ligne de configuration, sans effort technique supplémentaire.
La mémoire n’est pas transmise automatiquement. Le profil du sous-agent ne dispose pas de champ de mémoire, et la logique de transmission des permissions n’a pas d’équivalent pour la mémoire. Ce contexte isolé et propre est intentionnel, ce qui signifie que tout ce qui est enregistré via la mémoire du parent reste invisible pour un sous-agent à moins qu’il ne dispose de son propre middleware de mémoire.
La mémoire est révisable, pas autoritaire
Un fichier de mémoire, une fois chargé, n’entre pas directement dans le prompt du système. Il arrive enveloppé dans un bloc clairement identifié, avec des instructions ajoutées par le framework indiquant au modèle que le contenu est un matériel de référence qui peut être obsolète ou avoir été créé par quelqu’un d’autre, et non une commande à suivre sans question. Cela correspond à un niveau de confiance inférieur à celui du prompt du système, et cela correspond au rôle prévu : quelque chose que l’agent lit et modifie au fur et à mesure de son travail, et non un manuel de règles que le développeur écrit une fois et pour lequel il s’attend à une autorité absolue.
Détail de l’échelle
Etat de la conversation : il repose sur un réducteur personnalisé nommé DeltaChannel, conçu de manière à ce que le stockage des points de contrôle augmente de façon linéaire, O(N), à mesure que la conversation s’allonge, plutôt que de façon quadratique, O(N²).
Comportement aux limites
Deux conditions doivent être prises en compte avant de décider dans quelle mesure s’appuyer sur des sous-agents en environnement de production, les deux étant vérifiées dans le répertoire actuel.
Une seule erreur, de nombreuses conséquences (problème n°694). Lorsque plusieurs sous-agents s’exécutent en parallèle et qu’un d’eux génère une exception, les autres sont annulés en même temps que lui. Le lot réussit ou échoue en tant qu’unité, sans aucune isolation entre ses composants. Le rapport date de janvier 2026 et d’une version précoce ; ce problème a depuis été résolu, bien que la modification de code qui l’a corrigé ne puisse être localisée ni dans le code des sous-agents ni dans la couche d’exécution des outils de LangChain. Néanmoins, planifiez en tenant compte de ce risque : considérez que tout déploiement de tâches indépendantes peut échouer partiellement, jusqu’à ce que l’on confirme le contraire sur la version réellement déployée.
Erreurs affichées au mauvais utilisateur (#947). Après qu’un outil ait généré une erreur, le modèle devrait normalement réessayer ou s’adapter. Cependant, lors de certaines exécutions observées, il s’adressait directement à l’utilisateur final avec un texte destiné à une correction interne, ce qui faisait que cette personne voyait un message d’erreur qui ne lui était pas destiné. Le problème était toujours non résolu au moment de la vérification. Un rapport distinct indique qu’après une mise à jour ultérieure du framework, certaines exceptions d’outil arrêtent complètement l’agent au lieu de transmettre le message au modèle pour une tentative de correction, contrairement aux versions précédentes qui continuaient leur fonctionnement malgré ces exceptions.
Deux rapports plus anciens dans le même domaine ont été clôturés : les sous-agents qui ignorent la limite de récursivité du parent (#1698) et les interruptions dans la reprise des tâches lors des décisions d’édition ou de rejet (#554). La correction relative à la limite de récursivité a également été vérifiée à l’aide de la version source installée. Ils montrent à quel point les problèmes dans ce domaine ont tendance à être résolus rapidement, ce qui ne justifie donc pas de modifier la conception.
Alternatives pratiques
Il existe aujourd’hui trois options pratiques pour créer un agent à long terme : Deep Agents, le Claude Agent SDK et le OpenAI Agents SDK. Chacune reflète un choix différent quant à ce que l’équipe contrôle et ce qu’elle met en œuvre, et aucune n’est une version incomplète des autres.
Une distinction est plus souvent confondue que toute autre : celle entre un transfert de responsabilités et un sous-agent. Deux des trois options reposent sur le même modèle de base, atteint indépendamment par deux équipes. La troisième correspond à un mécanisme véritablement différent.
Le choix parmi elles se résume à deux questions plutôt qu’à une hiérarchie, et aucune n’a de réponse universelle. Chacune ne peut être résolue qu’en fonction des contraintes réelles d’un système spécifique.
À retenir
Les agents à long horizon ont besoin d’un cadre de soutien, car le contexte accumulé affaiblit le signal bien avant que la fenêtre de temps ne soit remplie. Les agents profonds construisent ce cadre selon une structure à quatre piliers, soutenus par une pile dynamique de middleware, comprenant un système de fichiers, des sous-agents et de la mémoire, chacun offrant des garanties et présentant des lacunes spécifiques. Connaître ces lacunes — telles que les omissions dans les profils pour les modèles plus récents, la mémoire invisible pour les sous-agents, les échecs parallèles totaux ou partiels, ainsi que les erreurs des outils qui se transmettent aux utilisateurs — est ce qui distingue l’utilisation intentionnelle du framework de la simple confiance en ses paramètres par défaut.
Lectures complémentaires
- Construire un harness DeepAgents : permissions, sous-agents et approbation humaine — Construction pratique d’un outil de planification de conception de système avec DeepAgents : backends de fichiers, règles de permissions, sous-agents, mécanismes d’approbation, compétences et MCP, ainsi que les cas où le jugement du modèle échoue encore.
- Flux de travail multi-agents LangGraph : comparaison entre essaim et superviseur — Division d’un flux de travail LangGraph en sous-agents avec sauvegardes intermédiaires, puis création d’un assistant financier fonctionnant à la fois en tant qu’essaim et en tant que superviseur, afin d’identifier les points de défaillance de chaque approche.