Accueil / Articles

Le système de fichiers de votre agent est une décision : explication des backends des Deep Agents.

Les backends de LangChain Deep Agents décident quels fichiers sont conservés : cinq intégrés, le routage via CompositeBackend, ainsi que le piège lié aux noms d’espace dans les stores et sandboxes multi-utilisateurs.

Éditeur : REACTAPP.TOP9 min de lecture2174 mots

Sources: LangChain Deep Agents: Your Agent’s Filesystem Is a Decision. Most People Make It by Accident. IV

Le bug le plus redoutable n’entraîne pas de panne. Il oublie discrètement ce qui s’est passé hier, expose la mémoire d’autrui ou écrit des connaissances précieuses dans un espace temporaire que vous n’aviez pas l’intention de perdre. Dans Deep Agents de LangChain, c’est le backend qui décide de ces résultats, et l’objectif est de cesser de traiter le système de fichiers comme s’il était magique, afin de déterminer intentionnellement ce qui survit, ce qui disparaît et qui peut y accéder.

Chaque agent Deep Agents dispose d’un système de fichiers, mais l’endroit où ce système de fichiers est stocké dépend de vous : en mémoire, sur disque, dans un stockage durable qui s’étend sur plusieurs threads, ou une combinaison de ces options. Un choix erroné fait que l’agent oublie silencieusement tout ce qui s’est passé entre les conversations.

Le bug à l’origine des notes disparues

Disons que vous créez un agent avec les outils de système de fichiers activés et que vous le voyez enregistrer des notes dans /memories/findings.md au milieu d’une conversation. Il les lit à nouveau plus tard dans la même conversation. Tout semble normal, alors vous le mettez en production. Le lendemain, un utilisateur revient, mentionne quelque chose que l’agent aurait dû connaître la veille, mais l’agent n’en a aucune idée. Les notes ont disparu. Aucune erreur, aucune ligne dans les journaux. L’agent a simplement recommencé à zéro sans le signaler.

Personne ne crée délibérément ce genre de bug. Il provient d’une configuration par défaut : les fichiers de l’agent sont stockés dans un espace temporaire lié à chaque thread, qui disparaît avec la fin de la conversation. En omettant de décider où les fichiers doivent être stockés, le framework le fait à votre place. C’est acceptable pour un espace de travail temporaire, mais c’est le mauvais choix pour la mémoire.

Les permissions, la mémoire, les environnements isolés et le déploiement reposent tous sur ce seul choix. Avec un backend adapté, tout s’organise naturellement, tandis qu’avec un mauvais choix, on passe des après-midis entiers à chercher des informations disparues.

Le système de fichiers est virtuel et c’est le backend qui le définit

L’agent voit toujours un système de fichiers ordinaire avec des chemins ordinaires. C’est le backend qui détermine de quoi est composé ce système de fichiers, et il est possible de changer de backend sans modifier le comportement de l’agent.

Six outils de gestion de fichiers sont fournis : ls, read_file, write_file, edit_file, glob et grep. Pour le modèle, ils agissent sur un système de fichiers classique. Il écrit /workspace/plan.md, le lit par la suite et effectue des recherches dans /repo comme s’il disposait d’un disque, sans que rien de tout cela ne change en fonction du backend utilisé.

Le backend est l’implémentation se trouvant derrière cette abstraction. Il répond à des questions auxquelles l’agent ne se pose jamais. Si le modèle écrit /workspace/plan.md, où vont les octets ? Dans la mémoire Python qui disparaît lorsque le processus s’arrête ? Sur un disque réel ? Dans une base de données qui persiste à travers toutes les conversations que l’utilisateur aura jamais ? Chaque réponse correspond à un backend différent.

Le choix s’effectue en passant un backend à create_deep_agent(model=..., backend=...). En ne lui passant rien, la valeur par défaut est utilisée, ce qui explique la disparition des notes mentionnée précédemment.

Un conseil pour que votre code soit compatible avec les versions actuelles : fournissez à l’agent une instance prête, comme StateBackend(). Les exemples anciens utilisaient un enveloppe lambda rt:, qui est désormais obsolète et inutile.

