Guide technique

Une mémoire d'agent qui se souvient vraiment : knowledge graphs et assemblage de contexte

Un guide technique approfondi sur la mémoire d'agent en production : fenêtres récentes, rappel sémantique, templates de working memory, knowledge graphs par tenant et assemblage de contexte en couches.

22 juillet 202620 min de lectureÉquipe d'Ingénierie Oronts

Le problème : des modèles sans état, des conversations avec état

Soyons directs : un modèle de langage n'a aucune mémoire. Chaque appel est une page blanche. L'illusion d'un système qui « se souvient de toi » est entièrement de l'ingénierie qui se passe en dehors du modèle, avant même que le prompt ne soit envoyé.

Rate cette ingénierie et tu obtiens les deux échecs classiques. Soit l'agent oublie ce que l'utilisateur a dit trois tours plus tôt, soit tu fourres tout l'historique de conversation dans chaque requête et tu regardes ta facture de tokens et ta latence exploser pendant que la pertinence chute. La vraie mémoire d'agent, c'est la discipline de décider, à chaque requête, exactement ce que le modèle doit voir et rien de plus.

La mémoire n'est pas un truc que tu actives. Ce sont quatre mécanismes différents avec des coûts différents et des rôles différents, assemblés dans une seule fenêtre de contexte au moment de la requête.

On fait tourner ça en production dans Exfinity, notre plateforme de commerce AI multi-tenant, où huit agents partagent une couche de mémoire et de connaissance à travers des milliers de conversations. Ce guide, c'est l'architecture réelle, pas un jouet. Pour la vue d'ensemble des systèmes agentiques, regarde notre guide des systèmes AI agentiques, et pour voir comment le retrieval s'y intègre, notre guide des systèmes RAG d'entreprise. C'est du travail au cœur de nos services AI.

Qui se soucie de quoi

RôleLa vraie questionÀ quoi ressemble le bon niveau
Ingénieur AIComment garder un contexte pertinent et pas cher ?Un assemblage en couches, pas un dump géant d'historique
ArchitecteComment la mémoire reste-t-elle isolée par tenant ?Des clés scopées, aucune fuite entre tenants
Product leadL'agent donne-t-il l'impression de connaître l'utilisateur ?Des préférences portées sans les redemander
CTOPeut-on opérer et auditer tout ça ?Un contexte traçable, un coût borné
DPOPeut-on effacer la mémoire d'un utilisateur sur demande ?Une suppression ciblée dans tous les stores

Quatre types de mémoire

Avant l'architecture, le vocabulaire. Ces quatre mécanismes font des jobs différents, et les systèmes en production les utilisent tous.

MécanismePortéeCoûtRôle
Fenêtre récenteThread courantBon marchéLes N derniers messages, mot pour mot
Rappel sémantiqueThread courantMoyenRamener les messages plus anciens similaires au présent
Working memoryThread courantBon marchéUn template structuré que l'agent met à jour
Knowledge graphEntre sessions, par tenantMoyenLes patterns qui tiennent au-delà d'un seul chat

L'erreur des juniors, c'est de traiter la mémoire comme un seul blob « historique de chat ». L'erreur que les seniors évitent, c'est de payer pour du contexte dont le modèle n'a pas besoin à ce tour.

Assemblage de contexte : les sept couches

Chaque requête dans Exfinity passe par un pipeline d'assemblage de contexte qui construit un tableau ordonné de messages injectés avant le texte de l'utilisateur. Chaque couche est conditionnelle. C'est le cœur du système, alors voici le pipeline complet.

CoucheSe déclenche quandSource
1. Règles de plateformeToujoursGuardrails statiques plus header de langue
2. Contexte de dateToujoursTimestamp courant pour les calculs de date
3. Message système du tenantLe tenant a configuré un persona ou un promptConfig tenant en cache
4. Insights du grapheLe graphe du tenant dépasse un seuil de nœudsKnowledge graph par tenant
5. CompactionLe thread dépasse un seuil de messagesRésumé structuré généré par le modèle
6. Contexte produitLa requête ressemble à une recherche produitAPI de recherche
7. Récupération de connaissanceRetrieval activé et la requête est une questionBase de connaissance du tenant

