Trois architectures de mémoire testées sur des conversations multi-agents. Le graphe de contexte écrase les autres avec 88,9 % de précision contre 61,1 % pour l'historique brut.
Un système multi-agents qui oublie une décision prise vingt tours plus tôt, c’est comme un groupe de copains qui ne se souvient plus qui a commandé la pizza. Pourtant, le dialogue est toujours là, sous leurs yeux. La raison ? Une faille structurelle dans la façon dont les IA stockent et retrouvent l’information.
Pour comprendre ce problème, un développeur a construit trois architectures de mémoire différentes et les a testées sur des conversations réelles entre agents. Résultat : une solution graphique bat largement les méthodes classiques. Mais attention, ce n’est pas un simple problème de bruit ou de fraîcheur des données. C’est une question de structure.
LE PROBLÈME QUI A TOUT DÉCLENCHÉ : UN AGENT QUI OUBLIE SES PROPRES DÉCISIONS
Tout a commencé avec un pipeline multi-agents pour EmiTechLogic. Le système fonctionnait parfaitement pour des tâches courtes. Mais dès que la conversation s’allongeait, les agents perdaient le fil. Prenez cet exemple : l’Agent_Planner décide d’utiliser PostgreSQL pour un projet. Vingt tours plus tard, l’Agent_Reviewer demande quelle technologie de stockage a été choisie. Malgré l’historique complet dans la fenêtre de contexte, l’agent ne parvient pas à répondre de manière fiable.
Au début, le développeur a cru à une limitation des modèles. Mais non. Le problème venait de l’architecture de mémoire. Deux solutions classiques existent pour stocker les conversations :
- L’historique brut : tout est enregistré sous forme de texte brut, tour après tour.
- Le RAG vectoriel : les tours sont transformés en vecteurs et stockés dans une base de données pour une Recherche par similarité.
Chacune de ces méthodes a ses forces… et ses failles structurelles. Plutôt que de choisir au hasard, le développeur a décidé de les mesurer.
TROIS ARCHITECTURES, UN SEUL BENCHMARK : LE TEST ULTIME
Pour évaluer ces architectures, cinq scénarios déterministes ont été créés, avec 18 questions notées. Chaque architecture a été testée sur les mêmes conversations. Résultat ? Une différence abyssale.
Le graphe de contexte a atteint 88,9 % de précision avec seulement 26,9 tokens par requête. L’historique brut a fait 61,1 % de bonnes réponses, mais pour un coût de 490,9 tokens par requête. Le RAG vectoriel a obtenu 50 % de précision avec 75,9 tokens par requête.
Le graphe de contexte ne se contente pas d’être plus précis. Il est aussi beaucoup plus efficace en termes de tokens. Et surtout, il résout un problème que les autres méthodes ne peuvent pas traiter : les questions nécessitant de combiner deux faits distincts.
COMMENT FONCTIONNENT CES ARCHITECTURES ?
Commençons par l’historique brut. Chaque tour de conversation est ajouté à une liste de texte. Pour répondre à une question, l’IA reçoit l’intégralité de l’historique et doit extraire la réponse. C’est simple, mais coûteux en tokens. Plus la conversation grandit, plus le nombre de tokens explose.
class RawHistoryDump:
def ingest(self, turn: Turn) -> None:
self.transcript.append(f"{turn.speaker}: {turn.text}")
def answerquery(self, queryturn: Turn) -> tuple[str, int]:
prompt = self.buildprompt(query_turn) # L'ENTIER historique
tokens = count_tokens(prompt)
answer = self.extractanswer(query_turn)
return answer, tokens
Le RAG vectoriel fonctionne différemment. Chaque tour est transformé en vecteur via TF-IDF (une méthode de calcul de similarité entre mots). Pour une question, l’IA récupère les k chunks les plus similaires et les utilise pour répondre. Le problème ? Si la réponse nécessite de combiner deux faits situés dans deux chunks différents, le RAG vectoriel n’a aucun moyen de le faire.
class VectorOnlyRAG:
def retrieve(self, querytext: str) -> list[str]:
if not self.chunks:
return []
corpus = self.chunks + [query_text]
vectorizer = TfidfVectorizer()
matrix = vectorizer.fit_transform(corpus)
sims = cosine_similarity(matrix[-1], matrix[:-1]).flatten()
topidx = sims.argsort()[::-1][:self.topk]
return [self.chunks[i] for i in top_idx if sims[i] > 0]
Le graphe de contexte, lui, stocke les faits sous forme de triplets (sujet, prédicat, objet). Par exemple : ("Projet_Alpha", "UTILISE", "PostgreSQL"). Ces triplets forment un graphe où les nœuds sont les entités et les arêtes sont les relations entre elles. Pour répondre à une question nécessitant deux faits, le graphe parcourt les relations pour construire la réponse.
class ContextGraph:
def ingest(self, turn: Turn) -> None:
if turn.subject is None:
return # Les distracteurs ne sont pas stockés
self.graph.add_node(turn.subject)
self.graph.add_node(turn.object)
self.graph.add_edge(turn.subject, turn.object,
predicate=turn.predicate, factid=turn.factid)
POURQUOI LES QUESTIONS À DEUX FAITS POSENT PROBLÈME ?
Imaginons une question comme : « Quelle équipe possède le composant qui dépend du service que X a choisi ? » Cette question ne peut pas être répondue par un seul chunk de texte. La réponse n’existe que sous forme de chemin à travers plusieurs faits.
L’historique brut ne peut pas construire ce chemin à la volée. Le RAG vectoriel non plus. Mais le graphe de contexte, lui, peut parcourir les relations pour trouver la réponse. C’est sa force.
DEUX BUGS MAJEURS DÉCOUVERTS EN COURS DE ROUTE
Le développeur a découvert deux problèmes majeurs en construisant ce graphe. Le premier concerne la gestion des faits obsolètes. Si un fait est mis à jour, l’ancien reste dans le graphe. Par exemple :
Ticket4471 --HASPRIORITY--> "high" # Stated first
Ticket4471 --HASPRIORITY--> "critical" # Stated later, supersedes the first
Les deux arêtes existent en même temps. Le graphe ne sait pas laquelle est la bonne. Résultat : il peut retourner « high » alors que la priorité actuelle est « critical ».
La solution ? Supprimer l’ancienne arête avant d’ajouter la nouvelle.
def ingest(self, turn: Turn) -> None:
if turn.subject is None:
return
self.graph.add_node(turn.subject)
self.graph.add_node(turn.object)
stale_edges = [
(u, v, k) for u, v, k, data in self.graph.edges(keys=True, data=True)
if u == turn.subject and data.get("predicate") == turn.predicate
]
for u, v, k in stale_edges:
self.graph.remove_edge(u, v, key=k)
self.graph.add_edge(turn.subject, turn.object,
predicate=turn.predicate, factid=turn.factid)
Le second bug est un problème de correspondance d’entités. Si une question utilise un terme différent de celui stocké dans le graphe, la récupération échoue. Par exemple :
- Le graphe stocke : « Module_Authentification »
- La question demande : « ce module »
Résultat : aucune correspondance. La solution ? Une table d’alias, ou mieux, un appel à un modèle de langage pour faire la correspondance.
LE COÛT EN TOKENS : UNE DIFFÉRENCE ABYSSALE
L’historique brut a un coût qui explose avec la longueur de la conversation. Pour une conversation de 800 tours, le nombre de tokens par requête est 64,15 fois plus élevé que pour une conversation de 10 tours. Le RAG vectoriel et le graphe de contexte, eux, gardent un coût constant.
Autrement dit, l’historique brut a un coût linéaire en fonction de la longueur de la conversation, tandis que les deux autres architectures ont un coût constant. C’est une différence majeure pour les systèmes multi-agents où les conversations peuvent durer des heures.
QUAND UTILISER UN GRAPHE DE CONTEXTE ?
Un graphe de contexte n’est pas une solution miracle. Voici quand il est utile :
- Vos agents doivent se souvenir des décisions des autres agents sur le long terme.
- Vos questions nécessitent souvent de combiner deux faits distincts.
- Vos conversations sont longues et le coût des tokens devient un problème.
En revanche, si vos tâches sont courtes ou si vos questions sont simples, un RAG vectoriel ou même un historique brut peut suffire. Un graphe de contexte ajoute une couche de complexité qui n’est pas toujours nécessaire.
LES CINQ SCÉNARIOS TESTÉS : UNE VARIÉTÉ POUR VALIDER LA SOLUTION
Cinq scénarios différents ont été testés pour valider le graphe de contexte :
- Planification logicielle : des agents décident de l’architecture d’un projet.
- Pipeline de recherche : des agents analysent des données pour produire un rapport.
- Réponse aux incidents : des agents gèrent une panne et documentent les étapes.
- Escalade de support client : des agents transfèrent un ticket avec toutes les informations nécessaires.
- Pipeline de données : des agents nettoient et transforment des jeux de données.
Chaque scénario contient des questions de trois types :
- Directes : une seule information à récupérer.
- Distant : une information située loin dans l’historique.
- Jointure : deux informations à combiner.
LES RÉSULTATS DÉTAILLÉS : CHIFFRES QUI PARLENT
Voici les résultats complets pour chaque architecture :
| Architecture | Précision | Tokens par requête | Coût en tokens (800 tours) |
|---|---|---|---|
| Graphe de contexte | 88,9 % | 26,9 | Constant |
| Historique brut | 61,1 % | 490,9 | 64,15x plus élevé |
| RAG vectoriel | 50,0 % | 75,9 | Constant |
Le graphe de contexte est le seul à dépasser les 80 % de précision sur les questions de jointure. Le RAG vectoriel et l’historique brut obtiennent des résultats bien inférieurs sur ce type de questions.
LIMITES ET PERSPECTIVES : CE QU’IL FAUT RETENIR
Le graphe de contexte n’est pas une solution parfaite. Il a deux limites majeures :
- Correspondance d’entités : si une question utilise un terme différent de celui stocké dans le graphe, la récupération échoue. Une solution existe : utiliser un modèle de langage pour faire la correspondance, mais cela ajoute de la complexité.
- Extraction des faits : dans ce benchmark, les faits sont extraits de manière déterministe. En production, cela nécessiterait un appel à un modèle de langage, ce qui ajoute un coût et une complexité supplémentaires.
Malgré ces limites, le graphe de contexte reste une solution prometteuse pour les systèmes multi-agents où la précision et l’efficacité sont cruciales. Il permet de résoudre un problème structurel que ni l’historique brut ni le RAG vectoriel ne peuvent traiter.
CODE ET RÉPLICATION : VOUS POUVEZ TESTER VOUS-MÊME
Tous les détails de ce benchmark sont disponibles en open source. Vous pouvez reproduire les résultats et tester vos propres scénarios :
git clone https://github.com/Emmimal/context-graph-benchmark/
cd context-graph-benchmark
pip install -r requirements.txt
python run_benchmark.py
Le dépôt contient tout le code nécessaire pour exécuter les tests et comprendre les détails de l’implémentation. C’est une ressource précieuse pour quiconque veut approfondir le sujet.
EN RÉSUMÉ : POURQUOI CELA COMPTE POUR VOUS
Si vous travaillez sur des systèmes multi-agents, ce benchmark montre que l’architecture de mémoire est aussi importante que le modèle de langage lui-même. Un graphe de contexte peut faire la différence entre un système qui oublie ses décisions et un système qui les retient sur le long terme.
Mais attention : ce n’est pas une solution universelle. Pour des tâches simples ou des conversations courtes, un historique brut ou un RAG vectoriel peut suffire. Le graphe de contexte est une solution pour les cas où la précision et l’efficacité sont critiques.
En fin de compte, ce benchmark révèle une vérité simple : la façon dont vous stockez l’information détermine ce que votre IA peut en faire.
Et si vous voulez que votre IA se souvienne de vos décisions, peut-être est-il temps de passer des mots aux relations.
- Towards Data Science
L'indépendance de CLODCO est votre garantie.
Pour que l'actualité de l'IA reste sans filtre et sans concession, votre soutien est indispensable. Votre contribution est le seul moteur de notre liberté éditoriale.
Soutenir CLODCO