Cinq backends, classés selon ce qui persiste

La manière simple de distinguer les cinq solutions intégrées réside dans leur durée de vie et non dans leurs mécanismes internes : pendant combien de temps un fichier écrit via l’une d’elles reste-t-il ?

StateBackend est la valeur par défaut et constitue un espace temporaire lié à un thread. Les fichiers sont stockés dans l’état de l’agent et persistent au cours des différentes étapes d’une conversation (un thread), mais ne sont pas partagés entre les threads. Il s’agit de la « note de bord » de l’agent, idéale pour des travaux intermédiaires qui sont relus deux étapes plus tard avant d’être supprimés à la fin de la conversation. C’est bien la valeur par défaut appropriée, mais c’est aussi précisément ce backend qui provoque la surprise des « notes disparues » lorsqu’on le confond avec la mémoire.

StoreBackend représente un stockage durable entre les threads. Les fichiers sont stockés dans une base LangGraph et survivent aux différentes conversations, ce qui correspond aux besoins d’une mémoire à long terme. Le problème majeur, cependant, est qu’il faut indiquer explicitement à quelles données il doit accéder. Cela sera abordé plus loin.

FilesystemBackend et LocalShellBackend utilisent le disque réel de l’ordinateur hôte. Ils lisent et écrivent des fichiers réels dans un répertoire que vous choisissez, et LocalShellBackend fournit en outre à l’agent un outil execute qui exécute des commandes de shell directement sur votre machine, sans aucun environnement isolé. Ils conviennent à un assistant de codage local que vous développez sur votre bureau, mais sont dangereux partout ailleurs ; c’est pourquoi la documentation met en garde fermement contre leur utilisation dans des agents déployés. Un accès brut à l’hôte combiné à des entrées utilisateur dynamiques constitue une menace de sécurité imminente.

ContextHubBackend est l’option native et fiable de LangSmith. Les fichiers sont stockés dans un répertoire LangSmith Hub et sauvegardés sous forme de commits, ce qui assure la persistance entre les exécutions ainsi qu’un historique de chaque modification du système de fichiers, sans nécessiter la mise en place d’un stockage propre. C’est une solution idéale lorsque vous faites déjà partie de l’écosystème LangSmith et souhaitez conserver les données sans devoir configurer une base de données.

En les examinant ensemble, ces options s’inscrivent sur un seul axe : rien n’est conservé, la conversation est conservée, tout est conservé, ou tout est conservé avec son historique. Choisissez ce dont a besoin votre fonctionnalité.

CompositeBackend : chemins différents, durées de vie différentes

L’idée qui fait fonctionner les systèmes de stockage en arrière-plan est qu’on ne souhaite généralement pas une seule politique de persistance pour tout le système de fichiers. Les fichiers en cours de travail doivent être peu coûteux, rapides et jetables, tandis que la mémoire doit être fiable. Ce sont là deux types de systèmes de stockage, et CompositeBackend vous permet d’avoir les deux au sein d’un même système de fichiers en utilisant des préfixes de chemin pour diriger les requêtes.

Attribuez-lui un système de stockage par défaut pour la plupart des chemins, ainsi qu’une table de routage qui redirige les préfixes spécifiques. La configuration habituelle consiste à utiliser un espace temporaire pour tout, avec un stockage fiable derrière /memories/ :

from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
from langgraph.store.memory import InMemoryStore

agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-6",
    backend=CompositeBackend(  # ①
        default=StateBackend(),  # ②
        routes={  # ③
            "/memories/": StoreBackend(namespace=lambda _rt: ("memories",)),  # ④
        },
    ),
    store=InMemoryStore(),  # passed to create_deep_agent, not to the backend  ⑤
)

