Changer le thème

LangGraph multi-agents en pratique : mode Supervisor et distribution des tâches

Easton editorial illustration: Supervisor baton, research brief card, research station, writing station, synthesis tray

Le mois dernier, j’ai aidé l’équipe à refactoriser un système Agent : un seul Agent avec 12 outils — recherche, exécution de code, génération de documents, envoi d’e-mails… Résultat ? Le LLM hésitait souvent entre les outils et lançait l’exécuteur de code là où il aurait fallu chercher. En déboguant les logs, impossible de savoir quel appel posait problème — tout le système était une boîte noire.

On a ensuite découpé l’architecture en mode Supervisor + Workers : un Agent de contrôle pour le routage, trois Agents experts chacun dans son rôle. Le taux d’erreur de sélection d’outils est tombé à environ un tiers, et le débogage est bien plus lisible — chaque couche se trace niveau par niveau.

En clair : dès qu’un Agent dépasse une dizaine d’outils, l’architecture mono-Agent montre ses limites. Cet article vous emmène de la logique du mode Supervisor à l’usage complet de l’API create_supervisor, jusqu’à un cas pratique d’équipe Research + Writing. Le code est exécutable tel quel ; le lien du dépôt GitHub est en fin d’article.

1. Pourquoi un système multi-agents ?

Les trois pièges du mono-Agent

Les écueils que j’ai rencontrés, vous les connaissez peut-être déjà. Le mono-Agent a l’air simple, mais trois problèmes sont souvent fatals :

Premier piège : trop d’outils, paralysie du choix.

Ce n’est pas une exagération. Au-delà d’une dizaine d’outils par Agent, le taux d’erreur de sélection monte nettement. Les modèles progressent, oui — mais les descriptions d’outils s’empilent dans le prompt : choisir le bon parmi 10+, c’est une charge cognitive non négligeable.

Dans mon ancien système, recherche et exécution de code se chevauchaient un peu (« trouver de l’information »), et le modèle oscillait entre les deux, gaspillant plusieurs tours de dialogue.

Deuxième piège : accumulation de contexte, fenêtre saturée.

Tout l’historique des sous-tâches se compresse dans une seule fenêtre de contexte. Après quelques tours, la fenêtre est noyée sous les appels d’outils ; l’instruction principale se dilue. Le modèle « oublie », parfois même la demande initiale.

J’ai vécu ça en déboguant un Agent après 20 tours : le contexte regorgeait d’appels de fonctions, et la sortie finale n’avait plus grand rapport avec le besoin initial.

Troisième piège : débogage opaque.

En cas de problème, impossible de savoir quel appel d’outil est en cause. Mono-Agent = boîte noire : des logs en rafale, et il faut tout parcourir ligne par ligne.

Le mode Supervisor adresse ces points : un « Agent de contrôle » coordonne plusieurs « Agents experts », chacun avec un périmètre clair.

L’idée centrale du mode Supervisor

En bref : c’est du partage des tâches.

Imaginez une équipe : un chef de projet coordonne, un chercheur fait l’étude de terrain, un ingénieur implémente, un rédacteur produit le rapport. Le chef n’a pas besoin de tout savoir faire — il doit savoir à qui confier quoi.

Le mode Supervisor suit la même logique :

  • Supervisor (Agent de contrôle) : pas d’exécution métier — routage, coordination, synthèse des résultats
  • Worker Agents (Agents experts) : un domaine chacun, jeu d’outils réduit, rôle explicite

Les bénéfices :

Les outils sont répartis entre les Workers ; chaque Agent choisit dans un petit ensemble. Le contexte aussi est fragmenté — chaque Agent ne garde qu’une partie de l’historique. En débogage, on trace couche par couche : quel Worker le Supervisor a mandaté, ce que le Worker a exécuté.

2. Architecture du mode Supervisor

Schéma d’ensemble :

                    ┌─────────────────┐
                    │   Requête       │
                    │   utilisateur   │
                    └────────┬────────┘


                    ┌─────────────────┐
                    │   Supervisor    │
                    │ (Agent contrôle)│
                    │                 │
                    │ Routage + coord.│
                    │ + synthèse      │
                    └────────┬────────┘

              ┌──────────────┼──────────────┐
              │              │              │
              ▼              ▼              ▼
       ┌──────────┐   ┌──────────┐   ┌──────────┐
       │ Research │   │   Math   │   │ Writing  │
       │  Agent   │   │  Agent   │   │  Agent   │
       │          │   │          │   │          │
       │ Recherche│   │ Calcul   │   │ Génération│
       └────┬─────┘   └────┬─────┘   └────┬─────┘
            │              │              │
            │   Worker     │   Worker     │   Worker
            │   résultats  │   résultats  │   résultats
            │              │              │
            └──────────────┴──────────────┘


                    ┌─────────────────┐
                    │   Supervisor    │
                    │   Synthèse      │
                    └────────┬────────┘


                    ┌─────────────────┐
                    │   Réponse       │
                    │   finale        │
                    └─────────────────┘

