Explication de la mémoire de l’agent : fenêtre de contexte, RAG et état persistant
La mémoire de l’agent comprend trois éléments distincts. Examinez en quoi la fenêtre de contexte, le récupération d’informations et l’état persistant diffèrent, quel est le coût de chacun, et comment choisir le bon en premier.
Sources: Agent Memory Explained
Peu de mots provoquent autant de confusion dans le développement des agents que le terme « mémoire ». Lorsque quelqu’un affirme que son agent en possède une, cela peut signifier que la conversation est longue, qu’une base de données vectorielle est connectée, ou que les informations sont enregistrées dans un fichier qui survit à une redémarrage. Il s’agit de trois systèmes distincts, avec des coûts différents, des modes de défaillance variés et des raisons d’existence différentes.
Une seule phrase suffit à dissiper la plupart des malentendus. Le modèle n’a pas de mémoire, seulement une entrée, et ce que les gens appellent mémoire n’est en réalité qu’une politique permettant de choisir ce qui sera pris en compte dans cette entrée.
Le modèle est sans état. Chaque appel API commence à zéro, sans aucun souvenir du dernier message, de la dernière session ou du nom que vous lui avez donné. Ce qu’il connaît pendant un appel, c’est uniquement ce que contient la requête, et rien d’autre.
So the useful question isn't whether the agent has memory. It's what earns a place in this request, and where it comes from. There are three answers, and the job is to tell them apart.
Stateless model, stateful application
Newcomers assume the model is holding a conversation. It isn't. The application holds the conversation and replays it. When you send a fourth chat message, your code transmits messages 1, 2, 3 and 4, all of them, every time. The feeling of continuity comes from resending history, and the model simply receives a longer input than before.
With that in mind, the three layers stop being abstract:
- The context window is what sits in the request right now.
- Retrieval (RAG) is how you find material to put in the request.
- État persistant : c’est le mécanisme qui permet aux informations de survivre à une requête afin de pouvoir être réutilisées ultérieurement.
Un conteneur, une recherche et un stockage : trois éléments distincts. Les confondre revient à intégrer une base de données vectorielle dans un problème que un fichier preferences.json aurait pu résoudre.
Niveau 1 : la fenêtre, ou le bureau du modèle
Imaginez la fenêtre comme tout ce que le modèle peut voir actuellement. L’instruction système, l’historique, les définitions des outils, leurs résultats, les documents récupérés ainsi que la réponse en cours d’écriture s’appuient tous sur un budget de tokens commun.
Imaginez un bureau : tout ce dont le modèle doit tenir compte doit se trouver physiquement dessus, car un classeur dans une autre pièce ne compte pas.
Son point fort : l’immédiateté. Tout ce qui se trouve dans la fenêtre est complètement visible sans nécessiter de recherche supplémentaire, et le modèle peut en prendre en compte l’ensemble en même temps. Pour une tâche avec un ensemble de données limité et connu, remplir la fenêtre représente la méthode la plus simple et souvent la meilleure.
Coûts :
- L’argent et le temps de réponse dépendent de ce que vous renvoyez. Renvoyer 51 messages d’une longue conversation signifie également renvoyer 50 messages de l’historique. Un contexte long n’est pas gratuit simplement parce qu’il tient dans la fenêtre.
- L’attention est inégale. Les modèles ont tendance à utiliser de manière la plus fiable les parties débutantes et finales d’une entrée longue, tandis que le milieu est moins utilisé, ce qui correspond à l’effet bien connu du « perdu au milieu ». Un élément situé à 60 % de la longueur d’une prompt très longue est présent mais risque de ne jamais être réellement utilisé. Être inclus dans la fenêtre ne signifie pas forcément être lu.
Toute cette mécanique se résume en quelques lignes :
messages = []
def turn(user_text):
messages.append(
{"role": "user",
"content": user_text}
)
# every call resends
# the ENTIRE history
reply = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=messages,
)
messages.append(
{"role": "assistant",
"content": reply.content[0].text}
)
return reply
# messages grows forever.
# so does your bill.
Cette liste constitue ici la « mémoire ». Elle n’existe que à l’intérieur de votre processus, donc une fois ce dernier terminé, la liste disparaît également.
La fenêtre elle-même suffit lorsque la tâche est brève, que les données nécessaires sont limitées et connues à l’avance, et qu’aucun élément n’a besoin de survivre au-delà de la session. Un résuméur, un éditeur pour un seul fichier ou une extraction ponctuelle n’ont besoin de rien de plus. Ne rajoutez pas de complexités inutiles.
Niveau 2 : récupération, la bibliothèque de référence
Tôt ou tard, les données deviennent trop volumineuses ou ne valent plus la peine d’être payées. Il s’agit peut-être de 10 000 tickets d’aide, d’un wiki interne ou d’une base de code. Vous ne pouvez pas inclure tout cela dans chaque demande, et vous ne le devriez d’ailleurs pas.
La génération assistée par récupération divise le travail : on prépare un index à l’avance, puis on y cherche des informations lorsque une question se pose.
Auparavant, vous divisez les documents en fragments, vous convertissez chaque fragment en un embedding (un vecteur numérique qui capture le sens) et vous le stockez. Lorsqu’une question arrive, vous l’embeddez, vous trouvez les fragments dont les vecteurs sont les plus proches et vous collez ces quelques-uns dans la fenêtre de contexte à côté de la question.
# 1. AHEAD OF TIME (once, offline)
for chunk in split(documents):
store.add(
text=chunk,
vector=embed(chunk),
)
# 2. AT QUESTION TIME (every request)
def answer(question):
hits = store.search(
embed(question), top_k=5
)
context = "\n\n".join(h.text for h in hits)
return client.messages.create(
model="claude-opus-5",
max_tokens=1024,
# retrieved chunks land in the same request
messages=[{
"role": "user",
"content": f"{context}\n\nQ: {question}",
}],
)
Remarquez ce qui s’est réellement passé. RAG n’a pas fourni de mémoire au modèle ; il vous a plutôt donné une stratégie de sélection pour la fenêtre de données. Les fragments récupérés sont inclus dans la même requête que tout le reste, ce qui fait de RAG un canal menant à la Couche 1 et aucune alternative n’existe.
Avantages : échelle et actualité. Des millions de documents peuvent changer indépendamment de votre code, tandis que le modèle ne voit que les quelques-uns qui sont pertinents pour cette question.
Coûts :
- Le résultat est déterminé tôt. La taille des fragments, le modèle d’incorporation et la conception de l’index sont fixés dès la création de l’index. Un fait réparti sur deux fragments, ou formulé différemment de la manière dont les utilisateurs posent leur question, ne sera tout simplement pas retrouvé, et le modèle répondra alors avec assurance en se basant sur ce qu’il a reçu. Pour un observateur, une mauvaise récupération des données et des hallucinations ont l’air identiques.
La récupération d’informations convient lorsque la base de connaissances est volumineuse, commune à tous les utilisateurs et peu sujette aux changements, comme par exemple la documentation, les politiques, les catalogues de produits et le code. Le signe distinctif est qu’un seul corpus sert tous les utilisateurs. Si ce que vous souhaitez mémoriser appartient à un utilisateur unique et change au fil de la conversation, la récupération d’informations n’est pas adaptée et c’est alors le niveau 3 qui est nécessaire.
Niveau 3 : état persistant, le carnet
Un état persistant est ce qui survit à la fin du processus. Il ne s’agit pas de ce que l’agent sait sur le monde, mais de ce qu’il a appris et qui doit durer au-delà de cette session : les préférences de l’utilisateur, les points de contrôle des tâches, les décisions déjà prises, les tentatives abandonnées, ainsi que des résumés synthétisés des exécutions précédentes. Il s’agit de faits petits, spécifiques et précieux que l’agent lui-même a enregistrés.
Le schéma repose sur un cycle de lecture, de modification et d’écriture qui entoure toute la session :
# session start: load
with open("state.json") as f:
state = json.load(f)
system = (
"Known about this user:\n"
f"{json.dumps(state)}"
)
messages = []
def turn(user_text):
messages.append(
{"role": "user",
"content": user_text}
)
reply = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
system=system, # state in
messages=messages,
)
return reply
# ... along the way ...
state["prefers"] = "metric units"
state["last_checkpoint"] = "step_4_done"
# session end: save
with open("state.json", "w") as f:
json.dump(state, f)
D’un point de vue conceptuel, rien d’autre n’est nécessaire. Un fichier JSON constitue une véritable implémentation ; de même qu’une ligne dans une base de données, un stockage clé-valeur ou un dossier contenant des notes en markdown. Le support est la partie peu intéressante, tandis que ce qui compte, c’est de savoir qui décide quoi enregistrer et quoi recharger.
Faire ce choix correctement est la partie difficile, et c’est là que les frameworks d’agents diffèrent. L’option actuelle la plus directe confie cette tâche au modèle : avec l’outil de mémoire d’Anthropic, Claude dispose d’un petit ensemble de commandes de fichiers (afficher, créer, str_replace, insérer, supprimer et renommer) qui fonctionnent dans un répertoire isolé /memories ; ainsi, l’agent choisit ce qui mérite d’être enregistré et lit ces notes au début de la session suivante. Votre propre application exécute les opérations sur le support de stockage que vous avez choisi. L’outil s’exécute du côté client, ce qui permet de garder les fichiers sous votre contrôle.
Points forts : continuité, personnalisation et travail à long terme. Un agent qui reprend une tâche sur plusieurs jours là où elle s’est arrêtée a besoin de cela, tout comme un agent qui ne devrait pas avoir à demander votre fuseau horaire à deux reprises.
Coûts :
- Quelqu’un doit assurer la gestion. Si vous notez tout, vous créez un texte transcriptible mais impossible à rechercher. Si vous écrivez trop peu, la continuité est perturbée. Un état persistant fiable est très difficile à mettre en place.
- Des informations obsolètes sont pires que rien du tout. Une préférence enregistrée il y a huit mois et jamais revue est appliquée avec une confiance totale. Les échecs de récupération entraînent de l’ambiguïté, tandis qu’un état obsolète provoque des comportements incorrects de manière certaine.
- Le chargement n’est pas gratuit. Quoi que vous chargiez, cela occupe toujours de l’espace dans la fenêtre, donc le niveau 3 alimente également le niveau 1.
Recourir à un état persistant si le même utilisateur ou tâche déclenche à plusieurs reprises l’agent, et ce que l’exécution n apprend doit permettre de modifier l’exécution n+1.
Les trois approches côte à côte
| Fenêtre de contexte | Récupération | État persistant | |
|---|---|---|---|
| Rôle | Le bureau | La bibliothèque | Le carnet |
| Contenu | Tout ce qui figure dans cette demande | Un corpus important partagé | De petits faits écrits par l’agent |
| Auteur | Votre application, à chaque appel | Tâche d’indexation | L’agent ou votre code |
| Non | Oui | Oui | |
| Risque principal | Cost, attaque de type « lost-in-the-middle » | ||
| Champs manquants | |||
| Faits obsolètes |
La ligne la plus importante est celle concernant les redémarrages. C’est la frontière entre une conversation longue et un agent doté de mémoire.
Ils composent
Un véritable agent utilise les trois éléments, et il est utile de les observer en action dans une seule demande. Un agent de support se voit demander si une commande remplit les conditions pour bénéficier d’un délai de retour prolongé.
- L’état persistant est chargé en premier : ce client dispose du plan d’affaires et préfère des réponses brèves. Deux faits, environ quarante tokens.
- La récupération d’informations s’exécute en fonction de la question et extrait la section relative à la politique de retour pour les plans d’affaires, ainsi que les deux dernières exceptions. Trois blocs, environ mille deux cents tokens.
- La fenêtre de contexte intègre les deux éléments précédents, ainsi que la conversation en temps réel et les définitions des outils ; le modèle raisonne alors sur l’ensemble de ces informations ensemble.
Une demande, trois niveaux. La fenêtre est l’endroit où tout se retrouve, tandis que la récupération des informations et l’état persistant en sont deux voies distinctes.
C’est aussi pour cette raison que l’on conseille généralement aux agents à long terme d’associer la mémoire à l’édition de contexte : les résultats des anciennes étapes sont effacés de la fenêtre pour la garder légère, et les faits importants ont déjà été enregistrés, ce qui permet à la fenêtre de rester petite sans perte de connaissances.
Lequel avez-vous vraiment besoin ?
Les débutants choisissent presque toujours d’abord une base de données vectorielle. C’est la partie la plus visible de l’ensemble, mais ce n’est pas toujours la solution. Montez plutôt les échelons.
Commencez par la fenêtre. Tout ce qui est pertinent tient-il dans une seule requête au prix que vous souhaitez ? Alors arrêtez-vous. Pas d’index, pas de stockage.
Intégrez l’état persistant une fois que l’agent s’exécute une deuxième fois. Lorsque la deuxième session doit connaître ce que la première a appris, un niveau 3 est nécessaire, et un fichier JSON constitue une solution adaptée. Faites-le avant la récupération des données ; c’est moins cher, plus simple, et c’est ce que la plupart des gens veulent vraiment lorsqu’ils disent souhaiter que l’agent se souvienne d’eux.
Intégrez la récupération lorsque le corpus est plus grand que la fenêtre. Cela signifie de nombreux documents partagés entre utilisateurs, modifiés séparément de votre code. C’est le niveau le plus complexe à mettre en place et à maintenir en fonctionnement, donc n’adoptez-le que lorsque c’est vraiment nécessaire.
Un piège à éviter : créer un pipeline RAG pour ce qui n’était en réalité qu’un fichier de préférences, puis se demander pourquoi l’agent oublie encore le nom de quelqu’un. La recherche dans un corpus ne peut jamais conserver l’information selon laquelle une personne préfère les unités métriques.
La version courte
- Fenêtre de contexte : le bureau. Visible instantanément, coûteux par token, et effacé dès la fermeture de la session.
- Récupération : la bibliothèque. Vaste, consultable, mise à jour selon un calendrier prédéfini, et que l’agent ne fait qu’analyser.
- État persistant : le carnet. De petite taille, rédigé par l’agent, et seul élément qui transmet une leçon d’aujourd’hui à demain.
Le modèle lui-même ne conserve rien. La mémoire apparente est un choix technique pris à chaque demande : quels éléments méritent une place dans cette fenêtre, et d’où doivent-ils provenir ?
Pour en savoir plus, les articles techniques d’Anthropic sur l’ingénierie du contexte efficace et sur la création d’agents performants, ainsi que sa documentation relative à l’outil de mémoire et à l’édition du contexte, abordent ces sujets en plus grande profondeur.