En parcourant les éléments numérotés de cette liste :

  1. CompositeBackend présente un système de fichiers à l’agent et redirige chaque opération vers le sous-backend dont le chemin correspond.
  2. default prend ce qui n’est pas attribué à un autre chemin. Dans cet exemple, il s’agit du StateBackend géré au niveau du thread.
  3. routes associe des préfixes de chemin à des backends. Si deux préfixes correspondent, le plus long l’emporte, donc /memories/projects/ prend le pas sur /memories/.
  4. Les écritures sous /memories/ sont envoyées à un StoreBackend persistant, dont la fonction namespace renvoie la tuple identifiant le propriétaire et définissant le contexte des données.
  5. Le store de LangGraph est transmis à create_deep_agent, et non au backend ; c’est un détail de connexion que les gens manquent la première fois.

Le résultat est que tout ce qui est écrit sous /memories/ est stocké de manière permanente et survit entre les conversations, tandis que tout le reste, qu’il s’agisse d’un plan dans /workspace, d’un fichier temporaire ou d’une lecture en mémoire cache, est envoyé au StateBackend par défaut et disparaît lorsque la thread se termine. L’agent ne le sait ni ne s’en soucie ; il se contente d’écrire des chemins.

Une stratégie de système de fichiers pour un véritable agent

Prenons un agent de maintenance de code, par exemple celui nommé buggy-shop. Lorsqu’il travaille sur un dépôt, les fichiers de ce dernier représentent l’état en cours de travail. Ils sont chargés, modifiés et testés, et il n’y a aucune raison pour qu’ils s’infiltrent dans la conversation suivante. Ce que l’agent apprend au cours de son travail est une autre affaire, et c’est ce qu’il devrait se rappeler pour la prochaine demande : des tests instables dans un module, un fichier de configuration partagé par plusieurs suites de tests, ou encore une erreur de pagination de type « off-by-one » déjà détectée précédemment.

Ainsi, buggy-shop utilise l’organisation composite décrite ci-dessus. Le répertoire est placé sous le StateBackend par défaut en tant qu’espace temporaire au niveau des threads, tandis que les notes accumulées se trouvent sous /memories/ dans un StoreBackend persistant. Il s’agit donc d’un agent disposant d’une réponse délibérée quant à ce qui doit survivre à la conversation. Séparer l’état de travail temporaire de les connaissances durables est la bonne approche, et le routage par préfixe de chemin est le moyen d’exprimer cela.

Où s’exécute le code par rapport à l’emplacement des fichiers

Jusqu’à présent, tout porte sur les fichiers, et de nombreux agents n’en ont jamais besoin davantage. Mais un agent dont la tâche est de prouver une correction en exécutant pytest a besoin d’un shell, car l’exécution de commandes n’est pas une opération de système de fichiers. Un backend en environnement isolé ajoute cela : en plus du système de fichiers, il expose l’outil execute pour exécuter des commandes, tout cela à l’intérieur d’un conteneur isolé afin que l’agent ne puisse pas endommager l’hôte.

Les agents peuvent être connectés à un environnement de test de deux manières. Dans le premier cas, l’agent est placé à l’intérieur de l’environnement de test, où il travaille sur son système de fichiers. Cela permet de rapprocher le développement local de la production, mais oblige à placer les clés API dans cet environnement. Dans le second cas, l’environnement de test est considéré comme un outil : l’agent reste sur votre serveur et appelle simplement l’environnement de test pour exécuter du code. Les identifiants restent à l’extérieur, vous pouvez jeter l’environnement de test, et en cas de panne, l’état de l’agent reste sécurisé. Deep Agents gère les deux cas, ce qui constitue une différence marquée par rapport aux outils qui ne permettent que la première méthode.

N’oubliez pas que l’exécution de code et le stockage de fichiers sont deux problématiques distinctes. Un backend basé sur un système de fichiers répond à l’une d’elles, un backend d’environnement de test à l’autre, et de nombreux agents sérieux ont besoin des deux.

La configuration indispensable