Deux décisions de design comptent ici plus que la liste elle-même.

D'abord, toutes les couches ne se déclenchent pas. La couche 4 ne tourne que quand le graphe d'un tenant a assez de nœuds pour être utile, et la couche 5 n'entre en jeu que quand un thread est assez long pour avoir besoin d'un résumé. Tu ne paies pas pour du contexte dont tu n'as pas besoin.

Ensuite, et c'est la décision de sécurité : les couches qui portent des données produites par d'autres utilisateurs ne sont jamais injectées comme instructions système. Les règles de plateforme et le message de l'utilisateur lui-même sont les seules voix qui font autorité. Les insights du graphe, l'historique compacté, les produits récupérés et les documents récupérés sont tous emballés comme données non fiables et injectés au rôle user, avec les délimiteurs de fence retirés du contenu pour que du texte injecté ne puisse pas s'échapper du bloc. On y revient dans la section guardrails.

User message
   │
   ▼
[1] Platform rules      (system, always)
[2] Date context        (system, always)
[3] Tenant persona      (system, if configured)
[4] Graph insights      (untrusted-data, if graph is big enough)
[5] Compacted history   (untrusted-data, if thread is long)
[6] Product context     (system, if product-like query)
[7] Knowledge passages  (untrusted-data, if question-like query)
   │
   ▼
Assembled context ──▶ Agent ──▶ Model

Working memory : le template dans lequel l'agent écrit

Porter les messages récents pour toujours coûte cher. La working memory résout ça avec un template structuré que l'agent lit et écrit d'un tour à l'autre. Au lieu de relire « j'ai 200 euros pour deux adultes samedi prochain » depuis l'historique brut à chaque tour, l'agent l'extrait une fois dans un template et porte le template.

<user_context>
preferences:
budget:
travel_dates:
group_size:
interests:
viewed_products:
current_intent:
</user_context>

Quand l'utilisateur donne un budget et des dates, l'agent écrit ces valeurs dans les champs. Au tour suivant, le template se recharge dans le contexte, donc l'agent se souvient sans que les messages d'origine soient présents. C'est dramatiquement moins cher que de porter tout l'historique, et ça survit à la compaction. Les agents qui n'ont pas besoin d'un schéma rigide gardent à la place un bloc de working memory libre.

La règle de design : mets les faits durables et structurés dans la working memory, et laisse la fenêtre de messages bruts gérer l'aller-retour immédiat. Notre guide du design de workflows AI couvre comment décider ce qui va où.

Rappel sémantique versus fenêtre récente

Deux mécanismes couvrent l'historique de conversation, réglés par agent.

La fenêtre récente, ce sont les N derniers messages, chargés mot pour mot. Bon marché et toujours pertinent pour l'échange immédiat.

Le rappel sémantique transforme le message courant en embedding et cherche dans l'historique plus ancien du thread des messages passés sémantiquement similaires, en ramenant quelques correspondances plus les messages autour de chacune. C'est comme ça qu'un agent se rappelle quelque chose que l'utilisateur a dit vingt tours plus tôt quand ça redevient pertinent, sans porter les vingt tours.

Chaque agent règle ces deux mécanismes indépendamment. Un agent de réservation peut garder une fenêtre récente courte et un rappel peu profond parce que les réservations sont du court terme. Un agent de recommandation garde un rappel plus profond parce que les goûts s'expriment sur un arc plus long. Le point clé : les réglages de mémoire sont par rôle, pas globaux. Notre guide d'architecture multi-agents couvre comment les agents spécialisés divergent dans leur configuration.

