Accueil
» Domaines
»
Comment corriger la perte de mémoire des agents LangChain lors de longues conversations
Comment corriger la perte de mémoire des agents LangChain lors de longues conversations
Si un agent LangChain oublie des détails au cours d'une longue conversation, corrigez l'architecture avant d'augmenter la fenêtre de contexte du modèle. Dans les agents actuels de style LangChain v1, la continuité de la conversation est construite à partir de deux couches distinctes : un checkpointer pour l'état à court terme, limité au thread, et un store pour les informations à long terme qui doivent survivre entre les threads. Les longues conversations nécessitent ensuite une troisième préoccupation : la gestion du contexte, généralement en réduisant ou en résumant les anciens messages avant qu'ils ne submergent le modèle.
Ce guide suit la documentation officielle de LangChain vérifiée le 11 septembre 2026. La documentation actuelle recommande langchain.agents.create_agent pour les nouveaux agents et décrit la persistance LangGraph comme le système de mémoire sous-jacent. Les exemples plus anciens basés sur ConversationChain, ConversationBufferMemory ou initialize_agent peuvent encore apparaître dans le matériel hérité, mais le guide de migration v1 de LangChain a déplacé les chaînes héritées et d'autres fonctionnalités obsolètes vers langchain-classic. Consultez le guide officiel de migration LangChain v1.
Illustration générée par IA : Le symptôme est simple : un fait a été fourni plus tôt, mais une réponse ultérieure ne l'utilise plus. L'illustration est conceptuelle, et non une capture d'interface LangChain.
Ce que signifie réellement la « perte de mémoire » dans LangChain
Avant de modifier le code, séparez trois problèmes qui semblent souvent identiques du point de vue de l'utilisateur.
Symptôme
Cause probable
Couche correcte à corriger
L'agent oublie après le redémarrage du serveur
L'état n'était stocké que dans la mémoire du processus
Checkpointer ou store persistant
L'agent oublie entre deux requêtes dans le même chat
Aucun checkpointer, ou un thread_id différent a été utilisé
Persistance du thread
L'agent se souvient des premiers tours dans le stockage mais cesse de les utiliser dans les très longues discussions
Le contexte du modèle est devenu trop volumineux ou trop bruité
Synthèse, réduction, récupération
L'agent se souvient d'une préférence dans un chat mais pas dans un nouveau chat
Illustration générée par IA : Considérez la mémoire à court terme comme l'état d'un fil de conversation unique. LangChain implémente actuellement cette continuité via un checkpointer plutôt que les classes de mémoire héritées souvent montrées dans les tutoriels plus anciens.
Ce dont vous avez besoin avant de commencer
Vous avez besoin d'une application LangChain/LangGraph actuelle, d'une intégration de modèle et d'un endroit pour persister l'état. Pour une expérience locale, InMemorySaver est suffisant. Pour la production, utilisez un checkpointer basé sur une base de données. La documentation officielle de LangChain montre PostgreSQL via le package séparé langgraph-checkpoint-postgres.
Gardez quatre identifiants clairs :
ID de conversation ou de chat : l'identifiant que votre application expose aux utilisateurs.
thread_id : la clé de persistance LangGraph utilisée pour reprendre l'état d'un thread.
ID utilisateur : l'identité durable utilisée pour nommer les espaces de mémoire à long terme.
Clé de mémoire : la clé pour un élément durable dans un espace de nommage de store.
Ils ne doivent pas automatiquement avoir la même valeur. Un utilisateur peut avoir plusieurs threads, et un thread peut contenir de nombreux faits.
Étape 1 : Reproduire l'échec avec un test à deux requêtes
Commencez par le test le plus petit possible. Demandez à l'agent de se souvenir d'un détail unique, puis appelez-le à nouveau et demandez ce détail. Ne testez pas la mémoire avec un seul appel invoke() car le modèle peut voir tout le contenu de cette seule requête, même si la persistance est cassée.
config = {"configurable": {"thread_id": "debug-thread-001"}}
agent.invoke(
{"messages": [{"role": "user", "content": "Souviens-toi que le nom de code de mon projet est Juniper."}]},
config,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Quel est le nom de code de mon projet ?"}]},
config,
)
Si la deuxième requête oublie « Juniper », inspectez la configuration du checkpointer et le thread_id réel avant de modifier les prompts.
Étape 2 : Ajouter un checkpointer pour la mémoire du même thread
Un checkpointer persiste les instantanés de l'état du graphe de l'agent. LangGraph l'utilise pour la mémoire à court terme, la récupération après interruption, les flux avec intervention humaine et la tolérance aux pannes. Le guide de persistance actuel décrit les checkpointers comme étant limités au thread et indique que l'application accède à l'état en passant un thread_id. Consultez le guide officiel de persistance LangGraph.
InMemorySaver est excellent pour confirmer que votre câblage de thread fonctionne, mais il stocke les checkpoints dans la RAM. LangGraph avertit explicitement que MemorySaver/InMemorySaver ne persistent pas entre les redémarrages du processus.
Étape 3 : Conserver le même thread_id pour la même conversation
Le bug d'application le plus courant consiste à créer un nouveau thread_id à chaque requête HTTP. La base de données peut fonctionner parfaitement tandis que chaque requête démarre un thread LangGraph différent.
Par exemple, supposons que votre front-end ait l'ID de chat chat_8bf4. Mappez cette valeur de manière déterministe vers le thread LangGraph et réutilisez-la pour chaque tour de cette conversation. Un nouveau chat doit recevoir un nouvel ID de thread.
Illustration générée par IA : La persistance ne supprime pas la limite de contexte du modèle. Un thread stable peut contenir plus d'historique que ce que le modèle ne devrait recevoir à chaque appel.
N'utilisez pas un seul thread_id permanent pour tous les chats appartenant au même utilisateur. Cela fusionne des conversations sans rapport dans un seul flux d'état. Si vous utilisez PostgreSQL, les directives actuelles de dépannage de LangGraph indiquent également que thread_id doit rester inférieur à 255 caractères ; un UUID ou un hash déterministe est plus sûr qu'un objet sérialisé énorme.
Étape 4 : Remplacer la persistance en mémoire avant la production
Une fois le test à deux requêtes réussi, testez un redémarrage du processus. Sauvegardez un fait, arrêtez l'application, redémarrez-la, puis demandez le fait avec le même ID de thread. Si vous utilisez toujours InMemorySaver, l'oubli est un comportement attendu.
La documentation officielle sur la mémoire à court terme montre une configuration de production basée sur PostgreSQL utilisant PostgresSaver :
from langchain.agents import create_agent
from langgraph.checkpoint.postgres import PostgresSaver
DB_URI = "postgresql://user:password@db-host/app"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
checkpointer.setup()
agent = create_agent(
model="your-provider:your-model",
tools=[],
checkpointer=checkpointer,
)
Pour la configuration du package actuellement documentée par LangChain, consultez Mémoire à court terme. Ne mettez pas de vrais identifiants de base de données directement dans le code source ; utilisez votre système de gestion des secrets habituel.
Illustration générée par IA : Un élément particulièrement important est le stockage local au processus : un checkpointer en mémoire est intentionnellement perdu après un redémarrage, donc les tests de redémarrage appartiennent à la suite de tests de mémoire.
Étape 5 : Gérer les longs historiques au lieu de tout envoyer pour toujours
Une fenêtre de contexte est la quantité de contexte d'entrée et de sortie qu'un modèle peut gérer dans un seul appel de modèle. La création de checkpoints peut préserver une très longue conversation dans le stockage, mais cela ne signifie pas que chaque message historique doit être renvoyé au modèle pour toujours.
Le guide de mémoire à court terme de LangChain indique que les longs historiques peuvent dépasser la fenêtre de contexte du modèle et que même les modèles capables d'accepter l'historique complet peuvent être distraits par du contenu obsolète ou hors sujet, avec une latence et un coût plus élevés. Les stratégies documentées sont la réduction, la suppression, la synthèse ou l'application d'une politique personnalisée.
Utilisez la synthèse lorsque les détails anciens comptent encore
SummarizationMiddleware est l'option intégrée actuelle pour remplacer l'ancien historique par un résumé compact tout en conservant les messages récents. Son déclencheur peut être basé sur le nombre de tokens, le nombre de messages ou une fraction du contexte du modèle.
Les chiffres ci-dessus sont un exemple de politique, pas des paramètres universels. Choisissez les seuils après avoir mesuré vos propres prompts, sorties d'outils, limites de contexte du modèle, latence et qualité du résumé. Consultez la documentation sur les middlewares intégrés de LangChain pour les options de déclenchement et de conservation actuellement prises en charge.
Illustration générée par IA : Testez le rappel après suffisamment de tours pour activer votre politique de réduction ou de synthèse ; un chat court peut masquer des bugs de long contexte.
Ne réduisez pas les messages d'outils aveuglément
Si vous implémentez une suppression ou une réduction personnalisée, préservez une séquence de messages valide. LangChain avertit que de nombreux fournisseurs exigent qu'un message assistant contenant des appels d'outils soit suivi des messages de résultat d'outils correspondants. Supprimer une moitié de cette paire peut créer des erreurs de fournisseur ou un comportement de modèle confus.
Étape 6 : Déplacer les faits durables dans un store à long terme
Un store est la couche de persistance de LangGraph pour les données définies par l'application en dehors de l'état du graphe d'un thread. La documentation actuelle de LangChain utilise les stores pour les informations qui doivent être disponibles entre les conversations, telles que les préférences utilisateur, les faits ou les connaissances partagées de l'application.
Les éléments du store à long terme sont des documents JSON organisés par un espace de nommage et une clé. Un espace de nommage pratique contient souvent un identifiant d'utilisateur ou d'organisation :
Ceci est différent de sauvegarder la transcription entière. Stockez les informations que votre produit traite intentionnellement comme durables. Si un fait est privé ou réglementé, appliquez vos politiques normales de rétention, d'autorisation, de chiffrement et de suppression plutôt que de supposer que la « mémoire de l'agent » en est exemptée.
Illustration générée par IA : Cette illustration utilise des étiquettes conceptuelles larges plutôt que des noms d'API actuels littéraux. Pour le nouveau code LangChain v1, utilisez la distinction checkpointer/store décrite dans le texte et la documentation officielle.
Utilisez un store basé sur une base de données en production
Le guide officiel de mémoire à long terme montre à la fois InMemoryStore et PostgresStore, et note explicitement que l'implémentation en mémoire doit être remplacée par un store basé sur une base de données pour la production. Il liste également les intégrations de stores au-delà de PostgreSQL. Utilisez le backend qui correspond à vos exigences de déploiement et opérationnelles plutôt que de sélectionner une base de données vectorielle simplement parce que le mot « mémoire » est impliqué.
Ajoutez une recherche sémantique uniquement lorsque vous avez besoin d'un rappel flou
Les stores LangGraph peuvent être configurés avec un index afin que store.search() puisse récupérer des éléments par similarité sémantique. C'est utile lorsque vous avez de nombreux souvenirs et que vous ne connaissez pas la clé exacte. Pour un petit ensemble de préférences structurées, la recherche directe par espace de nommage/clé est souvent plus simple et plus déterministe.
Étape 7 : Rendre explicites les chemins de lecture et d'écriture de la mémoire
Persister un élément à long terme ne garantit pas que l'agent l'utilisera. L'application a toujours besoin d'un chemin de récupération. Les agents LangChain actuels permettent aux outils d'accéder au store fourni via ToolRuntime.
from dataclasses import dataclass
from langchain.tools import tool, ToolRuntime
@dataclass
class Context:
user_id: str
@tool
def get_response_style(runtime: ToolRuntime[Context]) -> str:
store = runtime.store
if store is None:
return "No memory store configured"
namespace = ("users", runtime.context.user_id, "preferences")
item = store.get(namespace, "response_style")
return item.value["value"] if item else "default"
Vous pouvez également construire des prompts dynamiques ou des middlewares qui lisent l'état et la mémoire durable avant un appel de modèle. La règle de conception importante est que le chemin de récupération doit être observable et testable. « L'information existe quelque part dans la base de données » ne suffit pas.
Illustration générée par IA : Un test de régression utile demande un fait précédent après de nombreux tours et vérifie que la réponse provient de la couche de mémoire prévue, et non d'un texte de prompt accidentellement dupliqué.
Étape 8 : Tester les quatre limites de mémoire séparément
Une suite de tests de mémoire fiable doit couvrir plus que « le modèle a mémorisé mon nom une fois ». Utilisez au moins ces quatre cas :
Test
Résultat attendu
Deux invocations, même ID de thread
Les informations limitées au thread sont disponibles
Deux invocations, différents IDs de thread
L'historique du thread à court terme ne fuit pas
Redémarrage de l'application, même ID de thread avec checkpointer persistant
L'état du thread peut reprendre
Nouveau thread, même utilisateur avec store à long terme
Seuls les faits durables intentionnellement stockés peuvent être rappelés
Ajoutez ensuite un test de longue conversation qui dépasse votre seuil de synthèse. Affirmez que les faits durables importants survivent, que les séquences récentes d'appels d'outils restent valides et que la taille du prompt reste dans votre budget de contexte cible.
Illustration générée par IA : Traitez ceci comme une liste de contrôle QA conceptuelle. L'architecture actuelle de LangChain v1 doit être validée par rapport aux API officielles de checkpointer, store et middleware plutôt que par des exemples de classes de mémoire héritées.
Une architecture de production minimale
Pour de nombreuses applications d'agents, une conception robuste ressemble à ceci :
L'API reçoit user_id, conversation_id et le nouveau message utilisateur.
L'application mappe conversation_id vers un thread_id LangGraph stable.
Un checkpointer persistant restaure l'état du thread.
Un store à long terme récupère uniquement les faits durables utilisateur ou application nécessaires pour la requête.
La synthèse ou la réduction maintient l'historique exposé au modèle dans un budget de contexte mesuré.
L'agent exécute les outils et le modèle.
Le checkpointer valide l'état du thread mis à jour.
Seuls les faits approuvés sont écrits dans le store à long terme.
Si vous déployez via LangGraph Agent Server, le guide de persistance actuel indique que le serveur gère automatiquement l'infrastructure de persistance, donc ne dupliquez pas cette couche sans vérifier le modèle de déploiement.
Erreurs courantes qui font paraître la mémoire cassée
Générer un nouveau thread_id pour chaque requête
Cela crée un nouvel état de conversation à chaque tour. Journalisez l'ID de thread à côté de l'ID de chat de votre application et vérifiez la réutilisation.
Utiliser InMemorySaver dans un service multi-worker ou redémarrable
L'état local à la RAM disparaît avec le processus et peut ne pas être partagé entre les workers. Utilisez un backend persistant pour la continuité en production.
Supposer qu'un checkpointer résout le problème de la fenêtre de contexte
Un checkpointer préserve l'état ; il ne garantit pas qu'une transcription en croissance constante soit utile au modèle. Ajoutez une politique explicite de gestion du contexte.
Mettre chaque fait historique dans le prompt
Plus de contexte n'est pas automatiquement un meilleur contexte. Récupérez les informations pertinentes pour le tour actuel et préservez la continuité conversationnelle récente séparément.
Traiter les résumés comme une base de données parfaite
Les résumés sont des représentations compressées générées par le modèle. Si un fait doit être exact—un identifiant de compte, une contrainte contractuelle, une préférence approuvée par l'utilisateur ou un état de workflow—stockez-le comme données structurées plutôt que d'espérer qu'il survit à des synthèses répétées.
Mélanger les portées à court et à long terme
L'historique du thread ne doit pas silencieusement devenir un profil utilisateur global. Inversement, une préférence utilisateur destinée à suivre l'utilisateur entre les chats ne doit pas vivre uniquement dans un thread.
Copier des tutoriels de mémoire pré-v1 sans vérifier les imports
Si un exemple part de chaînes héritées ou d'anciennes classes de mémoire, comparez-le avec la migration v1 actuelle et la documentation sur la mémoire avant de l'utiliser dans une nouvelle application.
Liste de contrôle de débogage
Confirmez que l'agent a été créé avec un checkpointer.
Journalisez et comparez thread_id entre les requêtes consécutives.
Inspectez l'état du thread stocké avant de blâmer le modèle.
Redémarrez le processus et répétez le même test de thread.
Remplacez InMemorySaver par un checkpointer persistant pour la production.
Mesurez la croissance des messages/tokens sur les longs chats.
Activez la synthèse ou la réduction avant que l'historique ne devienne excessif.
Gardez les séquences d'appels/résultats d'outils valides lors de la suppression de messages.
Déplacez les faits inter-threads dans un store à long terme avec espace de nommage.
Testez un nouveau thread pour le même utilisateur pour vérifier le rappel à long terme intentionnel.
Testez un utilisateur différent pour vérifier l'isolation de la mémoire.
Tracez quels éléments de mémoire ont été récupérés pour chaque réponse.
En résumé
La perte de mémoire des agents LangChain est rarement résolue par une seule fenêtre de contexte plus grande. Rendez d'abord l'état du thread persistant avec un checkpointer et un thread_id stable. Ensuite, contrôlez les longs historiques avec la réduction ou SummarizationMiddleware. Enfin, placez les faits qui doivent survivre entre les conversations dans un store à long terme avec espace de nommage et récupérez-les délibérément.
Cette séparation vous donne quelque chose de beaucoup plus utile que la « mémoire » : un système que vous pouvez redémarrer, faire évoluer, tester, auditer et analyser lorsqu'un utilisateur demande : « Pourquoi l'agent a-t-il oublié ? »