Dès que StoreBackend sert plus d’un utilisateur, une factory de nom de domaine devient nécessaire. En l’absence de celle-ci, il n’y aura pas de crash, mais plutôt une fuite mémoire. Le backend doit savoir à qui appartiennent les fichiers qu’il gère, et il l’apprend grâce à un nom de domaine : une petite fonction qui prend le contexte d’exécution et renvoie une tuple indiquant le propriétaire. Le scoping par utilisateur permet à chaque personne d’avoir un espace de stockage distinct :

from deepagents.backends import StoreBackend
backend = StoreBackend(
    namespace=lambda rt: (rt.server_info.user.identity,),
)

Omettre l’espace de noms ainsi que les scopes par défaut hérités permet à l’assistant de faire en sorte que tous les utilisateurs du même agent partagent un même espace de stockage et puissent lire la mémoire censée être privée des uns et des autres.

Ce bug passe tous les tests sur votre propre machine, puisque vous êtes le seul utilisateur. Il se manifeste comme une violation de la vie privée dès que une deuxième personne se connecte. Considérez l’utilisation de la factory d’espace de noms comme obligatoire pour tout projet dépassant le cadre d’un outil local utilisé par une seule personne. La documentation indique qu’elle deviendra obligatoire, alors écrivez-la dès maintenant afin d’éviter une migration ultérieure.

Un mécanisme de capture plus petit est également fourni avec le backend par défaut. StateBackend enregistre l’ensemble de l’état de l’agent à chaque étape, ce qui permet de reprendre une conversation de manière propre. Le prix à payer est que les gros fichiers stockés dans cet état sont sérialisés encore et encore, étape après étape, ce qui entraîne discrètement une baisse des performances et de l’espace de stockage. La solution réside dans le même schéma composite : envoyer les éléments volumineux ou durables dans un stockage et conserver StateBackend uniquement pour des données temporaires et de petite taille.

Une liste de vérification pour aujourd’hui

  • Découvrez quel backend utilise votre agent. Sans l’argument backend= dans create_deep_agent, vous utilisez StateBackend et l’agent oublie tout entre les conversations. Vérifiez que c’est bien ce que vous souhaitez.
  • Décidez ce qui doit survivre à une conversation. Listez séparément les fichiers en cours de traitement et les connaissances durables. Tout ce qui se trouve dans la colonne des connaissances durables doit être stocké dans un StoreBackend.
  • Utilisez le pattern composite. Créez un CompositeBackend qui utilise par défaut StateBackend et redirige /memories/ vers un StoreBackend. Le store lui-même est envoyé à create_deep_agent, jamais au backend.
  • Donnez à chaque StoreBackend multi-utilisateurs une factory de nom d’espace. Associez-la à l’identité de l’utilisateur, et faites-le avant que la deuxième personne ne se connecte.
  • Auditez les gros fichiers dans l’état. Si l’agent écrit de gros artefacts, redirigez-les hors de StateBackend afin qu’ils ne soient pas réserialisés à chaque étape.

Le backend devient une décision

Considérez le backend comme un choix et vous éviterez dès le début le problème de consommation excessive en fin d’après-midi. Vous pouvez décrire les cinq composants intégrés en fonction de leur durée de vie, envoyer des chemins différents selon ces durées de vie à l’aide de CompositeBackend, et vous savez que le stockage multi-utilisateurs ne peut pas se passer d’une usine de noms de domaine. Un agent conçu de cette manière dispose d’un plan pour ses fichiers : un espace temporaire pour le répertoire de travail, et une mémoire persistante pour ce qu’il stocke.

Un stockage durable dans lequel l’agent écrit des données devrait vous inciter à la prudence, et c’est là une bonne chose. Un agent capable d’écrire des fichiers peut en écrire de faux, supprimer le bon ou exécuter une commande que vous n’aviez jamais demandée. Un système de fichiers, c’est comme lui donner des mains. Ce qui vient ensuite, c’est la laisse, c’est-à-dire des permissions déclaratives qui restreignent les fichiers qu’il peut modifier, ainsi qu’une approbation humaine qui le force à s’arrêter et à demander l’autorisation avant d’agir. La laisse n’est utile que si vous avez déjà déterminé, intentionnellement, l’endroit où se trouvent les fichiers.