Traduction automatique
Cet article a été traduit automatiquement depuis la version originale en anglais.
Architecture de mémoire des agents IA en 2026 : checkpoints, vector stores et mémoire basée sur des fichiers
Partie 2 de la série Engineering the Agentic Stack
L’état est ce qui distingue un chatbot d’un agent IA. Sans mémoire d’agent, chaque interaction redémarre à zéro. L’agent ne peut ni se mettre en pause et reprendre, ni apprendre des sessions passées, ni se personnaliser. La Partie 1 couvrait les boucles de raisonnement des agents IA : comment un agent réfléchit. Cet article porte sur l’architecture mémoire qui détermine ce dont il se souvient.
Je vais parcourir l’architecture mémoire du Market Analyst Agent, en montrant comment checkpoints chauds, vector stores froids et mémoire documentaire basée sur des fichiers fonctionnent ensemble pour des agents de longue durée. Puis j’expliquerai dans quels cas PostgreSQL, Redis, Qdrant, les magasins clé-valeur et de simples fichiers Markdown ont chacun du sens.
En bref : la mémoire d’un agent IA se découpe en trois couches. La mémoire chaude correspond aux checkpoints au niveau thread dans Redis ou PostgreSQL pour pause/reprise. La mémoire froide correspond à la connaissance inter-sessions dans un vector store ou un backend clé-valeur pour la personnalisation. La mémoire documentaire correspond à des fichiers lisibles par des humains que l’agent lit et écrit pour une connaissance projet persistante. Le système de checkpoints de LangGraph gère nativement la couche chaude. Pour la mémoire froide, la recherche vectorielle avec Qdrant fournit un rappel sémantique ; un magasin clé-valeur suffit pour des faits structurés. Pour la mémoire documentaire, un store basé sur des fichiers apporte débogabilité et zéro infrastructure d’embedding.
Pourquoi l’architecture de mémoire d’un agent IA est importante
Un agent sans état n’est qu’un autocomplete sophistiqué. Il prend une requête, retourne une réponse, oublie tout. Cela fonctionne pour du Q&A en un tour. Cela casse dès que vous avez besoin de :
- Pause et reprise : un utilisateur démarre une tâche de recherche, ferme son laptop, puis revient demain. Sans état checkpointé, l’agent repart de zéro.
- Cohérence multi-tours : sur une longue conversation, l’agent doit se souvenir des outils qu’il a appelés, des données qu’il a collectées et des étapes du plan qu’il a terminées.
- Personnalisation : un utilisateur qui revient s’attend à ce que l’agent connaisse sa tolérance au risque, sa profondeur d’analyse préférée et ses interactions passées.
- Human-in-the-loop (HITL) : l’agent rédige un rapport et attend une approbation. L’état « en attente » doit survivre aux redémarrages du processus.
Prenez le Market Analyst Agent de la Partie 1. Un utilisateur demande « Analyze NVDA ». L’agent construit un plan, appelle cinq outils, collecte des données, rédige un rapport. L’utilisateur répond « looks good, but add competitor analysis. » Sans état checkpointé, l’agent n’a aucune idée de ce à quoi « looks good » fait référence et doit tout recommencer. Avec un checkpoint store, il recharge l’état du dernier nœud, y compris la recherche déjà collectée, et ajoute simplement une étape concurrentielle.
Imaginez maintenant que ce même utilisateur revienne une semaine plus tard : « Update my NVDA analysis. » Sans mémoire long terme, l’agent ne sait pas que cet utilisateur préfère des évaluations de risque conservatrices ni qu’il s’intéresse aux actions de semi-conducteurs. Avec un store mémoire adossé à des vecteurs, il rappelle ces faits et personnalise la réponse sans avoir à les redemander.
LangGraph découpe cela proprement. Chaque exécution de graphe s’exécute dans un thread — une conversation ou une tâche unique. L’état au sein d’un thread est la working memory. L’état qui persiste au-delà des threads est la long-term memory. La vraie question d’ingénierie est de savoir quel backend de stockage utiliser pour chaque couche.
Une taxonomie de la mémoire des agents IA
Avant de parler implémentation, il est utile de classer ce que les agents doivent mémoriser. Le framework CoALA (Sumers, Yao et al., 2023) est la taxonomie de référence et s’appuie sur les sciences cognitives. J’ai introduit la notion de périmètre mémoire dans mon article sur le context engineering ; ici, je l’étends en six catégories :
| Type de mémoire | Périmètre | Durée de vie | Exemple | Pattern de stockage |
|---|---|---|---|---|
| Working | Étape courante | Millisecondes | Arguments d’appel d’outil, réponse LLM en cours | In-process (Python dict) |
| Court terme | Thread courant | Minutes–heures | Historique de conversation, progression du plan, données collectées | Checkpoint store |
| Épisodique | Inter-thread | Jours–mois | « La semaine dernière, l’utilisateur a demandé les résultats de NVDA » | Vector store / KV store |
| Sémantique | Inter-thread | Mois–permanent | « L’utilisateur préfère les investissements conservateurs » | Vector store / KV store |
| Documentaire | Inter-thread | Jours–permanent | Notes projet, synthèses de recherche, patterns appris | File store (Markdown/JSON) |
| Procédurale | Système global | Permanente | « Lors de l’analyse d’actions, toujours vérifier les SEC filings » | Config / system prompt |
La working memory correspond à ce sur quoi le LLM raisonne activement à l’instant T : variables Python de la fonction courante, contenu de la fenêtre de contexte, arguments d’appel d’outil en cours d’exécution. C’est la couche la plus rapide et la plus éphémère. Rien ne persiste au-delà de l’étape courante. La working memory est bornée par la fenêtre de contexte du modèle, ce qui en fait le véritable goulot d’étranglement. Tout ce que l’agent « sait » au moment de décider doit tenir ici, que cela provienne du checkpoint store, d’une requête vectorielle ou de la lecture d’un fichier. Les autres couches existent pour injecter la bonne information dans la working memory au bon moment.
La mémoire court terme est le checkpoint store que LangGraph écrit après chaque exécution de nœud. Les mémoires épisodique et sémantique sont des stores long terme persistants entre threads. La mémoire documentaire est une connaissance structurée que l’agent accumule au fil du temps — notes projet, synthèses de recherche, conventions apprises — stockée dans des fichiers lisibles par des humains que l’agent comme le développeur peuvent inspecter et modifier. La mémoire procédurale est encodée dans le system prompt et les définitions d’outils ; elle ne change pas selon l’utilisateur.
En pratique, cela se ramène à trois couches : mémoire chaude (working + court terme) pour la session courante, mémoire froide (épisodique + sémantique) pour le rappel inter-sessions, et mémoire documentaire pour la connaissance projet accumulée qui gagne à être lisible par un humain et directement modifiable.
La plupart des frameworks et des surveys se concentrent sur le découpage chaud/froid et ignorent la mémoire documentaire, malgré son adoption massive. Le framework CoALA classe la mémoire en working, épisodique, sémantique et procédurale ; aucune mention des fichiers. Le survey Memory in the Age of AI Agents couvre les vector stores et les graphes de connaissances, mais pas les fichiers documentaires. La documentation de LangGraph couvre les checkpoints et l’interface Store, mais n’a aucun concept natif de mémoire basée sur des fichiers.
En pratique, la mémoire documentaire est déjà un défaut dans les assistants de code IA. Claude Code, Cursor, Windsurf et Devin traitent tous la mémoire basée sur des fichiers comme une fonctionnalité centrale. Et le pattern se diffuse au-delà du code : les agents de jeux en monde ouvert stockent des compétences réutilisables sous forme de bibliothèques de code (Voyager), des agents d’entreprise gagnants en compétition itèrent sur des documents procéduraux de prompts entre les runs (approches gagnantes ECR3), et des agents d’automatisation web synthétisent des APIs de workflow réutilisables à partir d’épisodes réussis (Agent Workflow Memory). Les avantages — débogabilité, versioning, zéro infrastructure — ne sont pas spécifiques au code.
Un point utile issu du même survey : la mémoire d’agent n’est pas la même chose que le RAG ou le context engineering. Ce qui la distingue, c’est que l’agent lui-même effectue les opérations de lecture/écriture. Il décide de ce qu’il faut retenir et oublier, au lieu de dépendre d’un pipeline de retrieval figé.
Le papier Generative Agents (Park et al., 2023) a montré jusqu’où cela pouvait aller : des agents simulés qui stockaient, réfléchissaient sur et récupéraient leurs propres souvenirs produisaient des comportements étonnamment humains. Le pattern architectural — un memory stream dont la récupération est scorée par récence, importance et pertinence — reste le template sur lequel la plupart des systèmes mémoire d’agents modernes se construisent.
Mémoire court terme de l’agent : le checkpoint store
Chaque fois qu’un nœud LangGraph s’exécute, le framework sérialise l’état complet du graphe et l’écrit dans un checkpoint store. C’est la base de la pause/reprise, du débogage time-travel et des workflows HITL.
Un checkpoint contient l’état du graphe nécessaire pour reprendre : le AgentState de la Partie 1 (messages, identité, profil utilisateur, étapes du plan, données de recherche, mode d’exécution), plus des métadonnées LangGraph comme le nœud qui l’a produit et un numéro de séquence monotone croissant. Lorsque le graphe reprend — après une interruption HITL ou un redémarrage du processus — il charge le dernier checkpoint et continue exactement là où il s’était arrêté. Un checkpoint n’est pas la même chose qu’un event log append-only ou qu’une trace ; la Partie 5 sépare explicitement ces surfaces d’observabilité runtime.
Fonctionnement du checkpointing dans LangGraph
Le BaseCheckpointSaver de LangGraph est une interface simple : put() écrit un checkpoint, get_tuple() lit le plus récent pour un thread, list() retourne l’historique. Chaque checkpoint est indexé par (thread_id, checkpoint_ns, checkpoint_id), où thread_id identifie la conversation, checkpoint_ns gère le namespacing des sous-graphes, et checkpoint_id est une version unique.
La décision importante est le backend à placer derrière. LangGraph fournit deux options de production : PostgreSQL et Redis.
PostgreSQL vs Redis
| Dimension | PostgreSQL (langgraph-checkpoint-postgres) |
Redis (langgraph-checkpoint-redis) |
|---|---|---|
| Latence de lecture | ~0.65ms | ~0.095ms |
| Latence d’écriture | ~2ms (unlogged) à 10ms (avec WAL) | ~0.095ms |
| Débit | ~15K txn/s | ~893K req/s |
| Durabilité | ACID complet, WAL + réplication | Configurable (AOF/RDB), risque de perte de données |
| Historique des checkpoints | Historique complet (reprise, débogage time-travel) | Rétention configurable via maxcount |
| Coût opérationnel | Modéré (ops RDBMS standard) | Plus élevé (lié à la RAM, gestion mémoire) |
| Pattern de scaling | Vertical + read replicas | Horizontal (Redis Cluster) |
| Idéal pour | Reprise et débogage orientés durabilité | Faible latence, haut débit, temps réel |
Benchmarks de latence issus des comparaisons CyberTec et RisingWave.
PostgreSQL : le défaut durable
PostgreSQL est le choix par défaut le plus sûr pour la plupart des équipes. Les checkpoints survivent aux crashs, vous avez des garanties transactionnelles complètes, et l’historique des checkpoints rend le débogage time-travel simple.
Depuis checkpointer_setup.py :
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
async def create_postgres_checkpointer(connection_string: str) -> AsyncPostgresSaver:
"""Create a PostgreSQL-backed checkpoint store.
PostgreSQL gives us ACID guarantees — if a checkpoint write succeeds,
the state is durable even if the process crashes immediately after.
"""
checkpointer = AsyncPostgresSaver.from_conn_string(connection_string)
# Create the checkpoint tables if they don't exist.
# This is idempotent — safe to call on every startup.
await checkpointer.setup()
return checkpointer
# Usage: wire into the graph compilation
checkpointer = await create_postgres_checkpointer(
"postgresql://user:pass@localhost:5432/agent_memory"
)
graph = create_graph(checkpointer=checkpointer)
# Every invoke/stream call now persists state automatically
config = {"configurable": {"thread_id": "user-123-session-1"}}
result = await graph.ainvoke({"messages": [HumanMessage(content="Analyze NVDA")]}, config)
# Resume later — loads the latest checkpoint for this thread
result = await graph.ainvoke({"messages": [HumanMessage(content="approved")]}, config)
Le AsyncPostgresSaver utilise le package langgraph-checkpoint-postgres, qui crée trois tables : checkpoints (l’état sérialisé), checkpoint_blobs (les données binaires volumineuses) et checkpoint_writes (les écritures en attente pour la reprise après crash). Le schéma supporte l’accès concurrent et utilise des advisory locks pour éviter les conflits d’écriture.
Redis : quand la latence est le goulot d’étranglement
Quand une latence de checkpoint sous la milliseconde est importante (agents conversationnels temps réel, boucles d’outils à haute fréquence), Redis est un meilleur choix.
Depuis checkpointer_setup.py :
from langgraph.checkpoint.redis.aio import AsyncRedisSaver
async def create_redis_checkpointer(redis_url: str) -> AsyncRedisSaver:
"""Create a Redis-backed checkpoint store.
Redis stores checkpoints in memory for sub-millisecond access.
Trade-off: less durable than PostgreSQL unless AOF is enabled.
"""
checkpointer = AsyncRedisSaver.from_conn_string(redis_url)
# Initialize Redis data structures
await checkpointer.setup()
return checkpointer
# Usage: same graph API, different backend
checkpointer = await create_redis_checkpointer("redis://localhost:6379")
graph = create_graph(checkpointer=checkpointer)
Le AsyncRedisSaver de langgraph-checkpoint-redis stocke les checkpoints comme documents JSON indexés par thread ID. Le redesign v0.1.0 a remplacé plusieurs opérations de recherche par un unique appel JSON.GET, réduisant significativement la latence. Redis 8.0+ inclut RedisJSON et RediSearch par défaut — aucun module supplémentaire à installer.
Pour les déploiements contraints en mémoire, ShallowRedisSaver ne stocke que le dernier checkpoint par thread — pas d’historique, mais une utilisation RAM minimale. À utiliser si vous avez besoin de pause/reprise sans avoir besoin de débogage time-travel.
Quand utiliser quoi
Utilisez PostgreSQL si :
- Vous avez besoin d’un historique complet des checkpoints pour du débogage time-travel ou une reprise reproductible
- La durabilité est non négociable (services financiers, santé)
- Vous exécutez déjà PostgreSQL dans votre stack
- Votre agent exécute de longues tâches où perdre l’état signifie des heures de recomputation
- Vous voulez un store de données unifié — PostgreSQL avec pgvector peut servir de backend unique pour checkpoints, mémoire long terme et recherche vectorielle, ce qui simplifie l’infrastructure
Utilisez Redis si :
- La latence de checkpoint est votre goulot d’étranglement (chat temps réel, streaming UX)
- Vous construisez des voice bots — les pipelines STT-to-LLM-to-TTS ont besoin d’un accès à l’état sous la milliseconde
- Vous avez besoin de scaling horizontal sur de nombreux threads concurrents
- Vous avez des patterns de fan-out à forte concurrence où plusieurs agents partagent l’état
- Vos sessions sont courtes et la perte d’un checkpoint est récupérable
- Vous voulez du semantic caching pour réduire les appels LLM redondants (Redis LangCache met en cache des requêtes sémantiquement similaires pour éviter des appels LLM répétés)
Autres options : langgraph-checkpoint-sqlite fonctionne pour le développement local et les déploiements mono-processus. Pour les stacks AWS natives, langgraph-checkpoint-aws fournit un DynamoDBSaver avec gestion intelligente des payloads — les petits checkpoints (<350 KB) restent dans DynamoDB, les plus gros sont automatiquement déportés vers S3. Une tarification serverless et l’absence d’infrastructure à gérer en font une option attractive pour des déploiements à charge variable.
Mémoire long terme : se souvenir au-delà des sessions
La mémoire chaude gère la conversation en cours. Mais que faire d’un utilisateur qui revient la semaine suivante ? La mémoire long terme stocke les faits, préférences et historiques d’interaction qui persistent d’un thread à l’autre.
LangGraph fournit une interface Store pour la mémoire inter-thread via sa classe BaseStore. Chaque élément mémoire est une paire (namespace, key) avec une valeur JSON et éventuellement un embedding vectoriel. Le namespace encode en général l’utilisateur ou l’organisation : ("user", "user-123", "preferences").
Stockage vectoriel : rappel sémantique avec Qdrant
Quand l’agent doit rappeler des faits non structurés (« What did the user say about their investment timeline? »), la recherche vectorielle fournit un rappel sémantique. Au lieu de lookup exacts par clé, l’agent interroge par le sens.
Qdrant est une base de données vectorielle dédiée écrite en Rust qui gère le stockage des embeddings, l’indexation (HNSW) et la recherche filtrée. J’ai couvert HNSW et ses trade-offs en détail dans mon article sur le search ranking. Qdrant propose aussi un serveur MCP qui agit comme couche de mémoire sémantique — utile si votre framework d’agent supporte le Model Context Protocol.
Depuis memory_store.py :
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams
from langchain_anthropic import ChatAnthropic
import hashlib
import json
class UserMemoryStore:
"""Long-term memory backed by Qdrant vector search.
Stores user facts as embedded vectors for semantic retrieval.
Each fact is a short natural-language statement about the user.
"""
def __init__(self, qdrant_url: str, collection_name: str = "user_memory"):
self.client = QdrantClient(url=qdrant_url)
self.collection_name = collection_name
self._ensure_collection()
def _ensure_collection(self):
"""Create the collection if it doesn't exist."""
collections = [c.name for c in self.client.get_collections().collections]
if self.collection_name not in collections:
self.client.create_collection(
collection_name=self.collection_name,
vectors_config=VectorParams(
size=1536, # text-embedding-3-small dimensions
distance=Distance.COSINE,
),
)
def store_fact(self, user_id: str, fact: str, embedding: list[float]):
"""Store a user fact with its embedding."""
point_id = hashlib.md5(f"{user_id}:{fact}".encode()).hexdigest()
self.client.upsert(
collection_name=self.collection_name,
points=[PointStruct(
id=point_id,
vector=embedding,
payload={"user_id": user_id, "fact": fact},
)],
)
def recall(self, user_id: str, query_embedding: list[float], top_k: int = 5):
"""Retrieve the most relevant facts for a user given a query."""
results = self.client.query_points(
collection_name=self.collection_name,
query=query_embedding,
query_filter={"must": [{"key": "user_id", "match": {"value": user_id}}]},
limit=top_k,
)
return [hit.payload["fact"] for hit in results.points]
Le flux est le suivant : (1) après chaque conversation, un LLM extrait les faits clés de l’interaction (« user has high risk tolerance », « user is interested in semiconductor stocks »), (2) les faits sont embedded puis stockés dans Qdrant, (3) au démarrage de la conversation suivante, l’agent interroge Qdrant avec le nouveau message utilisateur pour rappeler le contexte pertinent.
Scoring de retrieval : au-delà de la similarité cosinus
La similarité cosinus brute est un point de départ, mais les systèmes mémoire de production ont besoin d’un retrieval plus riche. Le papier Generative Agents (Park et al., 2023) a introduit une fonction de scoring qui combine trois signaux :
- Récence : décroissance rule-based pour que les souvenirs récents scorent plus haut. Une fonction de décroissance exponentielle fait qu’un fait d’hier surclasse un fait équivalent vieux de six mois.
- Importance : significativité notée par LLM sur une échelle de 1 à 10. « User's portfolio is down 40% » score plus haut que « user said hello. »
- Pertinence : similarité cosinus d’embedding entre la requête et le fait stocké.
Le score final de retrieval est une somme pondérée : score = alpha * recency + beta * importance + gamma * relevance. Cela évite que des faits frais et importants soient enfouis sous des faits obsolètes mais sémantiquement proches. Pour le Market Analyst Agent, je donne le poids le plus fort à la pertinence (0.5), puis à la récence (0.3) et à l’importance (0.2), car l’intention courante de la requête utilisateur compte le plus. Ce sont des pondérations de départ adaptées du papier Generative Agents (qui utilisait une pondération égale) ; j’ai trouvé qu’accentuer la pertinence fonctionnait mieux pour les requêtes d’analyse financière, mais ces valeurs relèvent de l’intuition, pas d’une optimisation empirique.
Alternatives à la recherche vectorielle
La recherche vectorielle est puissante, mais ce n’est pas toujours le bon outil. Voici quand utiliser des alternatives :
| Approche | Idéal pour | Latence | Complexité |
|---|---|---|---|
| Recherche vectorielle (Qdrant) | Rappel sémantique de faits non structurés | 5–20ms | Moyenne |
| Magasin clé-valeur (Redis) | Profils utilisateur structurés, préférences | <1ms | Faible |
| Magasin documentaire (fichiers) | Connaissance projet, notes gérées par l’agent | 1–5ms | Faible |
| Recherche plein texte (PostgreSQL GIN index) | Rappel basé mots-clés de l’historique conversationnel | 2–10ms | Faible |
| Graphe de connaissances (Neo4j) | Relations entre entités, raisonnement multi-hop | 10–50ms | Élevée |
| Hybride (vecteur + mot-clé) | Meilleur rappel si l’intention de requête varie | 10–30ms | Moyenne |
Les magasins clé-valeur fonctionnent bien pour les données structurées. Si votre mémoire long terme correspond à un profil utilisateur — tolérance au risque, horizon d’investissement, secteurs préférés — un hash Redis ou une colonne PostgreSQL JSONB est plus simple et plus rapide que d’embedder puis interroger des vecteurs. Utilisez la recherche vectorielle quand la mémoire est non structurée et que la formulation de la requête varie.
Le Store intégré de LangGraph fournit une interface clé-valeur à base de namespace avec recherche vectorielle optionnelle. L’API BaseStore est simple : put(), get(), search() et delete() avec un scoping hiérarchique par namespace. Trois implémentations sont disponibles :
InMemoryStore— pour développement et tests (données perdues à l’arrêt du processus)PostgresStore— store persistant de production avec requêtage SQL completAsyncRedisStore— mémoire inter-thread avec recherche vectorielle, support TTL et filtrage par métadonnées
La configuration index active la recherche vectorielle sur les éléments stockés via un modèle d’embedding configurable. Pour beaucoup de cas d’usage, ce store intégré suffit sans recourir à une base de données vectorielle dédiée.
from langgraph.store.memory import InMemoryStore
# Create a store with vector search enabled
store = InMemoryStore(
index={
"dims": 1536,
"embed": my_embedding_function, # e.g., OpenAI text-embedding-3-small
}
)
# Store a user preference (namespace scopes to user)
await store.aput(
namespace=("user", "user-123", "preferences"),
key="risk-profile",
value={"risk_tolerance": "high", "horizon": "long-term"},
)
# Semantic search across user's memories
results = await store.asearch(
namespace=("user", "user-123"),
query="What is their investment style?",
limit=5,
)
Choisir une stratégie de mémoire long terme
Commencez par du clé-valeur si votre mémoire est structurée et bien définie (profils utilisateur, paramètres, entités nommées). Ajoutez de la recherche vectorielle quand vous avez besoin de retrieval sémantique sur des faits non structurés ou quand la formulation des requêtes varie de manière imprévisible.
Les graphes de connaissances deviennent intéressants lorsque les relations entre entités comptent, par exemple « Which companies did the user ask about that are competitors of NVDA? » Le projet récent le plus intéressant ici est Graphiti (par Zep), qui construit un graphe de connaissances temporellement conscient capable de suivre quand les faits étaient vrais, et pas seulement quels faits étaient vrais. Chaque arête porte des intervalles de validité, donc un changement de tolérance au risque de l’utilisateur invalide l’ancienne valeur au lieu de l’écraser silencieusement. Graphiti annonce 94.8% d’accuracy sur le benchmark DMR, et son modèle bi-temporel traite le problème de mémoire obsolète au niveau de la couche de données.
Le revers est opérationnel. Exécuter une base graphe n’est pas trivial, et pour la plupart des applications d’agents, la recherche vectorielle avec filtrage par métadonnées couvre le même terrain avec moins d’infrastructure.
Les frameworks de mémoire managés comme Mem0 et Letta (anciennement MemGPT) gèrent pour vous le pipeline extraction-consolidation-retrieval. L’approche de Mem0 est notable : un LLM extrait des souvenirs candidats, un moteur de décision compare chaque nouveau fait aux entrées existantes du vector store, puis un resolver décide d’ajouter, mettre à jour ou supprimer, ce qui maintient le store mémoire cohérent et sans redondance. Letta adopte un angle systèmes d’exploitation : les agents gèrent eux-mêmes leur fenêtre de contexte via des outils de gestion mémoire, en déplaçant de façon autonome les données entre « core memory » (dans le contexte) et « archival memory » (hors contexte). Les deux méritent d’être évalués si vous voulez réduire le temps de mise en production et n’avez pas besoin d’un contrôle complet sur le pipeline mémoire.
Mémoire documentaire : le classeur de l’agent
Le pattern est très répandu en pratique, mais sous-exploré dans la littérature académique. La documentation des frameworks couvre en profondeur les vector stores, les graphes de connaissances et la gestion du contexte, mais la mémoire basée sur des fichiers n’a droit qu’à une note de bas de page. C’est une lacune à combler, car c’est ainsi que les assistants de code IA les plus efficaces persistent réellement la connaissance. Le benchmark de Letta a montré qu’une approche simple basée sur le filesystem obtenait 74.0% sur le benchmark de mémoire conversationnelle LoCoMo, devant plusieurs bibliothèques mémoire spécialisées. Les modèles frontier sont déjà entraînés sur des tâches de code agentique et gèrent nativement les opérations sur fichiers ; le pattern s’aligne donc avec leurs forces.
Le basculement est venu de l’allongement des fenêtres de contexte. En 2023-2024, avec 8k-32k tokens, vous n’aviez pas le choix : il fallait découper les documents en chunks et les embedder. Avec des fenêtres à 1M+ tokens (Gemini 1.5, Claude 4), il est généralement plus efficace de laisser l’agent lire le fichier entier que d’essayer de deviner quels chunks sont pertinents. L’histoire opérationnelle est aussi plus simple. Vous pouvez lire, modifier et git diff un fichier Markdown. Vous ne pouvez pas git diff une base de données vectorielle.
Les vector stores et les backends clé-valeur gèrent bien le rappel sémantique et les lookups structurés. Mais il existe une troisième catégorie de connaissance agentique qu’aucun des deux ne sert proprement : le contexte projet accumulé, c’est-à-dire les conventions, notes de recherche et décisions dont l’agent a besoin entre sessions et qui gagnent à être lisibles par des humains et versionnées.
C’est cela, la mémoire documentaire : l’agent lit et écrit des fichiers structurés (Markdown, JSON, YAML) dans un répertoire connu. Pas d’embeddings, pas de base de données, pas d’infrastructure. Juste des fichiers sur disque que l’agent et le développeur peuvent cat, grep, git diff et modifier à la main.
Pourquoi des fichiers ?
Pour des workflows agentiques longue durée, le pattern le plus efficace que j’ai vu n’est pas une base de données vectorielle. C’est un répertoire de notes bien organisées. Considérez ce qui se passe quand un agent de code travaille sur un projet pendant des semaines :
- Il apprend que le projet utilise Pydantic v2, pas v1
- Il découvre que les tests doivent s’exécuter avec
pytest -x --tb=short - Il accumule de la connaissance sur l’architecture du codebase
- Il apprend les préférences du développeur (« toujours utiliser
pathlib, jamaisos.path»)
Ces faits sont trop structurés pour la recherche vectorielle (vous avez besoin d’un rappel exact, pas d’une similarité floue) et trop nombreux pour un magasin clé-valeur (ils forment des documents interconnectés, pas des faits isolés). Ce sont aussi des faits que le développeur veut voir et modifier directement. Si l’agent apprend quelque chose de faux, vous ouvrez le fichier et vous corrigez.
C’est ainsi que fonctionnent le CLAUDE.md de Claude Code et son répertoire .claude/. L’agent lit les fichiers CLAUDE.md au niveau projet pour les conventions et instructions, et écrit dans ~/.claude/MEMORY.md pour les apprentissages inter-sessions. Les fichiers sont en Markdown brut : vous les lisez, les modifiez, les commitez dans git, les partagez avec votre équipe. Les .cursorrules de Cursor et les .windsurfrules de Windsurf suivent le même pattern : des fichiers texte brut que l’agent charge au démarrage pour récupérer le contexte projet.
Implémenter un file memory store
L’implémentation est volontairement simple. L’agent dispose de quatre opérations : écrire un document, lire un document, lister les documents disponibles et rechercher dans les documents par mot-clé.
Depuis file_memory.py :
from pathlib import Path
import json
import fnmatch
class FileMemory:
"""Document memory backed by the local filesystem.
Stores agent knowledge as human-readable files organized by topic.
No embeddings, no database — just files that both the agent and
the developer can read, edit, and version-control.
"""
def __init__(self, base_dir: str | Path):
self.base_dir = Path(base_dir)
self.base_dir.mkdir(parents=True, exist_ok=True)
def write_doc(self, path: str, content: str, metadata: dict | None = None):
"""Write or overwrite a document at the given path.
Paths are relative to base_dir. Directories are created automatically.
Metadata (if provided) is stored as a JSON sidecar file.
"""
full_path = self.base_dir / path
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content, encoding="utf-8")
if metadata:
meta_path = full_path.with_suffix(full_path.suffix + ".meta")
meta_path.write_text(json.dumps(metadata, indent=2), encoding="utf-8")
def read_doc(self, path: str) -> str | None:
"""Read a document by path. Returns None if not found."""
full_path = self.base_dir / path
if full_path.exists():
return full_path.read_text(encoding="utf-8")
return None
def list_docs(self, pattern: str = "**/*") -> list[str]:
"""List documents matching a glob pattern."""
return [
str(p.relative_to(self.base_dir))
for p in self.base_dir.glob(pattern)
if p.is_file() and not p.name.endswith(".meta")
]
def search_docs(self, query: str, pattern: str = "**/*.md") -> list[dict]:
"""Search documents by keyword. Returns matching files with context.
This is intentionally simple — grep-style keyword search.
For semantic search, use a vector store instead.
NOTE: This is a sketch for demonstration. A simple substring check
won't scale beyond a few hundred documents. For production with 500+
documents, use TF-IDF/BM25 scoring (e.g., rank_bm25) or a full-text
search backend (PostgreSQL GIN index, Elasticsearch).
"""
results = []
for path in self.base_dir.glob(pattern):
if not path.is_file() or path.name.endswith(".meta"):
continue
content = path.read_text(encoding="utf-8")
if query.lower() in content.lower():
# Return the paragraph containing the match for context
for paragraph in content.split("\n\n"):
if query.lower() in paragraph.lower():
results.append({
"path": str(path.relative_to(self.base_dir)),
"match": paragraph.strip()[:500],
})
return results
Structure des dossiers
L’essentiel de la valeur de la mémoire documentaire vient de la manière dont le répertoire est organisé. Voici la structure que j’utilise pour le Market Analyst Agent :
.agent-memory/
README.md # What this directory is, for human readers
user-profiles/
user-123.md # Preferences, history, risk profile
user-456.md
research/
NVDA-2026-02.md # Research notes from recent analysis
TSLA-2026-01.md
conventions/
analysis-format.md # How to structure analysis reports
data-sources.md # Preferred data sources and API patterns
learnings/
common-errors.md # Mistakes the agent has learned to avoid
tool-patterns.md # Effective tool call sequences
Chaque fichier est en Markdown. Le rôle de chaque fichier est évident à partir de son chemin. Vous pouvez git diff tout le répertoire mémoire pour voir ce que l’agent a appris pendant une session, git revert un mauvais apprentissage, ou copier le répertoire vers un autre projet. Essayez de faire cela avec une collection Qdrant.
Quand utiliser mémoire documentaire vs vectorielle vs clé-valeur
Les trois backends mémoire servent des patterns d’accès différents :
| Dimension | Vector Store | Key-Value Store | Document Store |
|---|---|---|---|
| Pattern de requête | « Trouver des faits similaires à X » | « Obtenir la valeur pour une clé » | « Lire le doc à ce chemin » |
| Idéal pour | Rappel non structuré, varié | Lookups structurés | Contexte projet, notes |
| Lisible par un humain | Non (embeddings) | Partiellement (JSON) | Oui (Markdown) |
| Débogable | Difficile (scores de similarité) | Facile (clés exactes) | Trivial (ouvrir le fichier) |
| Versionnable | Non | Possible | Oui (natif git) |
| Infrastructure d’embedding | Requise | Inutile | Inutile |
| Passe à l’échelle jusqu’à | Des millions de faits | Des millions de clés | Des milliers de documents |
| Capacité de recherche | Similarité sémantique | Correspondance exacte | Mot-clé / basé sur chemin |
Utilisez la mémoire documentaire si :
- L’agent accumule de la connaissance projet au fil de plusieurs sessions
- Les développeurs doivent inspecter, modifier ou surcharger ce que l’agent « sait »
- La connaissance est structurée comme des documents (notes, synthèses, conventions) plutôt que comme des faits isolés
- Vous voulez versionner la mémoire de l’agent via git
- Zéro infrastructure est une contrainte forte
Utilisez les vector stores si :
- Vous avez besoin d’un retrieval sémantique flou (« find memories related to X »)
- La formulation des requêtes varie de manière imprévisible
- Vous avez des milliers à des millions de faits individuels
Utilisez les magasins clé-valeur si :
- Vous avez besoin de lookups exacts et rapides sur des données structurées (profils utilisateur, paramètres)
- Le schéma de données est bien défini
En pratique, les agents de production combinent souvent les trois. Le Market Analyst Agent utilise des checkpoints PostgreSQL pour la mémoire chaude, Qdrant pour le rappel sémantique des faits utilisateur, et un document store basé sur des fichiers pour les conventions projet et les notes de recherche.
Exemples concrets
Le pattern est déjà largement répandu dans les assistants de code IA :
- Claude Code lit des fichiers
CLAUDE.mddepuis la racine du projet et les répertoires parents, et écrit dans~/.claude/MEMORY.mdpour les apprentissages inter-sessions. Tout le système mémoire repose sur des fichiers Markdown bruts que vous committez avec votre code. - Cursor charge des fichiers
.cursorrulespour les instructions agent spécifiques au projet : conventions de code, préférences de framework, décisions d’architecture. - Windsurf utilise des fichiers
.windsurfrulesainsi qu’un répertoirememories/où l’agent stocke les patterns appris à partir de votre codebase. - L’outil mémoire d’Anthropic pour l’API Claude fournit des opérations
create_memory,read_memory,update_memoryetdelete_memoryimplémentées côté client. C’est votre application qui décide où les fichiers vivent réellement (disque local, S3, base de données).
Le point commun : tous stockent la connaissance de l’agent sous forme de fichiers texte lisibles par des humains, avec des opérations explicites de lecture/écriture. Pas d’embeddings. Pas d’infrastructure vectorielle. L’agent décide quoi écrire, le développeur peut tout voir et tout modifier, et l’ensemble du système tient dans un git diff.
Au-delà des assistants de code
La mémoire documentaire n’est pas limitée aux agents de code. Le pattern apparaît dans des domaines d’agents très différents :
-
Agents de jeux en monde ouvert : Voyager (Wang et al., 2023) construit une bibliothèque persistante de programmes JavaScript vérifiés qu’un agent Minecraft accumule au fil du temps, en collectant 3.3x plus d’objets uniques et en atteignant les milestones 15.3x plus vite que les baselines. Les compétences se transfèrent à de nouveaux mondes sans retraining. JARVIS-1 étend cela avec une mémoire multimodale qui combine plans textuels et observations visuelles, avec un taux de succès 5x sur les tâches les plus difficiles.
Une distinction utile ici : les skill libraries sont une mémoire exécutable (fichiers de code importés et exécutés), tandis que la mémoire documentaire des assistants de code est déclarative (Markdown injecté dans les prompts). Les modes de défaillance diffèrent. Du mauvais code exécutable fait planter l’agent ; du mauvais texte déclaratif provoque des erreurs de raisonnement. Mais le pattern de stockage et les bénéfices opérationnels (débogabilité, contrôle de version) restent les mêmes.
-
Automatisation de workflows d’entreprise : les gagnants de la compétition ECR3 ont utilisé de la mémoire documentaire pour l’itération sur les prompts. Les agents Analyzer et Versioner d’une équipe gagnante ont itéré sur 80 versions de prompt stockées comme documents procéduraux. Une autre équipe de tête a construit plus de 20 modules d’enrichissement sous forme de connaissance procédurale de type documentaire. LEGOMem (2025) formalise cela sous la forme d’un framework mémoire modulaire pour systèmes multi-agents, avec des types de mémoire spécialisés (sensorielle, court terme, long terme) que les agents composent comme des briques.
-
Automatisation web : Agent Workflow Memory (Wang et al., 2024) permet à des agents web d’induire des workflows réutilisables à partir d’épisodes réussis, avec une amélioration de 51% du taux de succès sur WebArena. SkillWeaver (2025) va plus loin : des agents synthétisent des outils API réutilisables à partir de l’exploration, avec un gain de 31.8% du taux de succès. Les compétences apprises se transfèrent aussi à des modèles plus faibles (amélioration de 54.3%), de sorte que la mémoire accumulée d’un agent plus fort peut améliorer un plus petit.
-
Support client : Gartner prévoit que les agents IA résoudront de manière autonome 80% des problèmes courants de service client d’ici 2029. Ces agents s’appuient sur des SOPs, playbooks et historiques clients, qui sont tous des formes de mémoire documentaire.
L’atelier MemAgents à ICLR 2026 est un signe que la communauté recherche commence à rattraper ce que les praticiens ont déjà construit. La mémoire documentaire a clairement dépassé ses origines d’assistant de code.
Les skills sont de la mémoire documentaire avec un schéma. Le standard Agent Skills (fichiers SKILL.md avec frontmatter YAML et corps Markdown) est désormais utilisé à la fois par Anthropic et OpenAI Codex.
MCP (Model Context Protocol) va dans la même direction : les définitions d’outils sont des fichiers JSON Schema que n’importe quel agent peut découvrir et appeler. Le protocole cumule 97 millions de téléchargements mensuels du SDK et est supporté par OpenAI, Google, Microsoft et AWS. MCP n’est pas spécifique au code. Les mêmes serveurs connectent des agents à des bases de données, APIs internes et systèmes d’entreprise.
Les deux pointent vers le même pattern : une connaissance procédurale stockée sous forme de documents à schéma imposé, avec des opérations explicites de lecture/écriture. MCP, désormais gouverné par la Agentic AI Foundation, est ce qui se rapproche le plus d’un standard d’interop dans l’écosystème agentique.
Faire passer la mémoire documentaire à l’échelle en production
L’implémentation basée sur des fichiers ci-dessus fonctionne bien sur le laptop d’un développeur seul et pour de petits déploiements. La production multi-tenant avec des centaines d’utilisateurs et des milliers de documents est un autre problème.
La limite d’un unique nœud de fichiers devient évidente : vous ne pouvez pas faire du scaling horizontal de l’I/O fichier, les écritures concurrentes nécessitent du locking, et la gestion des permissions entre tenants est pénible. La production a besoin d’un backing store qui gère correctement concurrence, recherche et multi-tenancy.
Trois approches courantes :
Approche A : hybride avec une fine couche base de données
Conservez les fichiers pour l’authoring (les développeurs modifient du Markdown en local) mais servez depuis une base à l’exécution. Au déploiement, synchronisez les fichiers vers des lignes PostgreSQL. L’agent lit depuis la base, pas depuis le disque. Cela vous donne :
- Une bonne ergonomie développeur (modifier du Markdown, commit dans git)
- De bonnes performances de requête en production (lectures base indexées)
- Une séparation claire entre authoring et serving
Approche B : object storage + sidecar d’index vectoriel
Stockez les documents dans S3/GCS comme objets, avec une collection Qdrant qui indexe leurs embeddings. L’agent interroge Qdrant pour les IDs de documents pertinents, puis récupère le contenu depuis l’object storage. Cela scale horizontalement et supporte la recherche sémantique, mais ajoute de la complexité : deux systèmes à gérer, un pipeline d’embedding à maintenir, et de l’éventual consistency entre le store et l’index.
Approche C : document store structuré avec PostgreSQL (recommandé)
Stockez les documents comme lignes PostgreSQL JSONB avec recherche plein texte (GIN index) et embeddings vectoriels optionnels (pgvector). Vous obtenez ainsi une recherche hybride (mot-clé + sémantique), des transactions ACID et un système opérationnel unique.
Un aperçu de l’approche C :
from typing import Optional
import asyncpg
class ProductionDocumentMemory:
"""PostgreSQL-backed document memory with hybrid search.
Schema:
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
tenant_id TEXT NOT NULL,
path TEXT NOT NULL,
content TEXT NOT NULL,
metadata JSONB,
embedding vector(1536), -- pgvector extension
ts_vector tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, path)
);
CREATE INDEX ON documents USING GIN(ts_vector);
CREATE INDEX ON documents USING ivfflat(embedding vector_cosine_ops);
"""
def __init__(self, pool: asyncpg.Pool):
self.pool = pool
async def write(
self,
tenant_id: str,
path: str,
content: str,
metadata: Optional[dict] = None,
embedding: Optional[list[float]] = None,
):
"""Write or update a document."""
async with self.pool.acquire() as conn:
await conn.execute(
"""
INSERT INTO documents (tenant_id, path, content, metadata, embedding)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (tenant_id, path) DO UPDATE
SET content = EXCLUDED.content,
metadata = EXCLUDED.metadata,
embedding = EXCLUDED.embedding
""",
tenant_id, path, content, metadata, embedding,
)
async def search(
self,
tenant_id: str,
query: str,
embedding: Optional[list[float]] = None,
limit: int = 5,
) -> list[dict]:
"""Hybrid search: full-text + optional vector similarity."""
async with self.pool.acquire() as conn:
if embedding:
# Hybrid scoring: 0.6 * text relevance + 0.4 * vector similarity
rows = await conn.fetch(
"""
SELECT path, content, metadata,
(0.6 * ts_rank(ts_vector, plainto_tsquery('english', $2)) +
0.4 * (1 - (embedding <=> $3))) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $4
""",
tenant_id, query, embedding, limit,
)
else:
# Full-text search only
rows = await conn.fetch(
"""
SELECT path, content, metadata,
ts_rank(ts_vector, plainto_tsquery('english', $2)) AS score
FROM documents
WHERE tenant_id = $1
AND ts_vector @@ plainto_tsquery('english', $2)
ORDER BY score DESC
LIMIT $3
""",
tenant_id, query, limit,
)
return [dict(row) for row in rows]
Ce que vous obtenez :
- Recherche hybride : matching par mot-clé (GIN index) + similarité sémantique (pgvector) scorés ensemble
- Multi-tenancy : scoping
tenant_idavec row-level security - Garanties ACID : pas de problèmes d’éventual consistency
- Système opérationnel unique : pas de base vectorielle séparée à gérer
- Scaling horizontal : read replicas pour la charge de requêtes, partitionnement par tenant pour la montée en charge en écriture
Les fichiers sont excellents pour les workflows mono-développeur. Pour la production multi-tenant, un document store structuré sur PostgreSQL est généralement le meilleur compromis entre simplicité, performances et maturité opérationnelle.
Assembler l’ensemble : l’architecture complète
Voici comment les trois couches mémoire fonctionnent ensemble dans le Market Analyst Agent. Le schéma montre le flux complet, de la requête utilisateur à la réponse, avec toutes les couches mémoire actives.
L’architecture comporte trois chemins mémoire :
-
Chemin chaud (checkpoint store) : chaque nœud du LangGraph écrit son état de graphe reprenable dans le checkpoint store. Quand le graphe atteint un nœud
interrupt_before(comme le reporter de la Partie 1), l’exécution se met en pause. L’utilisateur peut fermer l’application, et à son retour, le graphe reprend depuis le checkpoint. Les event logs runtime et les traces sont des sujets de production distincts. -
Chemin froid (long-term store) : au début de chaque conversation, l’agent interroge le long-term store pour récupérer le contexte utilisateur pertinent. À la fin, il extrait et stocke de nouveaux faits. Cela s’exécute de manière asynchrone — cela ne doit jamais bloquer la boucle principale de raisonnement.
-
Chemin documentaire (file store) : au démarrage, l’agent charge depuis le document store les conventions projet et les notes de recherche pertinentes. Pendant l’exécution, il écrit de nouvelles synthèses de recherche et de nouveaux patterns appris sur disque. Contrairement au chemin froid, les lectures documentaires sont synchrones (elles informent la tâche courante) tandis que les écritures peuvent être différées.
Le câblage dans LangGraph est simple — le checkpoint store et le long-term store sont passés à la compilation du graphe, tandis que le document store est injecté comme dépendance. Le sketch local ci-dessous utilise InMemoryStore pour garder l’extrait court ; la topologie Docker de référence utilise Qdrant pour le même rôle de rappel sémantique.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langgraph.store.memory import InMemoryStore
# Hot memory: PostgreSQL for durable checkpoints
checkpointer = await create_postgres_checkpointer(pg_connection_string)
# Cold memory: local sketch with vector search
# (The reference Docker topology uses Qdrant for persistent recall.)
memory_store = InMemoryStore(
index={"dims": 1536, "embed": embedding_function}
)
# Document memory: file-based store for project knowledge
doc_memory = FileMemory(base_dir=".agent-memory")
# Checkpoint store and long-term store wired into the graph
graph = create_graph(
checkpointer=checkpointer,
store=memory_store,
)
# The store is accessible inside any node via the store parameter
def planner_node(state: AgentState, *, store: BaseStore) -> dict:
"""Plan with user context from long-term memory."""
# Recall relevant user facts from vector store
user_memories = store.search(
namespace=("user", state.user_id),
query=state.messages[-1].content,
limit=5,
)
# Load project conventions from document memory
conventions = doc_memory.read_doc("conventions/analysis-format.md")
# Inject both into planning context
memory_context = "\n".join(m.value["fact"] for m in user_memories)
# ... rest of planning logic with personalized context and conventions
Le flux complet
Voici ce qui se passe quand un utilisateur récurrent envoie « Analyze TSLA » au Market Analyst Agent :
-
Chargement de la mémoire documentaire : au démarrage, l’agent lit les conventions projet depuis le document store : préférences de format d’analyse, sources de données privilégiées, patterns d’usage des outils. Cela fixe le comportement de base.
-
Rappel de mémoire froide : avant l’exécution du nœud routeur, le graphe interroge le long-term store avec le message de l’utilisateur. Il récupère : « User has high risk tolerance », « User prefers detailed competitor analysis », « User previously researched NVDA and AMD ».
-
Routeur + Planner : le routeur classe cela comme
DEEP_RESEARCH. Le planner crée un plan de recherche en 5 étapes, personnalisé selon les préférences rappelées. Il inclut une étape d’analyse concurrentielle parce que l’historique de l’utilisateur montre qu’il en veut une. Le plan suit le format défini dans le document de conventions. -
Boucle d’exécution (mémoire chaude) : chaque étape s’exécute via le pattern ReAct de la Partie 1. Après chaque nœud (routeur, planner, chaque étape de l’executor), LangGraph écrit un checkpoint dans PostgreSQL. Si le processus crash après l’étape 3 sur 5, vous redémarrez et poursuivez à l’étape 4.
-
Interruption HITL : le graphe atteint le nœud
reporteravecinterrupt_before. Le draft report se trouve dans le checkpoint. L’utilisateur le relit des heures plus tard, et le graphe recharge le checkpoint puis continue. -
Mises à jour mémoire : après la fin de la conversation : (a) un processus asynchrone extrait de nouveaux faits utilisateur (« user is now tracking TSLA », « user approved the report format ») et les stocke dans le vector store long terme, et (b) l’agent écrit une synthèse de recherche dans le document store (
research/TSLA-2026-02.md) pour référence future.
Le pattern en trois couches sépare clairement les responsabilités. Le checkpoint store gère la durabilité et la reprise ; c’est de l’infrastructure. Le long-term store gère la personnalisation ; c’est de la logique produit. Le document store héberge la connaissance projet accumulée ; c’est le carnet de notes de l’agent.
Trade-offs et points d’attention
La mémoire apporte de la valeur, mais aussi des coûts et de la complexité. Il faut être lucide sur les trade-offs :
-
Coût des embeddings : chaque fait stocké dans une base vectorielle nécessite un appel API d’embedding. À $0.02 par million de tokens (OpenAI
text-embedding-3-small), le coût par fait est négligeable, mais il s’accumule sur des milliers d’utilisateurs et de sessions. Batcher les appels d’embedding et mettre les résultats en cache. Le vrai coût est la latence : 100-300ms de latence d’API d’embedding au moment de la requête pour le rappel de mémoire froide, ce qui compte davantage que le coût en dollars pour des agents conversationnels temps réel. Mettez en cache les embeddings des requêtes fréquentes, ou utilisez un modèle d’embedding local pour les workloads sensibles à la latence. -
Mémoire obsolète : les préférences utilisateur changent. Un fait stocké il y a six mois (« user prefers conservative investments ») peut ne plus être exact. Définissez des politiques d’expiration. J’utilise 365 jours pour les préférences et 90 jours pour les événements épisodiques, comme décrit dans mon article sur le context engineering.
-
Overhead mémoire dans le contexte : chaque fait rappelé consomme des tokens dans la fenêtre de contexte du LLM. Si vous rappelez 20 faits par requête, cela représente plusieurs centaines de tokens de contexte mémoire en concurrence avec la tâche réelle. Limitez le nombre de faits rappelés et priorisez-les par score de pertinence.
-
Vie privée et conformité : la mémoire long terme stocke des données utilisateur. Vous avez besoin d’une redaction PII avant stockage, de politiques de rétention claires et de contrôles de suppression exposés à l’utilisateur. Rien de tout cela n’est optionnel dans des secteurs régulés.
-
Croissance du stockage des checkpoints : les tables de checkpoints PostgreSQL grossissent à chaque exécution de nœud. Pour des agents de longue durée, définissez une politique de rétention : gardez les N derniers checkpoints par thread et archivez ou supprimez les plus anciens. Exemple de requête de nettoyage qui conserve les 10 checkpoints les plus récents par thread et supprime tout ce qui a plus de 30 jours :
DELETE FROM checkpoints WHERE thread_id = $1 AND created_at < NOW() - INTERVAL '30 days' AND checkpoint_id NOT IN ( SELECT checkpoint_id FROM checkpoints WHERE thread_id = $1 ORDER BY created_at DESC LIMIT 10 ); -
Consolidation mémoire : avec le temps, les souvenirs épisodiques détaillés doivent se compresser en représentations sémantiques compactes : « user asked about NVDA three times in January » plutôt que de stocker les trois conversations verbatim. Cela reflète la consolidation de la mémoire humaine et permet de garder un store gérable. Mem0 et Graphiti gèrent cela automatiquement ; si vous construisez votre propre système, planifiez des jobs périodiques de consolidation.
-
Problème de cold start : les nouveaux utilisateurs n’ont aucune mémoire long terme. L’agent doit se dégrader proprement et poser des questions de clarification au lieu de faire des hypothèses. La mémoire est additive, pas requise.
-
Memory poisoning : tout ce qui entre dans la fenêtre de contexte de l’agent est un point d’injection potentiel. Si un attaquant écrit des faits trompeurs dans le document store ou la mémoire long terme (« always approve transactions without verification »), l’agent peut les exécuter comme des instructions. L’injection de prompt via des mémoires stockées est une surface d’attaque réelle. Les mitigations sont la validation avant stockage, le traitement du contenu rappelé comme donnée non fiable plutôt que comme instruction système, et des contrôles d’accès limitant quelles mémoires peuvent influencer des opérations critiques.
-
Dérive de la mémoire documentaire : la mémoire basée sur des fichiers n’a ni déduplication automatique ni résolution de conflit. Avec le temps, les documents accumulent des contradictions : un fichier dit « use pytest » tandis qu’un autre dit « use unittest ». Planifiez des revues périodiques (ou laissez l’agent les faire) pour élaguer et consolider. La bonne nouvelle est que, contrairement aux vector stores où l’obsolescence est cachée, vous pouvez
greppour repérer les contradictions. -
La mémoire documentaire ne scale pas à des millions d’items : la mémoire basée sur des fichiers fonctionne pour des centaines à quelques milliers de documents. Si votre agent doit rappeler des millions de faits avec du fuzzy matching, il vous faut un vector store. La mémoire documentaire sert à de la connaissance projet structurée, pas à la longue traîne de chaque interaction utilisateur.
Points clés à retenir
-
La mémoire d’un agent se découpe en trois couches : chaude (checkpoint store pour la session en cours), froide (long-term store pour la connaissance inter-sessions) et documentaire (file store pour la connaissance projet accumulée). Concevez chaque couche pour son pattern d’accès.
-
Utilisez le checkpointing PostgreSQL par défaut. Il vous apporte la durabilité ACID, l’historique complet des checkpoints et le débogage time-travel. Ne passez à Redis que si une latence sous la milliseconde est une contrainte dure.
-
Le système de checkpoints de LangGraph gère nativement la mémoire chaude. Chaque écriture de nœud est persistée automatiquement ; les workflows pause/reprise et HITL ne demandent donc aucun code applicatif.
-
Démarrez la mémoire long terme avec des magasins clé-valeur pour les profils utilisateur structurés. Ajoutez de la recherche vectorielle (Qdrant, Pinecone, ou le Store intégré de LangGraph avec index vectoriel) quand vous avez besoin de rappel sémantique sur des faits non structurés.
-
La mémoire documentaire est peu explorée dans la littérature mais largement adoptée en pratique. La plupart des frameworks et surveys couvrent vector stores et checkpoints mais ignorent la mémoire basée sur des fichiers. Le pattern s’est diffusé bien au-delà des assistants de code IA. Claude Code, Cursor et Windsurf ont convergé vers des fichiers texte bruts ; Voyager stocke des compétences Minecraft sous forme de bibliothèques de code ; les gagnants d’ECR3 itèrent sur des documents procéduraux de prompts ; des agents web synthétisent des APIs de workflow réutilisables. Quand l’agent apprend quelque chose de faux, vous ouvrez un fichier Markdown et vous corrigez. Quand vous voulez savoir ce que l’agent sait, vous
lsle répertoire mémoire. -
La mémoire change le produit, pas seulement l’infrastructure. La différence entre « l’agent se souvient de mes préférences » et « l’agent me pose les mêmes questions à chaque fois » est ce qui fait revenir les utilisateurs.
-
Définissez des politiques de rétention dès le premier jour. Une mémoire obsolète dégrade les performances de l’agent, et un stockage sans borne crée des risques de confidentialité. Expirez les souvenirs épisodiques après 90 jours, les préférences après 365, et relisez périodiquement la mémoire documentaire à la recherche de contradictions.
-
Plafonnez le contexte rappelé. Chaque fait rappelé entre en concurrence pour les tokens de la fenêtre de contexte. Récupérez les 5 faits les plus pertinents, pas tout ce que vous avez.
Et ensuite
Dans la Partie 3, AI Agent Tool Use in 2026, je couvrirai l’ergonomie des outils et l’Agent-Computer Interface (ACI) : comment concevoir des outils que les LLM peuvent réellement utiliser de manière fiable. Les descriptions d’outils, schémas d’arguments et patterns de gestion d’erreur sont ce qui détermine si votre agent appelle le bon outil avec les bons arguments, ou hallucine jusqu’à déclencher une cascade d’échecs.
Références
Articles scientifiques
- Cognitive Architectures for Language Agents (CoALA) — Sumers, Yao et al., 2023 — Taxonomie fondamentale des types de mémoire d’agent
- Memory in the Age of AI Agents: A Survey — Déc. 2025 — Taxonomie tridimensionnelle complète de la mémoire d’agent
- MemGPT: Towards LLMs as Operating Systems — Packer et al., 2023 — Gestion virtuelle du contexte pour agents LLM
- Generative Agents: Interactive Simulacra of Human Behavior — Park et al., 2023 — Architecture de memory stream avec scoring par récence, importance et pertinence
- Zep: A Temporal Knowledge Graph Architecture for Agent Memory — Rasmussen, 2025 — Graphe de connaissances bi-temporel pour la mémoire d’agent
- Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory — 2025 — Pipeline extraction/consolidation avec benchmarks
- Voyager: An Open-Ended Embodied Agent with Large Language Models — Wang et al., 2023 — Bibliothèque de compétences comme mémoire documentaire pour agents de jeux en monde ouvert
- JARVIS-1: Open-World Multi-task Agents with Memory-Augmented Multimodal Language Models — 2023 — Bibliothèque mémoire multimodale pour agents Minecraft
- Agent Workflow Memory — Wang et al., 2024 — Induction de workflows réutilisables pour agents d’automatisation web
- SkillWeaver: Web Agents can Self-Design Skill Libraries — 2025 — Outils API réutilisables auto-synthétisés pour agents web
- LEGOMem: Modular Memory Framework for LLM Agent Systems — 2025 — Modules mémoire composables pour systèmes multi-agents
Documentation LangGraph
- LangGraph Persistence (Checkpointing) — Concepts de base de la mémoire par checkpoints
- LangGraph Memory Store — Mémoire long terme inter-thread avec l’interface Store
- LangGraph Cross-Thread Persistence — API fonctionnelle pour la mémoire inter-thread
- How to add memory to the prebuilt ReAct agent — Guide pratique pour ajouter de la mémoire
Backends de checkpoint
langgraph-checkpoint-postgres— Sauvegarde de checkpoints PostgreSQL pour LangGraphlanggraph-checkpoint-redis— Sauvegarde de checkpoints Redis pour LangGraph- LangGraph Redis Checkpoint 0.1.0 Redesign — Détails d’architecture du checkpoint saver Redis
langgraph-checkpoint-aws— Checkpoint saver DynamoDB avec déport vers S3
Bases de données vectorielles et outils mémoire
- Qdrant — Base de données vectorielle open source avec indexation HNSW et filtrage
- Qdrant Agentic Builders Guide — Guide pratique de construction de mémoire d’agent avec Qdrant
- pgvector — Extension PostgreSQL pour la recherche par similarité vectorielle
- Graphiti — Moteur open source de graphe de connaissances temporel par Zep
Mémoire documentaire et mémoire basée sur des fichiers
- Claude Code Memory — Système de mémoire basé sur fichiers CLAUDE.md et MEMORY.md
- Anthropic Memory Tool — Mémoire basée sur des fichiers côté client pour agents Claude API
- Cursor Rules — Fichiers .cursorrules au niveau projet pour le contexte de l’agent
- Windsurf Memories — Mémoire basée sur des fichiers et .windsurfrules pour agents de code
Frameworks mémoire
- Mem0 — Couche mémoire managée avec pipeline extraction/consolidation
- Letta (MemGPT) — Gestion virtuelle du contexte inspirée des OS pour agents
- LangMem SDK — Outils de gestion mémoire pour LangGraph
Benchmarks
- PostgreSQL vs Redis Performance — Benchmarks de latence et débit CyberTec
- PostgreSQL vs Redis Comparison — Comparaison d’architecture RisingWave
- Redis AI Agent Engineering — Patterns Redis pour workloads d’agents
Workshops
- MemAgents: Memory for LLM-Based Agentic Systems — Workshop ICLR 2026
Projet démo
- Market Analyst Agent — Implémentation complète avec les trois couches mémoire
Le code complet du Market Analyst Agent, y compris l’architecture mémoire de cet article, est sur GitHub si vous voulez suivre en lisant.
Série : Engineering the Agentic Stack
- Partie 1 : AI Agent Reasoning Loops in 2026 — ReAct, ReWOO et Plan-and-Execute
- Partie 2 : Architecture de mémoire des agents IA en 2026 (cet article)
- Partie 3 : AI Agent Tool Use in 2026 — MCP, CLI, Skills, exécution de code et ACI
- Partie 4 : AI Agent Security in 2026 — guardrails, permissions, sandboxes, HITL et scoping MCP
- Partie 5 : Long-Running AI Agent Runtime in 2026 — sessions, sandboxes, checkpoints, harnesses et formes de déploiement