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

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 :
- Routage : analyser la requête et choisir le Worker adapté
- Coordination : orchestrer le flux entre Workers
- 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 :
- Le champ
nameest crucial : le Supervisor identifie et appelle les Workers par leur nom - Le
promptdéfinit le rôle : spécialité de l’Agent - 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 Agentsmodel: LLM du Supervisorprompt: 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 :
- Recherche les sources
- Génère le plan
- Rédige le contenu
- 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 :
- Prompt Supervisor minimal : uniquement la logique de routage
forward_message_tool: éviter les doubles synthèses- Outils par Worker au strict nécessaire
- 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
- Commencer petit : 2–3 Agents, puis étendre
- Rôles sans chevauchement : périmètre clair par Worker
- LangSmith dès le dev : traçage immédiat
- Surveiller les tokens : le multi-agent amplifie la facture
forward_message_tool: économies substantielles- 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
- LangGraph Supervisor Reference
- Hierarchical Agent Teams Tutorial
- LangGraph Multi-Agent Workflows Blog
- GitHub - langgraph-supervisor-py
- Build Multi-Agent Systems with AWS Bedrock
FAQ
Quelle différence entre le mode Supervisor et un multi-agent classique ?
Quand adopter le mode Supervisor ?
Le mode Supervisor augmente-t-il la consommation de tokens ?
Comment déboguer un système multi-agents ?
Quel lien entre create_supervisor et StateGraph ?
Un Worker peut-il être lui-même un Supervisor ?
12 min de lecture · Publié le: 12 mai 2026 · Mis à jour le: 27 juil. 2026
Guide d'ingénierie AI Agent
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
LangGraph en production : Checkpoint, thread state et reprise après échec
Guide pratique 2026 sur la gestion d'état LangGraph : checkpoint, thread state, reprise après échec, comparaison AutoGen et observabilité pour concevoir une architecture Agent de production récupérable.
Partie 9 sur 16
Suivant
LangGraph vs AutoGen : comparaison du suivi d'état — Checkpoint, reprise après timeout et choix de framework
Comparaison approfondie LangGraph vs AutoGen sur le suivi d'état : 12 dimensions (Checkpoint, reprise après timeout, support distribué), cas réels, arbre de décision et code exécutable pour choisir le bon framework.
Partie 11 sur 16



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire