Accueil / Articles

Le graphe en tant qu’architecture contre le graphe en tant que données : LangGraph d’abord, GraphRAG ensuite

Pourquoi GraphRAG n’est pas une mise à niveau par défaut par rapport à la recherche vectorielle, en quoi les graphes de flux de contrôle de LangGraph diffèrent des graphes de connaissances, et un agent de tri des demandes d’assistance avec sauvegardes intermédiaires et possibilité d’intervention humaine.

Éditeur : REACTAPP.TOP12 min de lecture2663 mots

Sources: Graph as Architecture, Not Graph as Data: Why I Stopped Reaching for GraphRAG First

Une première réaction courante face à un nouveau projet d’agent est de recourir à GraphRAG. Non pas parce que son efficacité a été mesurée par rapport à une recherche vectorielle classique sur le trafic propre de l’équipe, mais parce qu’un graphe de connaissances semble plus avancé — entités, arêtes, parcours structurés au lieu d’une simple similarité — tout comme un schéma normalisé paraît « plus correct » qu’un tableau, même lorsque ce dernier fonctionne déjà.

Les articles sur l’ingénierie des graphes et la transformation de grands corpus en graphes de connaissances adaptés aux agents renforcent cette vision : les graphes comme substrat mature pour la mémoire des agents. Présentée comme une vérité générale en matière de récupération d’informations plutôt que comme une affirmation limitée à des charges de travail spécifiques, de nombreuses approches commencent par se demander « avons-nous besoin d’une base de données graphique ? » plutôt que « avons-nous vraiment besoin d’une méthode de récupération allant au-delà de la similarité — et si oui, de quel type ? »

Deux articles consécutifs montrent que cette hypothèse mérite d’être vérifiée plutôt que répétée.

Le premier est un article d’évaluation intitulé « How Significant Are Essentially Real Performance Gains? An Unbiased Evaluation Framework for GraphRAG » (arXiv:2506.06331). Il a examiné les évaluations utilisant un LLM en tant que juge, à l’origine de nombreuses affirmations concernant des taux de réussite élevés. Les juges utilisés en paires présentent des biais bien connus : le biais de position (le changement de l’ordre des réponses peut faire varier les taux de réussite rapportés de plus de trente points), le biais de longueur (les réponses plus longues et plus détaillées l’emportent en termes de style) et le biais d’essai (les comparaisons identiques donnent des résultats différents lors de répétitions). Après corrections, le taux de réussite de 66,7 % rapporté pour une méthode populaire par rapport au RAG de base est tombé à environ 39 % — en dessous du seuil aléatoire. Ce n’est pas le signe que les gains étaient plus faibles ; c’est plutôt le signe que, une fois les biais des juges éliminés, les avantages de cette méthode dans cette évaluation pourraient disparaître.

Le deuxième est GraphRAG-Bench (arXiv:2506.02404, accepté pour l’ICLR vers 2026), conçu pour cesser de se fier essentiellement aux taux de réussite des juges et évaluer plutôt les méthodes GraphRAG par rapport à la récupération de fragments de texte brut sur des questions vérifiables dans seize disciplines différentes. Le résultat pertinent pour un usage en production : pour la recherche de faits simples — des questions du type « qu’est-ce que X » qui dominent de nombreux systèmes RAG en temps réel — les fragments de texte brut ont obtenu 60,9 points tandis que la récupération basée sur des graphes a obtenu 60,1 points. Il s’agit en fait d’un match nul, avec une construction de réseau, un stockage et une complexité des requêtes supplémentaires qui n’apportent aucun avantage mesurable dans cette catégorie.

GraphRAG-Bench results by question category (approximate, from published results)
+----------------------------+---------------+---------------+------------+
| Question category           | Text chunks   | Graph retrieval| Winner     |
+----------------------------+---------------+---------------+------------+
| Simple fact retrieval        | 60.9          | 60.1          | Tie        |
| Complex reasoning            | 42.9          | 53.4          | Graph +10.5|
| Contextual summarization     | 51.3          | 64.4          | Graph +13.1|
+----------------------------+---------------+---------------+------------+