// Config mémoire par agent : fenêtre récente + rappel sémantique
const bookingAgent = {
  memory: {
    lastMessages: 15,                       // fenêtre récente mot pour mot
    semanticRecall: { topK: 3, messageRange: 2 }, // passé similaire + voisins
    workingMemory: { enabled: true },
  },
};

const recommendationAgent = {
  memory: {
    lastMessages: 15,
    semanticRecall: { topK: 5, messageRange: 3 }, // rappel plus profond
    workingMemory: { enabled: true },
  },
};

Le knowledge graph : la mémoire qui traverse les sessions

Tout ce qui précède est limité à une conversation. Le knowledge graph, c'est la mémoire qui survit à la session et qui tient les patterns à travers toutes les interactions d'un tenant. C'est la couche que la plupart des équipes sautent, et c'est là que le contexte cesse d'être « ce que tu viens de dire » pour devenir « ce qui tend à être vrai ici ».

Le graphe est par tenant, avec des nœuds typés et des arêtes typées.

Les types de nœuds incluent product, query, response, user, document, tool call, booking et competitor price. Les types d'arêtes incluent searched-for, answered-by, booked, viewed, cited, similar-to, asked-about, used-tool et priced-against.

query ──searched_for──▶ product
query ──answered_by──▶ response ──cited──▶ document
user  ──booked──▶ product
user  ──viewed──▶ product
product ──similar_to──▶ product

Chemin d'écriture. Après chaque réponse de l'agent, un job fire-and-forget enregistre l'interaction : un nœud query avec le message utilisateur nettoyé des données personnelles, un nœud response avec la réponse nettoyée, une arête answered-by pondérée par le feedback (un pouce en haut la renforce, un pouce en bas l'affaiblit), et un nœud product plus une arête searched-for pour chaque produit mentionné. Ça tourne en asynchrone, donc ça n'ajoute jamais de latence à la réponse.

Chemin de lecture. Pendant l'assemblage de contexte (couche 4), le graphe ne produit des insights qu'une fois qu'il a assez de nœuds pour être significatif. Deux requêtes tournent en parallèle : les produits les plus recherchés, et les questions récentes sans bonne réponse. Ça devient un bloc compact de données non fiables qui dit à l'agent ce qui intéresse vraiment les utilisateurs de ce tenant.

// Chemin de lecture : ne montrer les insights que quand le graphe est substantiel
async function buildGraphInsights(tenantId) {
  const stats = await graph.getStats(tenantId);
  if (stats.totalNodes < GRAPH_MIN_NODES_FOR_INSIGHTS) return null;

  const [popular, unanswered] = await Promise.all([
    graph.getPopularProducts(tenantId, 5),      // les plus recherchés
    graph.getUnansweredQuestions(tenantId, 3),  // les trous à combler
  ]);
  return fenceUntrusted(formatInsights(popular, unanswered));
}

Empêcher la croissance infinie. Deux jobs de maintenance comptent. La décroissance d'arêtes applique une décroissance exponentielle basée sur l'âge aux poids des arêtes, pour que les vieilles interactions perdent en pertinence, et elle recalcule depuis le poids initial à chaque exécution, donc elle est idempotente au lieu de se cumuler. Le purge de nœuds supprime les nœuds transitoires au-delà d'un seuil et cascade vers les arêtes orphelines, pendant que les nœuds durables comme les produits persistent.

weight = initial_weight * exp(-ln(2) / halfLifeDays * ageDays)   # floored at 0.1

Effacement. Pour une demande de suppression GDPR, le graphe résout les nœuds query d'un utilisateur par thread, suit les arêtes answered-by jusqu'à leurs nœuds response, et supprime ces nœuds plus chaque arête qui les touche. Les nœuds product partagés ne sont jamais effacés. La suppression ciblée à travers les stores de mémoire est une exigence dure, pas un bonus, et on couvre le côté conformité dans notre guide GDPR et AI.

Confiance et auto-learn : une mémoire qui s'améliore

Une mémoire qui ne fait qu'accumuler est un passif. Une mémoire qui s'améliore est un actif. Le pont entre les deux, c'est un score de confiance sur chaque réponse et une boucle de revue humaine qui transforme les réponses à faible confiance en nouvelle connaissance.

La confiance est un composite de quatre facteurs, pondérés.

FacteurPoidsCe qu'il mesure
Retrieval40 %Les appels d'outils ont-ils renvoyé des résultats utiles ?
Policy20 %Des avertissements de validation sur la réponse ?
Complétude20 %La réponse est-elle substantielle, pas un stub ?
Mémoire20 %Y a-t-il assez de profondeur de conversation pour l'ancrage ?
const composite =
  retrieval * 0.4 + policy * 0.2 + completeness * 0.2 + memory * 0.2;

Quand une réponse score sous un seuil défini par tenant, elle est capturée comme candidate d'apprentissage et classée dans une classe d'échec : retrieval raté, blocage de policy, un signal d'hallucination comme une date passée présentée comme future, un feedback négatif, et ainsi de suite. Un humain revoit la candidate dans un dashboard. À l'approbation, la réponse est formatée en document de connaissance, transformée en embeddings et republiée dans la base de connaissance, pour que la même question récupère une bonne réponse la prochaine fois. Le seuil s'auto-ajuste même : si les reviewers approuvent presque tout, il baisse pour capturer plus ; s'ils rejettent beaucoup, il monte pour couper le bruit.

C'est le pattern human-in-the-loop appliqué à la couche mémoire elle-même, et c'est ce qui sépare un système qui se dégrade d'un système qui capitalise. Notre guide d'observabilité AI couvre les métriques à surveiller pour savoir lequel des deux tu as.

Guardrails : prompt injection et données non fiables

Voici le mode d'échec que la mémoire introduit : du texte qu'un utilisateur a stocké, ou qui a survécu à la compaction, peut essayer de détourner la session d'un autre utilisateur. Si le contenu récupéré ou les insights du graphe étaient injectés comme instructions système, un document malveillant pourrait dire « ignore tes règles » et être obéi.

La défense, c'est une séparation stricte. Seules les règles de plateforme et la requête de l'utilisateur final font autorité. Tout le reste, passages récupérés, données produit, insights du graphe, historique compacté, est déclaré donnée non fiable et injecté au rôle user à l'intérieur d'une fence, avec les délimiteurs de fence retirés du contenu pour que le texte injecté ne puisse pas fermer le bloc en avance et sembler s'échapper. C'est traité comme de l'information à considérer, jamais comme des commandes à suivre.

Ça compte de plus en plus à mesure que la mémoire grandit, parce que la surface de « texte que d'autres ont fait exister » grandit avec elle. Nos guides gouvernance AI et modes d'échec AI vont plus loin sur la défense des systèmes agentiques.

Coût : la mémoire n'est pas gratuite

Chaque couche que tu ajoutes, ce sont des tokens, et les tokens, c'est de la latence et de l'argent. Trois contrôles gardent ça borné.

La compaction résume les messages plus anciens quand un thread devient long, en remplaçant l'historique brut par un résumé structuré compact et en gardant le budget de tokens plat quand les conversations grandissent. Le prompt caching sert le préfixe système statique depuis le cache du provider sur les appels répétés, donc tu ne repaies pas les mêmes règles à chaque tour. Et des plafonds durs par tour sur les étapes de raisonnement, les appels d'outils et les tokens de sortie empêchent un seul tour de partir en vrille.

ContrôleEffet
CompactionBudget de tokens plat sur les threads longs
Prompt cacheLe préfixe statique n'est pas refacturé à chaque tour
Plafonds par tourPas de boucles qui s'emballent
Plafond de coût par tenantFilet de sécurité contre les abus

