Handoffs : orchestrer plusieurs agents
Handoffs : orchestrer plusieurs agents
Un seul agent ne peut pas tout faire. Les handoffs permettent à un agent de déléguer la conversation à un autre agent spécialisé. C’est le mécanisme clé pour construire des systèmes multi-agents où chaque agent excelle dans son domaine.
Le concept de handoff
Un handoff est un transfert de contrôle d’un agent à un autre. Quand l’agent A fait un handoff vers l’agent B, c’est l’agent B qui prend la main et produit la réponse. Le Runner gère cette transition automatiquement.
from agents import Agent, Runner
agent_facturation = Agent(
name="Agent facturation",
instructions="""Vous gérez les questions de facturation : factures, paiements,
remboursements, abonnements. Répondez de manière précise et professionnelle.""",
model="gpt-5.3",
)
agent_technique = Agent(
name="Agent technique",
instructions="""Vous gérez le support technique : bugs, configuration,
installation, dépannage. Donnez des instructions étape par étape.""",
model="gpt-5.3",
)
agent_triage = Agent(
name="Agent de triage",
instructions="""Vous êtes le premier point de contact.
Analysez la demande du client et transférez-la à l'agent approprié.
- Questions de facturation → Agent facturation
- Questions techniques → Agent technique""",
handoffs=[agent_facturation, agent_technique],
model="gpt-5.3",
)
result = Runner.run_sync(
agent_triage,
"Ma dernière facture semble incorrecte, le montant est trop élevé."
)
# L'agent de triage fait un handoff vers l'agent facturation
print(f"Répondu par : {result.last_agent.name}")
print(result.final_output)
Personnaliser la description du handoff
Par défaut, le handoff utilise le nom et les instructions de l’agent cible. Vous pouvez personnaliser la description pour guider le routage :
from agents import Handoff
agent_triage = Agent(
name="Agent de triage",
instructions="Transférez la demande à l'agent spécialisé.",
handoffs=[
Handoff(
agent=agent_facturation,
description="Transfert pour les questions de paiement, factures, remboursements et abonnements.",
),
Handoff(
agent=agent_technique,
description="Transfert pour les problèmes techniques, bugs et demandes d'aide à la configuration.",
),
],
)
Handoffs avec contexte
Quand un agent fait un handoff, l’historique de conversation est transmis à l’agent cible. Cela signifie que le nouvel agent a accès à tout ce qui a été dit :
from dataclasses import dataclass
from agents import Agent, Runner, RunContextWrapper, function_tool
@dataclass
class ContexteClient:
client_id: str
plan: str
historique_tickets: list[str]
@function_tool
def consulter_facture(ctx: RunContextWrapper[ContexteClient], mois: str) -> str:
"""Consulte la facture d'un mois donné pour le client courant."""
client_id = ctx.context.client_id
return f"Facture de {mois} pour {client_id}: 149.99€ (plan {ctx.context.plan})"
agent_facturation = Agent(
name="Agent facturation",
instructions="Vous gérez la facturation. Utilisez le contexte client.",
tools=[consulter_facture],
model="gpt-5.3",
)
agent_triage = Agent(
name="Triage",
instructions="Transférez au bon agent.",
handoffs=[agent_facturation],
model="gpt-5.3",
)
contexte = ContexteClient(
client_id="cli_456",
plan="Pro",
historique_tickets=["Ticket #001: Connexion lente"]
)
result = Runner.run_sync(
agent_triage,
"Pouvez-vous vérifier ma facture de mars ?",
context=contexte,
)
Pattern : triage à trois niveaux
Voici un pattern production avec trois niveaux de support :
agent_niveau3 = Agent(
name="Expert technique L3",
instructions="""Vous êtes un expert technique de niveau 3.
Vous traitez les problèmes complexes d'architecture et d'infrastructure.
Si vous ne pouvez pas résoudre, indiquez qu'une escalade humaine est nécessaire.""",
model="gpt-5.4", # Le modèle le plus capable pour les cas complexes
)
agent_niveau2 = Agent(
name="Support technique L2",
instructions="""Vous êtes un technicien de niveau 2.
Vous traitez les problèmes de configuration avancée.
Si le problème est trop complexe, transférez au niveau 3.""",
handoffs=[agent_niveau3],
model="gpt-5.3",
)
agent_niveau1 = Agent(
name="Support client L1",
instructions="""Vous êtes le premier contact du support client.
Traitez les questions simples (mot de passe, compte, navigation).
Transférez les problèmes techniques au niveau 2.""",
handoffs=[agent_niveau2],
model="gpt-5.3",
)
Handoffs bidirectionnels
Deux agents peuvent se transférer mutuellement le contrôle :
agent_vente = Agent(
name="Agent vente",
instructions="""Vous vendez des produits. Si le client demande
du support technique, transférez à l'agent technique.""",
)
agent_support = Agent(
name="Agent support",
instructions="""Vous faites du support technique. Si le client
veut acheter quelque chose, transférez à l'agent vente.""",
)
# Handoffs bidirectionnels
agent_vente.handoffs = [agent_support]
agent_support.handoffs = [agent_vente]
Attention aux boucles infinies : un guardrail ou un max_turns est recommandé.
Suivre les handoffs
Le RunResult vous indique quel agent a finalement répondu :
result = Runner.run_sync(agent_triage, "J'ai un bug critique")
print(f"Agent initial : agent_triage")
print(f"Agent final : {result.last_agent.name}")
# Affiche "Agent technique" si le triage a bien fonctionné
Points clés à retenir
- Les handoffs permettent de déléguer la conversation à un agent spécialisé
- L’historique de conversation est transmis à l’agent cible
- Utilisez
Handoff(agent=..., description=...)pour guider le routage - Le contexte partagé (
RunContextWrapper) est accessible par tous les agents result.last_agent.nameindique quel agent a finalement répondu- Protégez les handoffs bidirectionnels avec
max_turnscontre les boucles