Interprétez avec soin. Aucun de ces travaux n’est définitif. Les benchmarks mesurent l’âge en mois ; GraphRAG-Bench compare neuf méthodes à une méthode de segmentation de base ; une méthode qui échoue dans des recherches simples peut encore convenir à un corpus que la suite ne prend pas en compte. L’audit des biais ne prétend pas que les graphes ne sont jamais utiles ; il indique plutôt qu’un style d’évaluation spécifique a exagéré certaines affirmations — plus précis et utile que l’affirmation selon laquelle « les graphes échouent ». Ensemble, ils permettent de dégager quelque chose de constructif : la récupération par graphes ne représente pas une amélioration catégorique par rapport aux vecteurs. Elle excelle clairement dans le raisonnement complexe et la synthèse nécessitant une intégration de nombreux contextes. Cependant, elle ne bat pas de façon fiable la récupération simple dans les recherches élémentaires qui dominent de nombreuses utilisations réelles. Ce fait peu reluisant est suffisamment important pour influencer un processus de décision.

Il existe une deuxième confusion qui dure plus longtemps qu’il ne le faudrait : le mot « graph » remplit deux fonctions sans rapport entre elles.

Deux choses appelées « graph », ne faisant rien de similaire

Le graph en tant que données est ce que GraphRAG entend généralement par là : un graph de connaissances composé d’entités et de relations extraites d’un corpus, stocké sous forme de nœuds et d’arêtes, utilisé comme support de récupération à la place de — ou en plus de — du texte fragmenté et des embeddings. Il répond à la question suivante : où se trouvent les informations, et comment l’agent trouve le fragment pertinent ?

Le graph en tant qu’architecture est ce que des outils comme LangGraph entendent par là : une machine à états pour le flux de contrôle — quelles étapes sont exécutées ensuite, dans quelles conditions, quand l’exécution est suspendue pour un humain, comment l’exécution survit à une panne et reprend. Il répond à la question suivante : comment l’exécution passe-t-elle d’une étape à l’autre ? Cela n’a rien à voir avec l’endroit où les faits sont stockés.

The two meanings of "graph" in agent engineering
+------------------+---------------------------------+---------------------------------+
| Aspect            | Graph as architecture            | Graph as data                    |
+------------------+---------------------------------+---------------------------------+
| Answers            | How does control flow move        | Where do facts live and how      |
|                    | between agent steps               | do I retrieve the right ones     |
+------------------+---------------------------------+---------------------------------+
| Nodes represent    | Units of work: classify, fetch,   | Entities: people, orders,        |
|                    | decide, respond                    | products, concepts               |
+------------------+---------------------------------+---------------------------------+
| Edges represent    | Transitions and conditions         | Relationships: "works for",      |
|                    | between steps                      | "purchased", "caused by"         |
+------------------+---------------------------------+---------------------------------+
| Typical tool       | LangGraph, a hand-rolled state     | Neo4j, a Cosmos DB graph API,     |
|                    | machine, an orchestration library  | a GraphRAG pipeline               |
+------------------+---------------------------------+---------------------------------+
| Cost to adopt       | Low. A dependency and some         | High. Extraction pipeline,        |
|                    | Python classes                     | graph store, query layer          |
+------------------+---------------------------------+---------------------------------+
| When it's wrong     | Almost never, once you have        | When your questions are simple    |
| to skip it          | more than a couple of steps        | lookups a vector or keyword       |
|                    | and any branching                  | search already answers            |
+------------------+---------------------------------+---------------------------------+

Lorsque les éléments sont séparés de manière claire, un ordre logique apparaît. En fait, le Graph-as-architecture est peu coûteux et presque immédiatement utile pour tout outil d’orchestration comportant plus d’une ou deux étapes — il n’y a guère de raison de ne pas l’adopter tôt. Le Graph-as-data, en revanche, est coûteux ; son utilité dépend du fait que les questions nécessitent ou non une exploration des relations, ce qui peut être vérifié avant de commencer. Considérer un élément coûteux et conditionnel comme par défaut, tout en traitant un élément peu coûteux et quasi universel comme une solution secondaire, est une approche inversée. La manière correcte de procéder se présente comme suit.

Construire l’architecture : un véritable agent LangGraph avec fonction de sauvegarde et possibilité d’interruption par l’humain

Considérez un petit agent de tri des demandes. Il classe la requête, récupère les données du compte, détermine si le niveau de risque nécessite une validation humaine, puis répond. Les remboursements exigent une intervention humaine, tandis que les vérifications du solde et de l’état de la commande n’en ont pas besoin. L’exemple est simplifié, mais les mécanismes — schéma d’état, routage conditionnel, points de contrôle, interruptions — sont les mêmes utilisés en production, à la différence qu’ils impliquent moins de nœuds.

Installez les dépendances :

pip install langgraph langgraph-checkpoint-sqlite

État partagé que chaque nœud lit et écrit :

