Remettre en question l’Agent Stack : les bases de données vectorielles, les hypergraphes et les tâches de codage
Trois cas où une pile d’agents plus importante a manqué le véritable goulot d’étranglement : la mémoire des agents basée sur des fichiers a surpassé un stockage vectoriel, un hypergraph RAG, ainsi que le travail de niveau senior.
Sources: The Case Against Your Own Agent Stack: What Contrarians Get Right This Year
La tendance à en ajouter toujours plus
En parcourant les articles de cette année sur les agents IA, une chose ressort clairement par-dessus toutes les affirmations individuelles : les conseils conseillent presque toujours d’ajouter quelque chose. Une couche de mémoire. Un graphe. Un cadre d’orchestration. Un pipeline de récupération avec trois étapes de réclassification. Plus d’agents pour surveiller ceux que l’on possède déjà. Au fond, il y a la croyance tacite que la complexité signifie progrès, et que tout système qui ne fait pas mieux que celui de il y a six mois est en retard.
Une part considérable de cette pile technologique a probablement été développée et défendue par quiconque lit ceci. Il est donc utile de s’arrêter un instant sur un petit ensemble d’exemples qui avancent l’inverse : l’élément sophistiqué que vous avez ajouté n’a probablement pas mérité sa place. La plupart des écrits technologiques contraires à la tendance ne sont que du marketing inversé, tout aussi affirmés mais tout aussi dépourvus de preuves. Ces trois cas sont différents car chacun repose sur quelque chose de vérifiable, qu’il s’agisse d’un résultat de benchmark, d’une démonstration structurelle ou d’un récit simple de la journée de travail de quelqu’un. Ce standard s’applique ci-dessous, ainsi qu’à toute pile technologique, y compris la vôtre.
Chacun de ces cas représente un moment où le domaine a cherché plus de structure, bien que le véritable problème se trouvât ailleurs, de manière moins évidente.
Premier cas : mémoire d’agent et stockages vectoriels
La mise en mémoire des agents dans un stockage vectoriel prend généralement environ une demi-minute. Quelqu’un a souligné que les agents devraient conserver les données entre les sessions, ce qui a conduit à la méthode consistant à intégrer les données, à les stocker et à les récupérer par similarité. Tous les tutoriels fonctionnent de cette manière, ce qui en a fait la norme, sans que personne n’ait testé cette approche par rapport à des solutions plus simples.
Un article paru cette année a effectué ce test. Sur le benchmark LoCoMo, une solution de base composée d’un dossier de fichiers de texte brut et de grep a obtenu de meilleurs résultats que plusieurs produits spécialisés financés à cet effet. Cette comparaison n’a pas été organisée de manière contrôlée. Les systèmes utilisant des intégrations vectorielles, une recherche par similarité et parfois une mémoire graphique se sont retrouvés inférieurs à une configuration qui nécessite une après-midi entière pour être mise en place.
Après y avoir réfléchi, l’explication semble claire. La recherche par similarité trouve des résultats, mais elle ne raisonne pas. Elle excelle à récupérer du texte dont le sens est proche d’une requête. En revanche, elle est médiocre pour gérer les besoins en mémoire, comme reconnaître qu’un fait datant de trois semaines a été remplacé par un fait d’hier, ou que deux souvenirs enregistrés entrent en conflit et qu’un seul doit prévaloir. Un index ne possède ni notion de temps ni capacité de correction, il renvoie donc les vecteurs les plus proches et laisse au modèle le soin de constater que deux des cinq meilleurs résultats se contredisent.
Un deuxième problème concerne la familiarité et non la capacité. Les modèles ont été entraînés sur d’énormes quantités de matériel concernant le travail avec les fichiers : ouverture, recherche, modification, affichage des dossiers, suivi des importations. Les fichiers sont proches du matériel de base de leurs données d’entraînement, et une boucle combinant grep et lecture de fichiers en profite directement. En revanche, une API de requête vectorielle fournie par un fournisseur est quelque chose que le modèle doit apprendre à utiliser efficacement au sein de sa propre fenêtre de contexte, sans avoir été exposé à elle auparavant. Cela consiste à remplacer une compétence déjà acquise par une autre qu’il doit acquérir en cours de tâche, ce qui ajoute des retards liés aux embeddings et à la récupération des informations.
Un guide de décision comparant les deux options côte à côte se présente comme suit.
WHEN A FILESYSTEM + GREP BASELINE IS ENOUGH WHEN YOU ACTUALLY NEED A VECTOR STORE
------------------------------------------------ ------------------------------------------------
Single-agent or small-team memory Retrieval across a corpus too large to
fit or scan in context at all
Facts that change over time and need Cross-document semantic search where
correction, not just accumulation keyword overlap is genuinely weak
Memory the model itself writes, Centralized memory shared by many agents
manages, and re-reads in its own loop that needs access control and auditing
Debuggable state, plain text you can Multi-hop or relational reasoning across
open, diff, and edit by hand thousands of entities where similarity
search is doing real narrowing work
À quoi ressemble la boucle basée sur les fichiers
« Il suffit d’utiliser des fichiers » semble vague tant que ce n’est pas du code. Cette version ne nécessite aucun service hébergé.
import os
import subprocess
from datetime import datetime
MEMORY_DIR = "agent_memory"
def write_memory(topic: str, content: str) -> str:
os.makedirs(MEMORY_DIR, exist_ok=True)
path = os.path.join(MEMORY_DIR, f"{topic}.md")
timestamp = datetime.utcnow().isoformat()
with open(path, "a", encoding="utf-8") as f:
f.write(f"\n## {timestamp}\n{content}\n")
return path
def recall(query: str) -> str:
# ripgrep if you have it, grep -r works fine too
result = subprocess.run(
["rg", "-i", "-C", "2", query, MEMORY_DIR],
capture_output=True, text=True
)
return result.stdout or "no matches"
def list_topics() -> list[str]:
if not os.path.isdir(MEMORY_DIR):
return []
return [f[:-3] for f in os.listdir(MEMORY_DIR) if f.endswith(".md")]
Donnez au modèle ces trois fonctions en tant qu’outils et laissez-le décider quand enregistrer, quand rechercher et quand charger un fichier complet sur un sujet court. Le résultat est une mémoire sans coûts d’incorporation, sans base de données à héberger ou à payer, et avec un état que l’on peut ouvrir dans n’importe quel éditeur en cas de problème. Lorsque cette approche finit par échouer, la raison sera concrète, comme un corpus trop volumineux pour être parcouru rapidement ou une question à plusieurs étapes que la recherche par mots-clés ne peut pas résoudre. Cela justifie l’utilisation d’un stockage vectoriel bien mieux qu’une simple copie d’un tutoriel.
Rien dans tout cela ne dit que les bases de données vectorielles ne devraient pas exister. Cela signifie simplement de ne pas en recourir avant d’avoir évalué la solution de base. Essayez d’abord le contexte complet, le système de fichiers ainsi que grep, et n’adoptez un produit de mémoire payant que s’il surpasse cette solution de base suffisamment pour couvrir son coût et la perte de capacité de débogage. La plupart des mémoires d’agent n’ont jamais besoin d’atteindre ce niveau.
Cas deux : les hypergraphes dans RAG
L’an dernier, la solution par défaut était la base de données vectorielle. Cette année, c’est le RAG basé sur les graphes, et les hypergraphes entrent en jeu lorsque l’équipe estime qu’un graphique de connaissances ordinaire ne suffit pas pour exprimer l’ensemble des informations. Ce cas semble logique. Un bord ordinaire relie deux nœuds, mais de nombreux faits impliquent plusieurs acteurs. Prenons l’approbation d’une dépense : une personne approuve le rapport d’un collègue, au nom d’une équipe financière, et cela est imputé sur le budget marketing trimestriel. Il s’agit donc de cinq parties impliquées dans un seul fait. En les regroupant en paires, on perd l’idée d’un événement atomique unique ou bien on doit diviser ce fait en plusieurs bords binaires que les requêtes doivent ensuite reconstituer. Un hyperbord, quant à lui, peut relier n’importe quel nombre de nœuds en même temps, ce qui semble être le moyen le plus fiable pour stocker de tels faits ; les équipes développent donc des systèmes RAG à base d’hypergraphes dans l’espoir que cette fiabilité améliore les résultats de recherche.
Un autre article a vérifié cette hypothèse en se basant sur le code fonctionnel d’un papier sur HyperGraph RAG plutôt que sur son résumé, et a découvert quelque chose de presque comique. HyperGraphRAG, conçu pour favoriser les hyperarêtes natives, stocke tout dans une base de données de graphes ordinaire utilisant des arêtes binaires. Ses propres auteurs démontrent que transformer chaque hyperarête en quelques arêtes binaires autour d’un nœud représentant l’événement ne entraîne aucune perte d’information, de sorte que chaque forme peut être reconstituée avec précision à partir de l’autre.
Ce détail résume l’ensemble de l’argumentation. Supposons qu’un hyperarête natif et un cluster de bornes binaires possèdent exactement la même structure d’incidence, et que l’un puisse générer l’autre sans effort. Alors le choix entre eux n’a aucune conséquence sur le modélisation et revient simplement à choisir un format de stockage. Un commentateur a résumé brièvement les mathématiques : les deux formes correspondent à la même structure d’incidence, et la largeur de l’hyperarbre ne varie que d’une constante lorsque l’arity est bornée. C’est la largeur de l’hyperarbre qui détermine le coût d’une requête. Le nombre d’étapes de saut ne le fait pas, tout comme le nombre de participants regroupés dans une seule borne. En convertissant votre représentation, en décalant cette valeur d’une constante, et en limitant le nombre de participants pour chaque fait (cinq personnes approuvant une dépense, et non cinq cents), la difficulté des requêtes reste identique.
Pourquoi cela prend les gens au dépourvu
C’est une erreur facile à commettre. Le nombre d’étapes semble naturel, car plus il y a de nœuds entre la question et la réponse, plus la requête semble complexe. La largeur de l’hyperarbre est bien moins intuitive : elle relève de la théorie de la satisfaction de contraintes et de la complexité des requêtes, et diffère du nombre d’étapes de manières que l’on ne perçoit qu’en y regardant de plus près. On peut ajouter une structure qui réduit le nombre d’étapes pour une requête d’exemple sans affecter, ou au contraire nuire légèrement, à la classe de complexité à laquelle appartient cette requête. Les exemples illustrés dans un article peuvent paraître impressionnants sans pour autant révéler quoi que ce soit sur la mesure qui gouverne le cas général.
Avant de choisir entre un graphe simple, un graphe réifié ou un stockage d’hypergraphes natif, cette comparaison est utile.
REPRESENTATION WHAT IT ADDS WHAT IT ACTUALLY CHANGES
--------------------- ------------------------------- --------------------------------
Plain binary graph Simplest to build and query Baseline; loses atomicity of
with standard graph tooling multi-participant facts
Reified binary graph Recovers atomicity via an Same incidence structure as a
(event node + roles) explicit "event" node hyperedge; hypertree width
shifts by a constant only
Native hypergraph Hyperedges as first-class No reduction in query
store objects, arguably cleaner complexity class over a
to write against reified graph; new storage
engine to run and maintain
Cela ne rend pas la structure de graphe inutile pour RAG. La récupération relationnelle à plusieurs étapes bénéficie effectivement des graphes par rapport à la recherche vectorielle plate, et personne ne contredit cela. L’argument porte plutôt sur le saut supplémentaire du graphe vers l’hypergraphe. Une fois la démonstration avancée, ce saut permet d’obtenir des modèles de données plus élégants, ajoute un nouveau type d’infrastructure à gérer, sans affecter le critère qui détermine si les requêtes sont rapides ou lentes. Si la récupération est insuffisante, le remède, étayé par des preuves, consiste généralement en une meilleure construction du graphe ou en une stratégie de récupération plus intelligente sur le graphe déjà créé, et non en un type d’arête plus exotique.
Cas trois : en quoi consiste réellement le travail d’un ingénieur senior
Les deux cas précédents portent sur l’architecture de récupération. Le dernier cas est différent, car il ne s’agit pas d’un choix d’outil, mais de la question de la composition du travail d’ingénierie senior.
Un chef technique dirigeant une équipe de cinq personnes au sein d’une entreprise comptant plus de mille employés a écrit que l’IA ne pouvait pas assumer 95 % de son travail. Il commence par ce qui ressemble à une concession : l’écriture de code occupe la part la plus petite de son temps, de loin. Son équipe fonctionne selon le principe « vous le créez, vous le gérez », ce qui signifie que ce sont ses propres ingénieurs qui doivent assurer la surveillance de ses systèmes, et non un groupe d’opérations distinct qui pourrait considérer la production comme une affaire qui ne le concerne pas. Sa journée commence à 8h30 par l’examen des demandes de fusion de code. Le code qui arrive, en grande partie tapé par des agents d’IA, est clairement de meilleure qualité que celui qu’il avait examiné deux ans auparavant. Il ne remet pas en question le fait que l’IA puisse écrire de bon code ; il l’accepte pleinement, avant de souligner à quel point cela modifie peu le travail qu’il effectue réellement.
Cela remet en question une prémisse courante dans la façon de concevoir les piles d’agents, à savoir que les capacités déterminent le rythme de l’automatisation. Selon cette logique, si un modèle génère du code correct, il n’est plus nécessaire d’intervenir humainement, et l’automatisation de la tâche correspond alors à la partie consacrée à la création du code. Le chef technique affirme que cette équation n’a jamais été vraie, et que l’IA se contente de rendre les défauts visibles. Son travail ne consistait pas principalement en écriture de code, mais plutôt en coordination : choisir ce qui doit être développé et dans quel ordre, négocier les objectifs avec des personnes aux priorités divergentes, examiner et soutenir les décisions prises par d’autres, gérer les communications urgentes, former les ingénieurs plus jeunes, et médier entre les demandes des parties prenantes et ce que le système peut soutenir en toute sécurité. Aucune de ces tâches n’est une mission d’écriture de code.
Ce pourrait être le point le plus négligé de l’année dans les débats sur les outils d’évaluation, plus que n’importe quel autre critère, car il explique pourquoi des scores de codage nettement améliorés et un travail considérablement plus facile n’ont pas évolué en même temps pour de nombreux ingénieurs seniors, même pour les utilisateurs quotidiens les plus enthousiastes. Une hausse des scores SWE-bench de quelques unités à plus de soixante-dix en quelques années représente bien une avancée significative. Cela ne se traduit cependant pas par 70 % de travail en moins, car la production de code n’a jamais représenté 70 % du travail, surtout lorsque le poste inclut des tours de garde, un plan stratégique ainsi que des demandes de fusion de code.
Une précision : le troisième point s’applique aux autres paramètres de manière moins claire que les deux premiers. Les affirmations concernant les bases de données vectorielles et les hypergraphes peuvent être vérifiées à l’aide d’un benchmark ou d’une démonstration. La répartition entre coordination et codage au cours de la semaine d’un ingénieur senior varie en fonction de la taille de l’entreprise, du niveau de maturité de l’équipe, du fait que les tâches supplémentaires soient dues à des dysfonctionnements ou à des activités essentielles, ainsi que du grade de l’individu lui-même. Une équipe de cinq personnes au sein d’une entreprise de mille employés suivant une politique de « construis et exécute » représente un type de poste qui ne correspond pas à tous les rôles d’ingénierie. La direction de la correction semble globalement correcte. Les capacités de l’agent limitent la part du codage qui peut être automatisée, et elles ne disent presque rien concernant la partie liée à la coordination, qui n’a jamais dépendu de la vitesse de frappe ou de la qualité du code.
Le schéma commun
En plaçant les trois cas côte à côte, on constate que la leçon ne porte pas spécifiquement sur les bases de données vectorielles, les hypergraphes ou les capacités d’intelligence artificielle. Elle concerne plutôt l’écart entre l’endroit où un domaine pensait que se trouvait le goulot d’étranglement et l’endroit où il se situait réellement.
CASE WHERE COMPLEXITY WAS ADDED WHERE THE REAL BOTTLENECK WAS
------------------ ---------------------------------- --------------------------------
Agent memory Vector embeddings, similarity Whether the model can use a
search, sometimes a graph layer retrieval method it already
on top of that has deep fluency with
RAG structure Native hyperedges, a new The complexity class governing
storage engine, more query cost, which the fancier
elaborate graph modeling structure barely touches
"How much of the An assumption that model Whether the job was ever
job gets automated" capability alone predicts mostly about the thing the
the automatable fraction model is good at
Aucune de ces options sophistiquées n’est fausse pour avoir existé. Les bases de données vectorielles ont des utilisations réelles, les hypergraphes pourraient être utiles dans des domaines où personne n’y a encore pensé, et les agents d’IA permettent effectivement d’éliminer des tâches fastidieuses dans le travail d’ingénierie, ce que reconnaît le responsable technique. L’erreur a été de négliger la question de savoir si cette complexité supplémentaire vise le véritable goulot d’étranglement ou simplement celui pour lequel il est le plus facile de trouver une solution élaborée.
Cette habitude dépasse de loin le domaine de l’ingénierie des agents. Ajouter une couche est plus simple que de se demander si la solution de base a jamais été évaluée de manière équitable par rapport à elle. Une nouvelle abstraction est considérée comme un progrès visible auquel on peut se référer en parlant d’amélioration. Vérifier que grep fait déjà le travail, que la complexité des requêtes s’est réellement améliorée, ou que le problème qui occupe toute une semaine correspond bien à ce que l’on pensait, prend plus de temps, est plus difficile, et conduit parfois à décider d’arrêter de développer.
Une liste de contrôle avant l’ajout d’une nouvelle couche
Une courte liste à vérifier pour tout stack avant d’ajouter une nouvelle couche :
1. Have I benchmarked the boring baseline, not just assumed it loses?
(full-context, grep, a plain graph, a human doing the coordination)
2. Does the new structure change the metric that actually governs cost
or quality, or does it just look more sophisticated on a diagram?
3. Am I reaching for this because a benchmark or proof told me to,
or because it's what the tutorials and the funded products default to?
4. If I strip this layer back out, what specifically breaks?
If I can't name it precisely, I probably don't need the layer yet.
5. Am I solving the bottleneck I actually have, or the bottleneck
that's most interesting to build a sophisticated solution for?
Rien de tout cela ne plaide en faveur d’une moindre ingénierie. Au contraire, il s’agit d’investir des efforts d’ingénierie pour identifier où se trouve le goulot d’étranglement avant de construire quelque chose de complexe en partant du principe que la réponse est déjà connue. Ces trois études n’ont pas été menées par esprit de contradiction. Elles ont effectué des vérifications peu spectaculaires, et ces vérifications étaient en désaccord avec les hypothèses initiales. Cela représente un standard bien plus élevé qu’une opinion tranchée accompagnée d’un titre accrocheur, et c’est le critère auquel tout système devrait se conformer.