Accueil / Articles / Pourquoi la simplicité l’emporte sur la complexité dans les architectures d’agents IA actuelles

Pourquoi la simplicité l’emporte sur la complexité dans les architectures d’agents IA actuelles

Cet article examine trois arguments de 2024 contre les bases de données vectorielles, la mémoire hypergraphique et la complexité de l’orchestration, montrant que des systèmes plus simples surpassent souvent des ensembles d’agents sophistiqués.

3407 mots

En lisant suffisamment de contenus sur les agents d’IA publiés cette année, un schéma se dégage avant même que ne soit avancé le moindre argument. Presque tout ce qui est proposé est additionnel : on ajoute une couche de mémoire, un graphe, un cadre d’orchestration, ou encore un pipeline de récupération comprenant trois étapes de réclassement. On ajoute également davantage d’agents dont la fonction est de superviser ceux que l’on a déjà créés. Au fond de tout cela se trouve la même croyance non exprimée : la complexité équivaut au progrès, et si votre système n’est pas plus sophistiqué qu’il y a six mois, c’est que vous êtes en retard.

Voici trois exemples. Dans chacun d’eux, on a recours à davantage d’architectures complexes alors que le véritable problème était bien moins intéressant.

Cas un : votre agent n’a probablement pas besoin d’une base de données vectorielle

Choisir une mémoire pour l’agent de livraison alimentée par un stockage vectoriel est une décision qui se prend généralement en quelques secondes. Quelqu’un énonce le besoin : « L’agent doit se souvenir de certaines choses entre les sessions », et la réponse automatique est : intégrer les données, les stocker, puis les récupérer en fonction de leur similarité. C’est la solution par défaut non pas parce que quelqu’un l’a testée par rapport à d’autres alternatives, mais parce que tous les tutoriels le font de cette manière.

C’est précisément cette hypothèse qui a été mise à l’épreuve par un article de cette année, « Your AI Agent Doesn’t Need a Vector Database » d’Anubhav. Le résultat à retenir provient du benchmark LoCoMo : une solution de base composée uniquement d’un répertoire de fichiers texte simples recherchés à l’aide de grep a surpassé plusieurs produits spécialisés financés, conçus spécifiquement pour le stockage en mémoire, sur exactement le même benchmark utilisé pour évaluer ces derniers. Il ne s’agissait pas d’une comparaison biaisée visant à obtenir un avantage. Les systèmes plus sophistiqués, certains combinant des embeddings, des recherches de similarité et même une mémoire structurée en graphes, ont tout de même perdu face à quelque chose qui pouvait être développé en une seule après-midi.

Lorsque l’on comprend bien ce résultat, son explication cesse de surprendre. La similarité vectorielle est une technique de récupération, et non une technique de raisonnement. Elle excelle à mettre en évidence des textes semantiquement proches d’une requête. En revanche, elle est médiocre pour les tâches que la mémoire exige réellement, comme reconnaître qu’un fait enregistré il y a trois semaines a été remplacé par une mise à jour d’hier, ou que deux entrées stockées s’opposent catégoriquement et qu’il faut en privilégier une. Un index vectoriel ne possède ni notion de temps intégrée, ni concept de correction. Il se contente de restituer ce qui se trouve le plus proche dans l’espace d’encodage et laisse au modèle le soin de déterminer, par lui-même, pourquoi deux des cinq résultats les plus pertinents sont en désaccord.

Il existe un deuxième manque de correspondance, qui concerne moins les capacités intrinsèques que plutôt la familiarité avec certains outils. Les modèles de langage ont absorbé d’énormes quantités de données d’entraînement axées sur le travail avec des fichiers : les lire, les parcourir avec grep, les modifier, lister des dossiers, suivre des chaînes d’importation. Ce n’est pas un effet secondaire, car cela constitue presque l’essentiel de leur corpus d’entraînement. Un boucle basée sur grep et la lecture de fichiers exploite directement cette aisance déjà acquise. En revanche, l’interface de requête d’une base de données vectorielle est un outil que le modèle doit apprendre à utiliser efficacement dans votre contexte spécifique, sans disposer de la même expérience préalable avec les fichiers. On se retrouve ainsi à remplacer une compétence déjà possédée par le modèle par une autre qu’il doit acquérir sur le moment, au prix d’une latence accrue liée aux embeddings et à la récupération des données.