from typing import TypedDict, Literal
class AgentState(TypedDict):
    user_message: str
    intent: str
    risk_level: str
    account_data: dict
    human_decision: str
    final_response: str

Quatre nœuds suivent. La fonction classify_intent étiquette le message en fonction de son intention et du niveau de risque :

def classify_intent(state: AgentState) -> dict:
    message = state["user_message"].lower()
    if "refund" in message or "cancel" in message:
        intent, risk = "refund_request", "high"
    elif "balance" in message or "order status" in message:
        intent, risk = "account_lookup", "low"
    else:
        intent, risk = "general_question", "low"
    return {"intent": intent, "risk_level": risk}

fetch_data remplace un CRM, un système de gestion des commandes ou une API de facturation :

def fetch_data(state: AgentState) -> dict:
    fake_db = {
        "refund_request": {"order_id": "ORD-4471", "amount": 249.00, "status": "delivered"},
        "account_lookup": {"balance": 1200.50, "last_order": "ORD-4402"},
        "general_question": {},
    }
    return {"account_data": fake_db[state["intent"]]}

Le routage n’est pas un nœud ; il s’agit du lien conditionnel qui choisit le nœud suivant :

def needs_human(state: AgentState) -> Literal["human_review", "respond"]:
    return "human_review" if state["risk_level"] == "high" else "respond"

human_review correspond au point où interrupt() pause l’ensemble du graphe en cours d’exécution et restitue le contrôle à l’appelant sans perdre l’état :

from langgraph.types import interrupt
def human_review(state: AgentState) -> dict:
    decision = interrupt({
        "question": f"Approve {state['intent']} for order {state['account_data'].get('order_id')}?",
        "amount": state["account_data"].get("amount"),
    })
    return {"human_decision": decision}

respond affiche la réponse finale, en intégrant une décision humaine lorsque c’est nécessaire :

def respond(state: AgentState) -> dict:
    if state["risk_level"] == "high":
        if state.get("human_decision") == "approve":
            text = f"Refund approved for {state['account_data']['order_id']}."
        else:
            text = "This request needs manual follow-up before we can proceed."
    else:
        text = f"Here's what I found: {state['account_data']}"
    return {"final_response": text}

Le schéma de connexion illustre l’objectif de cet exercice : une description explicite et lisible du flux de contrôle, plutôt que des conditions imbriquées au sein d’une seule fonction géante :

from langgraph.graph import StateGraph, START, END
builder = StateGraph(AgentState)
builder.add_node("classify_intent", classify_intent)
builder.add_node("fetch_data", fetch_data)
builder.add_node("human_review", human_review)
builder.add_node("respond", respond)
builder.add_edge(START, "classify_intent")
builder.add_edge("classify_intent", "fetch_data")
builder.add_conditional_edges(
    "fetch_data",
    needs_human,
    {"human_review": "human_review", "respond": "respond"},
)
builder.add_edge("human_review", "respond")
builder.add_edge("respond", END)

Le marquage des points de contrôle est ce qui distingue un agent durable d’un simple script. Le mécanisme de pointage SQLite de LangGraph convient parfaitement aux travaux en un seul processus ou localement — aucune base de données externe n’est requise :

from langgraph.checkpoint.sqlite import SqliteSaver
with SqliteSaver.from_conn_string("support_agent_checkpoints.sqlite") as checkpointer:
    graph = builder.compile(checkpointer=checkpointer)
    config = {"configurable": {"thread_id": "ticket-8821"}}
    result = graph.invoke({"user_message": "I want a refund for my last order"}, config)
    print(result)

Dans un message de remboursement, le graphe exécute les opérations de classification et de récupération des données, place l’arête à haut risque dans human_review, puis s’arrête : interrupt() met fin à l’exécution. Le résultat contient une description __interrupt__ indiquant ce qui est en attente, et non une final_response finale. Le processus peut planter à cet étape ; la machine peut redémarrer ; l’état de chaque étape a déjà été enregistré dans support_agent_checkpoints.sqlite. En rouvrant ce même fichier avec le même identifiant de thread, le graphe reprend exactement là où il s’était arrêté :

with SqliteSaver.from_conn_string("support_agent_checkpoints.sqlite") as checkpointer:
    graph = builder.compile(checkpointer=checkpointer)
    config = {"configurable": {"thread_id": "ticket-8821"}}
snapshot = graph.get_state(config)
    print(snapshot.next)  # ('human_review',)
    from langgraph.types import Command
    final = graph.invoke(Command(resume="approve"), config)
    print(final["final_response"])