Rôles des composants

Le Supervisor fait trois choses :

  1. Routage : analyser la requête et choisir le Worker adapté
  2. Coordination : orchestrer le flux entre Workers
  3. Synthèse : agréger les résultats et produire la réponse finale

Chaque Worker Agent :

Chaque Worker n’a que ses outils dédiés. Par exemple Research n’a que recherche et scraping web ; Math n’a qu’une calculatrice. Moins d’outils, meilleure précision de choix.

Mécanisme de messages

Point clé : l’état global du graphe (Global Graph State).

Tous les Agents partagent le même objet d’état. Après exécution, le Worker ajoute son résultat au champ messages. Le Supervisor lit le nouveau message et décide de la suite.

Mécanisme append-only — les messages ne sont pas supprimés, l’historique reste complet.

Fan-out / Fan-in

Certaines tâches exigent plusieurs Workers en parallèle. Exemple : « comparer les parts de marché des produits A et B » — le Supervisor peut lancer deux recherches (A et B) : c’est le fan-out.

Quand les deux Workers ont répondu, le Supervisor fusionne : fan-in.

LangGraph gère ce parallélisme ; dans cet article de base, on ne l’approfondit pas — voir la section techniques avancées.

"LangGraph offre une façon de construire des systèmes multi-agents où chaque Agent dispose de son propre jeu d’outils et de son périmètre, coordonnés par un Supervisor pour la distribution des tâches."

3. API create_supervisor en détail

La théorie posée, passons au code.

Installation et imports

pip install langgraph-supervisor langchain-openai
from langchain_openai import ChatOpenAI
from langgraph_supervisor import create_supervisor
from langgraph.prebuilt import create_react_agent

Définir les outils

Préparez les outils pour chaque Worker :

from typing import Annotated

# Outils de calcul
def add(
    a: Annotated[float, "Premier nombre"],
    b: Annotated[float, "Deuxième nombre"]
) -> float:
    """Add two numbers together."""
    return a + b

def multiply(
    a: Annotated[float, "Premier nombre"],
    b: Annotated[float, "Deuxième nombre"]
) -> float:
    """Multiply two numbers."""
    return a * b

# Outil de recherche (simulé)
def web_search(query: str) -> str:
    """Search the web for information."""
    # En production : Tavily, Serper, etc.
    if "population" in query.lower():
        return "北京人口约 2189 万(2023 年数据)"
    elif "weather" in query.lower():
        return "北京今日晴,气温 15-25°C"
    else:
        return f"搜索结果:{query}"

Les annotations Annotated clarifient chaque paramètre pour le modèle. La docstring de l’outil compte aussi — le modèle s’en sert pour comprendre la fonction.

Créer les Worker Agents

model = ChatOpenAI(model="gpt-4o")

# Agent expert mathématiques
math_agent = create_react_agent(
    model=model,
    tools=[add, multiply],
    name="math_expert",
    prompt="Vous êtes un expert en mathématiques, focalisé sur le calcul numérique. Pour les opérations, utilisez vos outils."
)

# Agent expert recherche
research_agent = create_react_agent(
    model=model,
    tools=[web_search],
    name="research_expert",
    prompt="Vous êtes un chercheur senior, apte à chercher et structurer l'information. Pour les requêtes documentaires, utilisez l'outil de recherche."
)

Points d’attention :

  1. Le champ name est crucial : le Supervisor identifie et appelle les Workers par leur nom
  2. Le prompt définit le rôle : spécialité de l’Agent
  3. Jeu d’outils minimal : uniquement le nécessaire

Créer le Supervisor

# Système Supervisor
supervisor = create_supervisor(
    agents=[math_agent, research_agent],
    model=model,
    prompt="""Vous êtes le leader d'équipe et coordonnez les Agents experts.

Selon la requête utilisateur, décidez à qui confier la tâche :
- calcul mathématique → math_expert
- recherche documentaire → research_expert
- tâche terminée → répondre directement à l'utilisateur

Si plusieurs experts doivent collaborer, appelez-les dans un ordre logique."""
)