Voici à peu près comment la décision pourrait être formulée maintenant, après l’avoir réellement analysée plutôt que de se fier à l’habitude :

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

Le cycle d’écriture, de gestion et de lecture décrit dans cet article mérite d’être concrétisé, car l’idée de « simplement utiliser des fichiers » peut sembler vague tant que ce n’est pas démontré avec du code fonctionnel. Voici une version qui peut être exécutée dès aujourd’hui, sans nécessiter de 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")]

Intégrez ces trois fonctions en tant qu’outils, laissez le modèle décider lui-même quand écrire une note, quand effectuer une recherche, et quand charger simplement tout un fichier de sujet dans le contexte s’il est suffisamment court pour y tenir. Le résultat sera un système de mémoire sans aucun coût d’incorporation, sans base de données vectorielle à héberger ou à payer, dont l’état peut être ouvert directement dans un éditeur de texte en cas de problème. Si cette configuration devient trop limitée, la raison sera évidente : elle se manifestera par une panne spécifique — un corpus trop volumineux pour être recherché rapidement avec des outils de texte simple, ou une question à plusieurs étapes que la recherche par mots-clés ne peut réellement pas répondre. C’est là une justification bien meilleure pour introduire un stockage vectoriel que de simplement suivre ce qu’un tutoriel a choisi de démontrer.

Cas deux : les hypergraphes ne sauveront pas non plus votre système RAG

Si les bases de données vectorielles étaient le choix automatique de l’année dernière, le RAG basé sur les graphes en est devenu le choix cette année, et les hypergraphes représentent l’évolution qui apparaît lorsque une équipe juge qu’un simple graphique de connaissances n’est pas suffisamment expressif. L’idée semble raisonnable en apparence : un arête ordinaire de graph connecte exactement deux nœuds, mais de nombreux faits du monde réel impliquent plus de deux acteurs en même temps. Prenons par exemple un scénario où un employé approuve les dépenses de voyage d’un collègue au nom de tout un département pour un cycle budgétaire donné — ce seul fait implique cinq acteurs distincts. Si l’on force cette situation dans des arêtes binaires, on se retrouve face à un choix : soit on perd la notion qu’il s’agissait d’un événement unique, soit on le divise en plusieurs arêtes binaires qui doivent ensuite être réassemblées au moment de la requête. Un hyperedge, capable de relier n’importe quel nombre de nœuds en même temps, semble être la solution.

Une manière plus fidèle de le modéliser. Ainsi, les équipes développent des systèmes RAG à hypergraphes en partant du principe que cette plus grande fidélité se traduira par de meilleures performances de récupération.

Un article publié cette année par un auteur nommé Dustin, intitulé « Les hypergraphes ne rendront pas votre système RAG meilleur. Voici ce qu’ils changent réellement », a mis à l’épreuve cette idée en s’appuyant sur la mise en œuvre réelle présentée dans un article sur les hypergraphes RAG, et non seulement sur son résumé. Les résultats furent presque comiques : HyperGraphRAG, un système dont l’idée de base est que les hyperarêtes natives sont importantes, stocke en réalité tout en interne dans une base de données graphique standard utilisant des arêtes binaires ordinaires. Plus révélateur encore, les auteurs mêmes de l’article ont démontré que cette transformation — qui consiste à diviser chaque hyperarête en un petit groupe d’arêtes binaires entourant un nœud représentant l’événement — ne perd rien. Aucune information de la structure sous-jacente n’est perdue lors de cette conversion. La représentation prétendument plus fidèle basée sur les hypergraphes et la version « ennuyeuse » à base d’arêtes binaires peuvent être reconstituées l’une à partir de l’autre avec précision.