Command(resume="approve") renvoie une valeur vers l’appel interrupt() en suspens, comme si un responsable de validation avait approuvé. La classification et les données récupérées ne sont pas recalculées ; elles étaient déjà présentes dans le point de contrôle.

Omettre le mécanisme de point d’arrêt et conserver l’état dans un dictionnaire en mémoire « pour plus tard » entraîne une défaillance bien connue : un redémarrage au milieu de la révision fait disparaître le ticket en cours d’exécution — sans erreur, sans trace — car rien en dehors de la mémoire du processus ne savait qu’il existait. Le point d’arrêt existe justement pour éviter ce mode de défaillance ; il faut le considérer comme faisant partie intégrante du graphe, et non comme une fonctionnalité optionnelle ajoutée ultérieurement.

Lorsque le graphe en tant que données justifie réellement sa complexité

Tout cela ne signifie pas pour autant que GraphRAG est inutile. Il s’agit plutôt du mauvais premier choix — l’intégrer uniquement lorsqu’un besoin concret se présente, et non parce qu’il semble sophistiqué. Les benchmarks montrant des résultats équivalents pour des recherches simples et des avancées réelles en matière de raisonnement/compréhension complexe soulignent ce besoin : des questions qui doivent explorer les relations entre éléments, et non simplement trouver le passage le plus proche.

Trois raisons qui justifient l’utilisation d’un graphe de connaissances :

Questions de relations multi-étapes, où la réponse s’étend sur plus d’une connexion. La question « Quels fournisseurs le fabricant de cette pièce rappelée dessert-il également pour d’autres lignes de produits ? » ne peut pas être résolue grâce au seul fragment le plus similaire ; il faut suivre les liens pièce → fabricant → autres fournisseurs. Un index vectoriel renvoie du texte similaire, mais il ne dispose d’aucun mécanisme de chaînage d’étapes.

Questions où les relations constituent la réponse, sans qu’un contexte supplémentaire soit essentiel. La question « Qui dans l’équipe a travaillé à la fois sur les paiements et sur la détection de fraude ? » implique une navigation personne–service–personne. La recherche de similarité espère que un document indiquera directement cette intersection — ce qui n’arrive généralement pas, car ce fait n’existe que sous forme de structure graphique.

Questions sur la cause racine et la chaîne d’impact en opérations : « Quel changement en amont pourrait expliquer cette cascade affectant trois services ? » concerne les liens causaux et de dépendance, et non la ligne de journal ressemblant à une « cascade d’échecs ». Pour acquérir une véritable connaissance opérationnelle relationnelle, un graphe n’est pas un simple outil utile — c’est la représentation qui contient la réponse.

Si le trafic ne présente pas ces structures — et c’est le cas pour une grande partie des systèmes RAG en production — GraphRAG représente de la complexité sans bénéfice réel, ce qui correspond aux chiffres présentés ci-dessus.

Le piège du coût des tokens : ne superposez pas un graphe en tant que données sur une infrastructure déjà coûteuse

Le coût s’aggrave avec une autre erreur courante : les systèmes à plusieurs agents. Selon le rapport d’Anthropic sur leur système de recherche multi-agents, on comptait environ 15 fois plus de tokens par interaction que pour une simple conversation, et les agents uniques utilisant des outils représentaient déjà environ 4 fois plus que les conversations classiques. Ce qui est encore plus frappant, c’est que l’utilisation des tokens seule expliquait environ 80 % de la variation de performance entre les exécutions ; le nombre d’appels aux outils et le choix du modèle permettaient d’expliquer près de 95 % de cette variation. Les écarts de performance tenaient principalement au nombre de tokens consommés, et non à la complexité de l’orchestration mise en œuvre.

Cela met en garde contre les architectures multi-agents en général. Le problème devient particulièrement critique lorsqu’on superpose l’approche graph-as-data à plusieurs agents : GraphRAG ajoute une charge en tokens des deux côtés — pour l’extraction et la construction au préalable, ainsi que pour l’assemblage d’un contexte plus riche à plusieurs étapes au moment de la requête. Si l’on ajoute cela à une configuration qui consomme déjà environ 15 fois plus de tokens pour des appels simples sans mesurer l’impact, les coûts peuvent augmenter d’un ordre de grandeur tandis que la qualité dans des scénarios réels reste inchangée — car la plupart des requêtes consistent en de simples recherches pour lesquelles la récupération via un graphe n’est d’aucune aide.

Le remède est peu attrayant et souvent négligé : il faut mesurer l’écart en nombre de tokens généré par l’ajout du graphique en tant que données, sur la distribution réelle des questions, par rapport à la base de référence multi-agents avant de procéder. Si le trafic est principalement de type recherche simple, où GraphRAG ne fait que combiner des opérations de récupération, d’extraction et de formulation de requête sans apporter de gain réel, cette mesure le montre — et fournit un chiffre permettant de justifier les coûts plutôt que de se fier à une intuition.

La rigueur dans les mesures est aussi importante que le choix du graphique : il convient d’utiliser un ensemble de questions fixe, d’évaluer autant que possible avec des réponses vérifiables et graduées, et de noter les écarts en nombre de tokens ainsi que ceux liés à la qualité, afin que coûts et précision soient présentés dans le même tableau.

La discipline de mesure est tout aussi importante que le choix du graphique : maintenez un ensemble de questions fixe, évaluez avec des réponses vérifiables et notées autant que possible, et enregistrez les écarts de tokens à côté des écarts de qualité afin que le coût et la précision figurent dans le même tableau.

La discipline de mesure est tout aussi importante que le choix du graphique : maintenez un ensemble de questions fixe, évaluez avec des réponses vérifiables et notées autant que possible, et enregistrez les écarts de tokens à côté des écarts de qualité afin que le coût et la précision figurent dans le même tableau.

La discipline de mesure est tout aussi importante que le choix du graphique : maintenez un ensemble de questions fixe, évaluez avec des réponses vérifiables et notées autant que possible, et enregistrez les écarts de tokens à côté des écarts de qualité afin que le coût et la précision figurent dans le même tableau.

La discipline de mesure est tout aussi importante que le choix du graphique : maintenez un ensemble de questions fixe, évaluez avec des réponses vérifiables et notées autant que possible, et enregistrez les écarts de tokens à côté des écarts de qualité afin que le coût et la précision figurent dans le même tableau.

La discipline de mesure est tout aussi importante que le choix du graphique : maintenez un ensemble de questions fixe, évaluez avec des réponses vérifiables et notées autant que possible, et enregistrez les écarts de tokens à côté des écarts de qualité afin que le coût et la précision figurent dans le même tableau.

La discipline de mesure est tout aussi importante que le choix du graphique : maintenez un ensemble de questions fixe, évaluez avec des réponses vérifiables et notées autant que possible, et enregistrez les écarts de tokens à côté des écarts de qualité afin que le coût et la précision figurent dans le même tableau.

Une approche progressive pour l’adopter réellement

Un ordre de construction plus judicieux que « les graphiques d’abord » :

Démarrez immédiatement avec graph as architecture pour le flux de contrôle, que l’on prévoit ou non un graphique de connaissances. Cela coûte peu — une dépendance et quelques types d’état — et porte ses fruits dès qu’un agent dispose de plus de quelques étapes ou de branches. Les points d’arrêt et les interruptions ne sont pas des luxes pour la fin du projet ; ce sont eux qui permettent aux agents de résister aux pannes et aux pauses dues à la révision humaine. Ajouter ultérieurement une machine d’état explicite nuit davantage que de commencer par en utiliser une.

Ajoutez graph as data uniquement après des questions complexes à plusieurs étapes où il a été vérifié — et non supposé — que la recherche vectorielle échoue. Cette vérification est essentielle : mettez les candidats à l’épreuve avec des systèmes de récupération existants, identifiez leurs limites, et déterminez si le problème provient du parcours des relations ou d’autre chose (mauvaise segmentation, manque de matériel). De nombreux rapports indiquant que « RAG ne parvient pas à trouver la réponse » relèvent du second cas ; un graphe de connaissances ne corrige pas une mauvaise segmentation.

Au moment de mettre en place une base de données graphiques, effectuez des tests de comparaison avec un système RAG classique sur des données et des questions propres, en utilisant des comparaisons évaluées et vérifiables dans l’esprit de GraphRAG-Bench plutôt que des taux de victoire par paires où l’LLM agit en juge — étant donné à quel point ce style d’évaluation peut être peu fiable après un audit des biais. Si un graphe de connaissances ne surpasse pas clairement une base vectorielle ou à mots-clés bien ajustée pour les questions qui comptent, il n’y a pas encore de raison d’utiliser le graphe comme source de données, quel que soit l’intérêt théorique de cette idée.

Les graphes restent constamment utiles. Le changement réside dans l’arrêt de l’habitude d’imaginer à l’avance quel type est nécessaire, sans d’abord examiner ce que le problème concret exige réellement.