# Compiler en application exécutable
app = supervisor.compile()

create_supervisor prend trois paramètres centraux :

  • agents : liste des Worker Agents
  • model : LLM du Supervisor
  • prompt : consignes de distribution des tâches

Exemple d’exécution

from langchain_core.messages import HumanMessage

# Test calcul
result = app.invoke({
    "messages": [HumanMessage(content="计算 123 加 456 等于多少")]
})
print(result["messages"][-1].content)
# Sortie : 123 加 456 等于 579

# Test recherche
result = app.invoke({
    "messages": [HumanMessage(content="北京的人口是多少")]
})
print(result["messages"][-1].content)
# Sortie : 根据搜索结果,北京人口约 2189 万

Le Supervisor détecte le type de requête et route vers le bon Worker. Transparent pour l’utilisateur — il n’a pas besoin de savoir qu’il y a plusieurs Agents derrière.

4. Cas pratique : équipe Research + Writing

Exemple de base vu ; construisons un système plus complet : une équipe qui recherche et rédige automatiquement un article technique.

Scénario

L’utilisateur saisit un sujet technique. Le système :

  1. Recherche les sources
  2. Génère le plan
  3. Rédige le contenu
  4. Relit et valide

Trois Agents spécialisés collaborent.

Jeu d’outils complet

from typing import TypedDict, List
import json

# Outil de recherche simulé
def tech_search(query: str) -> str:
    """Rechercher documentation et ressources techniques."""
    # En production : Tavily ou Serper
    database = {
        "langgraph": "LangGraph est le framework Agent de LangChain, avec gestion d'état et graphes cycliques.",
        "supervisor": "Le mode Supervisor est l'architecture centrale multi-agents : un Agent de contrôle coordonne des experts.",
        "multi-agent": "Les systèmes multi-agents distribuent les tâches pour éviter trop d'outils et l'explosion de contexte sur un seul Agent."
    }

    results = []
    for key, value in database.items():
        if key in query.lower():
            results.append(value)

    return json.dumps(results) if results else "Aucune ressource trouvée ; élargir la recherche"

# Génération de plan
def generate_outline(topic: str) -> str:
    """Générer un plan d'article selon le sujet."""
    return json.dumps({
        "title": f"Guide complet : {topic}",
        "sections": [
            "1. Contexte et enjeux",
            "2. Concepts clés",
            "3. Cas pratique",
            "4. Bonnes pratiques",
            "5. Synthèse"
        ]
    }, ensure_ascii=False)

# Génération de contenu
def write_section(section_title: str, context: str) -> str:
    """Rédiger un paragraphe à partir du titre et du contexte."""
    # En production : appel LLM
    return f"## {section_title}\n\nÀ partir des sources, points clés de {section_title}...\n\n"

# Outil de relecture
def review_content(content: str) -> str:
    """Vérifier exactitude et lisibilité."""
    issues = []
    if len(content) < 100:
        issues.append("Contenu trop court, à développer")
    if "TODO" in content:
        issues.append("Marqueurs TODO non résolus")

    return json.dumps({
        "passed": len(issues) == 0,
        "issues": issues,
        "suggestion": "Qualité OK, publication possible" if not issues else "Corriger puis resoumettre"
    }, ensure_ascii=False)

Créer les Worker Agents

# Agent chercheur
researcher = create_react_agent(
    model=model,
    tools=[tech_search],
    name="researcher",
    prompt="""Vous êtes chercheur technique senior, rapide pour explorer de nouvelles technologies.

Missions :
1. Recevoir le sujet de recherche
2. Utiliser l'outil de recherche
3. Produire un rapport structuré

Note : recherche uniquement, pas de rédaction. Transmettre le rapport au writer."""
)

# Agent rédacteur
writer = create_react_agent(
    model=model,
    tools=[generate_outline, write_section],
    name="writer",
    prompt="""Vous êtes rédacteur technique, capable de rendre les concepts complexes lisibles.

Missions :
1. Recevoir le rapport de recherche
2. Générer le plan
3. Rédiger chaque section

Note : après le brouillon, passer au reviewer."""
)

# Agent relecteur
reviewer = create_react_agent(
    model=model,
    tools=[review_content],
    name="reviewer",
    prompt="""Vous êtes relecteur exigeant sur la qualité et l'exactitude.

Missions :
1. Vérifier exhaustivité et exactitude
2. Évaluer lisibilité et logique
3. Valider la publication ou demander des corrections

Note : en cas de problème, renvoyer au writer."""
)

Logique Supervisor personnalisée