ly.

Ce n’est pas une simple note en bas de page concernant l’implémentation — cela affaiblit l’ensemble du raisonnement. Si un hyperarête natif et un cluster d’arêtes binaires réifiées en rôles codent la même structure d’incidence, et que chacun peut être reconstruit de manière triviale à partir de l’autre, alors choisir l’un plutôt que l’autre n’est pas vraiment un choix de modélisation ayant des conséquences ultérieures. C’est simplement une décision concernant le format de stockage. Un commentateur de cet article, Felix Anderson, a formulé les mathématiques pertinentes aussi clairement que possible : un hyperarête et un graphe binaire réifié en rôles décrivent la même structure d’incidence, et la largeur de l’arbre hypertrophié ne varie que d’un facteur constant lorsque l’arity est bornée. La largeur de l’arbre hypertrophié est la mesure de complexité réelle qui détermine le coût d’évaluation d’une requête — ce n’est ni le nombre de sauts, ni le nombre de participants entassés dans une même arête. Si le changement de représentation ne modifie ce chiffre que d’une constante, et chaque fait impli

Lorsque le nombre de participants est limité (ce qui est vrai pour presque tous les faits du monde réel — un petit groupe de personnes dans une chaîne d’approbation, et non des milliers), alors tous les efforts d’ingénierie déployés sur le commutateur ne servent à rien en ce qui concerne la dimension qui détermine réellement le coût des requêtes.

Il est utile d’être honnête quant aux raisons pour lesquelles cette idée fausse est facile à adopter. Le nombre d’étapes est intuitif : plus il y a de nœuds entre une question et sa réponse, plus on pense que la requête doit être difficile. En revanche, la largeur de l’hyperarbre n’est pas du tout intuitive — elle provient de la théorie de la satisfaction des contraintes et de la complexité des requêtes, et elle peut varier de manières que le nombre d’étapes ne permet jamais de détecter, à moins que quelqu’un ne vérifie expressément. Il est tout à fait possible d’ajouter une complexité structurelle qui réduit le nombre d’étapes pour une requête d’exemple soigneusement choisie, tout en laissant inchangée la classe de complexité sous-jacente, voire en la rendant légèrement pire. Les exemples présentés dans un article peuvent sembler impressionnants, mais ils ne disent rien de significatif sur le cas général.

Voici la comparaison côte à côte qu’il convient d’examiner avant de choisir entre un graphe simple, un graphe réifié ou un stockage d’hypergraphes natif :

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

Rien de tout cela ne prouve que la structure graphique est inutile pour RAG. La récupération relationnelle à plusieurs étapes bénéficie réellement de la structure graphique par rapport à une recherche vectorielle plate — cela ne fait aucun doute. Ce qui est discuté, c’est le passage supplémentaire d’un graphique ordinaire à un hypergraphique ; et une fois que l’on regarde au-delà des promesses pour examiner les preuves réelles, la conclusion honnête est que ce passage permet d’obtenir un modèle de données plus propre, mais à un coût supplémentaire en termes d’infrastructure nécessaire pour le faire fonctionner, sans pour autant influencer le facteur qui détermine si les requêtes s’exécutent rapidement ou lentement. Si la qualité de la récupération est le problème, la solution étayée par des preuves concrètes consiste généralement en un processus de construction de graphes amélioré ou en une stratégie de récupération plus intelligente utilisant le graphique existant — et non en un type d’arête plus exotique.

Cas trois : le véritable goulot d’étranglement n’a jamais été le code lui-même

Les deux premiers arguments portaient sur l’architecture de récupération, un sujet qui relève clairement d’un domaine familier. Le troisième, en revanche, est différent, car il ne s’agit pas de choisir l’outil adéquat — il concerne plutôt la nature réelle du travail d’un ingénieur senior, et il a touché un point sensible plus que prévu.