On couvre les trade-offs de coût et de latence sur toute la stack dans notre guide latence et précision AI.

Par où commencer

Ajouter de la mémoire à un agent, par ordre de retour sur effort :

  1. La fenêtre récente d'abord. Porte les N derniers messages. Ça seul rend un agent cohérent.
  2. La working memory ensuite. Ajoute un template structuré pour les faits durables. Pas cher, gros impact.
  3. Le rappel sémantique quand les threads s'allongent. Ramène les messages anciens pertinents au lieu de tous les porter.
  4. La compaction quand le coût fait mal. Résume le vieil historique une fois les threads longs.
  5. Un knowledge graph quand tu as du volume. Ça ne vaut le coup qu'une fois que tu as assez d'interactions pour que des patterns émergent.

Ne construis pas le graphe en premier. Il ne mérite sa place qu'à volume. Notre méthodologie est construite autour de ce genre de livraison par étapes, et nos équipes logiciel sur mesure et conseil construisent ces couches avec les clients. Commence par contact ou demande un devis.

Les façons classiques de se planter

  1. Dumper tout l'historique à chaque tour. Coût et latence explosent, la pertinence chute. Assemble, ne dumpe pas.
  2. Des réglages de mémoire globaux. Des agents différents ont besoin de fenêtres et de rappels différents. Règle par rôle.
  3. Injecter le texte récupéré comme instructions système. C'est un trou de prompt injection. Fence-le comme donnée non fiable.
  4. Un graphe sans décroissance ni purge. Il grossit sans limite et le vieux bruit noie le signal. Ajoute des jobs de maintenance.
  5. Pas de chemin d'effacement. Si tu ne peux pas supprimer la mémoire d'un utilisateur, tu ne peux pas répondre à une demande GDPR. Prévois la suppression dès le design.
  6. Accumuler sans améliorer. Ajoute un score de confiance et une boucle de revue, sinon le système devient juste plus gros, pas meilleur.

Qui construit ça

Oronts est une société de logiciel dirigée par son fondateur, basée à Munich. Refaat Al Ktifan, notre fondateur et architecte solution, mène une équipe senior en backend, data et AI. L'architecture de ce guide est la vraie couche mémoire derrière Exfinity, qu'on fait tourner contre nous-mêmes avant d'apporter ces patterns aux clients. On conçoit le pipeline de contexte, on construit les couches mémoire et graphe, et on te livre un système que tu peux opérer et auditer. Regarde nos pages services et solutions pour l'image complète.

À retenir

  • Un modèle n'a pas de mémoire. Tout est de l'assemblage qui se passe avant le prompt.
  • Utilise les quatre mécanismes : fenêtre récente, rappel sémantique, working memory et un knowledge graph.
  • Assemble le contexte en couches conditionnelles, et n'injecte jamais les données d'autres utilisateurs comme instructions système.
  • Un knowledge graph donne une mémoire qui traverse les sessions, mais il lui faut de la décroissance, du purge et un chemin d'effacement.
  • Ajoute un score de confiance et une boucle de revue humaine pour que la mémoire s'améliore au lieu de juste accumuler.

La meilleure mémoire d'agent est invisible. L'utilisateur se sent compris, la facture de tokens reste plate, et un régulateur peut regarder les données d'un utilisateur se faire supprimer sur demande. C'est de l'ingénierie, pas de la magie.

Tu construis un agent qui doit se souvenir, de façon sûre et pas chère ? Parle-nous-en via contact ou demande un devis cadré.

Sujets couverts

mémoire dagentknowledge graphcontext engineeringassemblage de contexteworking memoryrappel sémantiquecompaction de conversationAI agentiquemémoire LLMAI multi-tenant

Vous construisez quelque chose de similaire ?

Nous concevons et exploitons des systèmes de production comme celui de ce guide. Parlez aux ingénieurs qui l'ont écrit, sans argumentaire commercial.

Démarrer une conversation