# Supervisor custom avec StateGraph
from langgraph.graph import StateGraph, END
from typing import Annotated, Sequence
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages

# État
class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]
    next_agent: str

# Nœud de décision Supervisor
def supervisor_node(state: AgentState) -> dict:
    """Décider quel Agent exécuter selon la progression."""
    messages = state["messages"]

    decision = model.invoke([
        {"role": "system", "content": """Vous êtes le leader d'équipe. Selon l'historique, décidez la prochaine étape :

- pas encore de recherche → 'researcher'
- recherche faite mais pas d'article → 'writer'
- article rédigé mais pas relu → 'reviewer'
- relecture OK → 'FINISH'

Retournez uniquement le nom de l'Agent, rien d'autre."""},
        *messages
    ])

    next_agent = decision.content.strip()

    agent_map = {
        "researcher": "researcher",
        "writer": "writer",
        "reviewer": "reviewer",
        "FINISH": END
    }

    return {"next_agent": agent_map.get(next_agent, "researcher")}

# Construire le graphe
workflow = StateGraph(AgentState)

workflow.add_node("supervisor", supervisor_node)
workflow.add_node("researcher", researcher)
workflow.add_node("writer", writer)
workflow.add_node("reviewer", reviewer)

workflow.add_conditional_edges(
    "supervisor",
    lambda state: state["next_agent"],
    {
        "researcher": "researcher",
        "writer": "writer",
        "reviewer": "reviewer",
        END: END
    }
)

for agent in ["researcher", "writer", "reviewer"]:
    workflow.add_edge(agent, "supervisor")

workflow.set_entry_point("supervisor")

app = workflow.compile()

Boucle typique : chaque Worker revient au Supervisor, qui choisit le prochain Worker ou la fin.

Flux d’exécution

result = app.invoke({
    "messages": [HumanMessage(content="写一篇关于 LangGraph Supervisor 模式的技术文章")]
})

print(result["messages"][-1].content)

for i, msg in enumerate(result["messages"]):
    print(f"{i+1}. {msg.__class__.__name__}: {msg.content[:100]}...")

Flux approximatif :

Requête → Supervisor analyse → Researcher →
retour Supervisor → Writer →
retour Supervisor → Reviewer →
retour Supervisor → fin → résultat

Chaque étape est traçable — le débogage devient nettement plus simple.

5. Techniques avancées

Optimisation des messages : create_forward_message_tool

Problème fréquent : après exécution, le Worker renvoie un message que le Supervisor peut reformuler — gaspillage de tokens et risque de dilution.

LangGraph propose create_forward_message_tool :

from langgraph_supervisor.handoff import create_forward_message_tool

forward_tool = create_forward_message_tool("supervisor")

supervisor = create_supervisor(
    agents=[researcher, writer, reviewer],
    model=model,
    tools=[forward_tool]
)

Le Supervisor peut transmettre la réponse du Worker à l’utilisateur sans la résumer. Moins de tokens, plus efficace.

Architecture hiérarchique

Pour des projets plus lourds, plusieurs niveaux de Supervisor :

                    ┌──────────────┐
                    │ Top Supervisor│
                    └──────┬───────┘

           ┌───────────────┼───────────────┐
           │               │               │
           ▼               ▼               ▼
    ┌────────────┐  ┌────────────┐  ┌────────────┐
    │ Équipe     │  │ Équipe     │  │ Équipe     │
    │ Research   │  │ Writing    │  │ QA         │
    │ Supervisor │  │ Supervisor │  │ Supervisor │
    └─────┬──────┘  └─────┬──────┘  └─────┬──────┘
          │               │               │
     ┌────┼────┐    ┌────┼────┐    ┌────┼────┐
   Web  Doc  API   Out  Cont Rev   Test Code Audit
 Search Scrp Parse line ent  iew

Chaque sous-équipe a son Supervisor ; un Supervisor racine coordonne. Adapté aux grands projets à rôles fins.

Gestion des erreurs

Échec d’un Worker ?

from langgraph.pregel import RetryPolicy

retry_policy = RetryPolicy(
    max_attempts=3,
    initial_interval=1.0,
    backoff_factor=2.0
)

app = workflow.compile(retry_policy=retry_policy)

On peut aussi ajouter dans le prompt du Supervisor :

Si un Agent échoue :
1. Enregistrer l'erreur
2. Tenter un Agent de secours
3. Après plusieurs échecs, informer l'utilisateur

Persistance d’état

Les conversations multi-tours nécessitent de sauvegarder l’état. LangGraph fournit les Checkpointer :