Patrick Koss, chef technique dirigeant une équipe de cinq ingénieurs au sein d’une entreprise comptant plus d’un millier d’employés, a intitulé son article « L’IA ne peut pas faire 95 % de mon travail (et je suis ingénieur logiciel) ». Cette affirmation initiale ressemble presque à une reconnaissance de défaite avant de se transformer en argument : écrire du code représente, de loin, la plus petite partie du temps qu’il consacre à son travail. Son équipe suit un modèle « vous le créez, vous le gérez », ce qui signifie que la responsabilité de surveillance des systèmes qu’ils gèrent incombe à ses propres ingénieurs et non à un groupe opérationnel distinct qui pourrait traiter les problèmes de production comme s’ils appartenaient à quelqu’un d’autre. Ses journées commencent vers 8h30 par l’examen des demandes de fusion, et le code présent dans ces demandes — en grande partie rédigé par des agents d’IA chargés de la plupart de l’élaboration — est nettement de meilleure qualité que celui qu’il examinait il y a quelques années. Il ne remet pas en question la capacité de l’IA à produire du bon code.

Il admet complètement ce point, puis fait remarquer que cela ne change guère ce que son travail exige réellement de lui.

C’est ce détail qui mérite une attention particulière, car il remet en question une hypothèse inhérente à de nombreuses réflexions sur les stacks d’agents, y compris de nombreux arguments avancés dans ce domaine : l’idée que les capacités déterminent l’automatisation. La logique habituelle veut que, dès qu’un modèle peut écrire du code correct, la production de code cesse d’être un travail humain, et donc la part du « travail » qui est automatisée devrait correspondre à la part du travail qui impliquait auparavant l’écriture de code. Le contre-argument de Koss est que cette équation était déjà fausse bien avant l’apparition de l’IA — l’IA se contente de rendre cette erreur plus visible. Le rôle d’un chef technique n’a jamais été principalement axé sur la production de code. Il a toujours porté avant tout sur la coordination : choisir ce qui sera développé et dans quel ordre, négocier le périmètre du projet avec des parties prenantes ayant des priorités contradictoires, examiner et soutenir les décisions techniques des autres, assumer la responsabilité globale du projet, former ceux qui en ont moins d’expérience.

Il s’agit de négocier avec les ingénieurs, et de traduire ce que demande un partie prenante en fonction de ce que le système peut réellement gérer sans tomber en panne. Rien de tout cela n’est en réalité du codage déguisé. Il s’agit de travail organisationnel et interpersonnel qui produit par hasard du code au fil du processus — un résultat d’ailleurs hautement automatisable — inséré au sein d’un ensemble bien plus vaste de responsabilités qui résistent à l’automatisation précisément parce qu’elles ne visent pas à produire des artefacts, mais plutôt à instaurer un accord, des compromis et une responsabilité entre les personnes.

Ce pourrait être l’observation la plus sous-estimée dans les discussions de cette année sur les outils d’aide au développement : elle est plus importante que n’importe quel chiffre de référence, car elle explique pourquoi « le modèle s’est considérablement amélioré en matière de codage » et « mon travail est devenu nettement plus simple » ne progressent pas toujours en parallèle pour de nombreux ingénieurs seniors, même ceux qui utilisent activement ces outils et en tirent réellement des avantages. Voir les scores de SWE-bench passer de quelques unités à plus de soixante-dix en l’espace de deux ans représente une amélioration réelle et significative des capacités. Mais cette amélioration ne se traduit pas automatiquement par un travail 70 % moins lourd, car ce travail n’a jamais été à 70 % consacré à la production de code — surtout lorsque l’on est suffisamment senior pour que le poste inclue également la gestion des tours de garde et du plan stratégique, et pas seulement les demandes de modification de code.

La réserve importante est que cet argument ne se généralise pas aussi clairement que les deux premiers. Les affirmations concernant les bases de données vectorielles et les hypergraphes sont suffisamment techniques pour pouvoir être vérifiées à l’aide d’un benchmark ou d’une preuve, et c’est précisément ce qui a été fait précédemment. La question de savoir dans quelle mesure le travail d’un ingénieur senior consiste en coordination plutôt qu’en codage variera en fonction de la taille de l’entreprise, du niveau de maturité de l’équipe, du fait que les coûts organisationnels soient réellement utiles ou simplement source de dysfonctionnements, ainsi que du niveau d’expérience de l’individu. Une équipe de cinq personnes au sein d’une entreprise de mille employés suivant le principe « vous le construisez, vous le gérez » représente un type particulier de poste, et non un substitut à tous les rôles d’ingénieur. Néanmoins, la correction de base semble valable dans l’ensemble : les capacités des agents fixent un plafond à la part du travail de rédaction de code qui pourrait théoriquement être automatisée, mais cela indique

On ne sait presque rien sur la part que peut représenter l’aspect de coordination, car cette partie n’a jamais été limitée par la vitesse de frappe ou la qualité du code dès le départ.

Le fil conducteur

En comparant ces trois cas côte à côte, on constate que la véritable leçon n’a rien de spécifique aux bases de données vectorielles, aux hypergraphes ou aux capacités des agents. Elle concerne plutôt un décalage entre l’endroit où chaque domaine pensait que résidait la difficulté et l’endroit où elle se trouvait 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 des options plus complexes n’est en soi une mauvaise idée. Les bases de données vectorielles résolvent des problèmes réels dans le contexte adéquat, les hypergraphes peuvent se révéler utiles dans certaines situations non abordées ici, et les agents d’IA éliminent véritablement les tâches fastidieuses des postes d’ingénierie, comme l’indique explicitement le chercheur derrière le troisième cas d’étude. L’erreur n’a pas résidé dans la quête de sophistication, mais plutôt dans le fait d’avoir omis de vérifier que cette sophistication ciblait bien la contrainte réelle, et non celle qui se trouvait être la plus facile à traiter pour proposer une solution impressionnante.

Cette habitude se manifeste bien au-delà du domaine de l’ingénierie des agents. Ajouter une couche supplémentaire est presque toujours plus facile que de s’arrêter pour se demander si la version de base, simple et peu attrayante, a jamais été correctement testée par rapport à cette nouvelle couche. Une nouvelle abstraction apparaît comme un progrès visible, quelque chose que l’on peut pointer du doigt et qualifier d’amélioration. Vérifier si grep couvre déjà le cas, ou si la complexité de sa requête s’est réellement améliorée, ou encore si ce qui occupe toute une semaine de travail correspond vraiment à ce que l’on pensait, c’est un travail plus lent et bien moins gratifiant, qui peut se terminer par la conclusion qu’il vaut mieux arrêter de développer plutôt que de continuer.

Voici une version condensée de la liste de contrôle à utiliser avant d’ajouter une nouvelle couche à n’importe quel stack :

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 justifie de réduire les efforts d’ingénierie dans leur ensemble. Au contraire, il s’agit de diriger ces efforts vers la détermination précise de l’endroit où se trouve réellement le goulot d’étranglement, avant de concevoir une solution complexe qui part du principe que l’on connaît déjà la réponse. Les trois exemples présentés ici n’ont pas été choisis pour surprendre. Ils ont reçu cette étiquette parce que quelqu’un a effectué le travail peu glamour de vérification, et les résultats ont contredit l’hypothèse initiale. Cela représente un standard bien plus élevé qu’un titre accrocheur accompagné d’une opinion tranchée, et c’est ce standard qu’il convient d’appliquer à son propre système technologique à l’avenir.

Lectures complémentaires

  • À l’intérieur d’un agent IA : modèles, outils, mémoire et raisonnement expliqués — Décrit en détail l’architecture de base des agents IA — objectifs, modèles, outils, mémoire et raisonnement — et présente un exemple concret d’agent de recherche basé sur Python.