from langgraph.checkpoint.memory import MemorySaver

memory = MemorySaver()
app = workflow.compile(checkpointer=memory)

config = {"configurable": {"thread_id": "user-123"}}
result = app.invoke({"messages": [HumanMessage(content="...")]}, config=config)

result2 = app.invoke({"messages": [HumanMessage(content="继续上次的任务")]}, config=config)

En production : Redis ou PostgreSQL comme Checkpointer.

"Hierarchical Agent Teams montre comment construire une architecture Supervisor multi-niveaux pour des collaborations multi-agents plus complexes."

6. Déploiement en production

Monitoring et débogage

LangSmith, plateforme officielle LangChain, trace chaque étape :

import os

os.environ["LANGSMITH_API_KEY"] = "your-api-key"
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_PROJECT"] = "multi-agent-project"

Chaque exécution laisse une trace complète :

  • entrées/sorties de chaque Agent
  • paramètres et retours des outils
  • consommation de tokens
  • temps d’exécution

Indispensable en débogage — fini de parcourir des logs ligne par ligne.

Maîtrise des coûts en tokens

Le mode Supervisor aide, mais plusieurs Agents augmentent la consommation. Quelques leviers :

  1. Prompt Supervisor minimal : uniquement la logique de routage
  2. forward_message_tool : éviter les doubles synthèses
  3. Outils par Worker au strict nécessaire
  4. Limite de tours : plafond de rounds
app = workflow.compile(
    checkpointer=memory,
    interrupt_after=20
)

Intégration AWS Bedrock

Sur AWS, modèles Bedrock :

from langchain_aws import ChatBedrock

model = ChatBedrock(
    model_id="anthropic.claude-3-sonnet-20240229-v1:0",
    region_name="us-east-1"
)

supervisor = create_supervisor(
    agents=[math_agent, research_agent],
    model=model
)

Le reste du code reste identique — remplacez seulement model.

Synthèse des bonnes pratiques

  1. Commencer petit : 2–3 Agents, puis étendre
  2. Rôles sans chevauchement : périmètre clair par Worker
  3. LangSmith dès le dev : traçage immédiat
  4. Surveiller les tokens : le multi-agent amplifie la facture
  5. forward_message_tool : économies substantielles
  6. Dépôt officiel : langgraph-supervisor-py pour des exemples complets

Synthèse

Le mode Supervisor, c’est avant tout le travail en équipe — découper la grande tâche et laisser chaque Agent faire ce qu’il fait le mieux.

Des trois limites du mono-Agent (choix d’outils, contexte, débogage) à la solution Supervisor, vous avez le fil complet. L’API create_supervisor est simple une fois l’architecture comprise.

Commencez par un petit projet : deux Agents (recherche + synthèse), validez le flux, puis étendez. Branchez LangSmith — vous le remercierez au débogage.

Code complet : langgraph-supervisor-py. Tutoriel recommandé : Hierarchical Agent Teams.

Des questions ? Laissez un commentaire — je réponds dès que possible.


Références

FAQ

Quelle différence entre le mode Supervisor et un multi-agent classique ?
En multi-agent classique, chaque Agent peut répondre directement à l'utilisateur — les rôles se mélangent. Le mode Supervisor ajoute un Agent de contrôle dédié au routage et à la coordination ; les Workers n'exécutent que leur tâche.
Quand adopter le mode Supervisor ?
Dès que vous dépassez une dizaine d'outils, que la tâche couvre plusieurs domaines, ou que le débogage devient pénible. Pour les tâches simples, un mono-Agent suffit.
Le mode Supervisor augmente-t-il la consommation de tokens ?
Oui — plusieurs Agents impliquent plusieurs appels modèle. Optimisez avec forward_message_tool, des prompts courts et une limite de tours. Le gain en clarté et en précision compense souvent le surcoût.
Comment déboguer un système multi-agents ?
LangSmith en premier choix : entrées/sorties, appels d'outils, tokens. Branchez-le dès le développement — bien plus efficace que des logs bruts après coup.
Quel lien entre create_supervisor et StateGraph ?
create_supervisor est une API de haut niveau pour un Supervisor simple. StateGraph est l'API bas niveau pour routage custom et hiérarchies complexes. Les deux se combinent.
Un Worker peut-il être lui-même un Supervisor ?
Oui — architecture hiérarchique. Chaque sous-équipe a son Supervisor ; un Supervisor racine coordonne. Idéal pour les grands projets.

12 min de lecture · Publié le: 12 mai 2026 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog