Aller au contenu principal

Créer des agents spécialisés et un routeur

Mis à jour le 28 juillet 2026

De la théorie à la pratique

La leçon précédente a posé le concept de handoff. Vous allez maintenant construire le système complet, dans l’ordre où il se construit réellement : d’abord les spécialistes, ensuite le routeur, puis le câblage entre eux, et enfin la conversation qui met le tout en mouvement. Cet ordre n’est pas arbitraire — le routeur ne peut pointer que vers des agents qui existent déjà.

Étape 1 : Créer les agents spécialistes

Chaque agent reçoit un domaine d’expertise précis et rien d’autre.

from mistralai import Mistral
from mistralai.models import CompletionArgs, ResponseFormat, JSONSchema
import os

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

# Agent de recherche web
web_agent = client.beta.agents.create(
    model="mistral-large-latest",
    name="web-search-agent",
    description="Agent qui recherche des informations en ligne.",
    instructions="Effectuez des recherches web précises. Citez toujours vos sources.",
    tools=[{"type": "web_search"}]
)

# Agent financier
finance_agent = client.beta.agents.create(
    model="mistral-large-latest",
    name="finance-agent",
    description="Agent spécialisé dans les questions financières.",
    instructions="""Vous êtes un expert financier.
- Utilisez des données chiffrées
- Expliquez les concepts clairement
- Signalez les risques"""
)

# Agent calculateur
calculator_agent = client.beta.agents.create(
    model="mistral-large-latest",
    name="calculator-agent",
    description="Agent pour les calculs mathématiques détaillés.",
    instructions="Expliquez chaque étape du calcul. Utilisez le code interpreter pour les calculs complexes.",
    tools=[{"type": "code_interpreter"}]
)

Les quatre champs jouent des rôles très différents et l’un d’eux mérite une vigilance particulière. Le name en kebab-case vous sert à vous : il apparaît dans vos logs et vos tests, un nom lisible vous évitera de comparer des identifiants opaques. Les instructions s’adressent au modèle une fois qu’il travaille, elles doivent donc être spécifiques au domaine — « signalez les risques » veut dire quelque chose pour un agent financier, « soyez utile » ne veut rien dire pour personne. Les tools obéissent au strict nécessaire : l’agent financier ci-dessus n’a aucun outil parce qu’il raisonne sans en avoir besoin, et lui en donner reviendrait à l’inviter à s’en servir mal à propos. La description, enfin, n’est pas de la documentation : c’est le texte que le routeur lit pour décider s’il transfère ici ou ailleurs.

Étape 2 : Créer l’agent routeur

Le routeur n’a pas d’outils. Son travail entier tient dans l’analyse de la requête et le choix du destinataire.

router_agent = client.beta.agents.create(
    model="mistral-large-latest",
    name="router-agent",
    description="Agent routeur qui distribue les requêtes.",
    instructions="""Vous êtes un orchestrateur.
Analysez chaque requête et transférez au bon agent :
- Questions nécessitant des données en ligne → web-search-agent
- Questions financières → finance-agent
- Calculs et analyses numériques → calculator-agent

Ne répondez jamais directement — transférez toujours."""
)

La dernière ligne est celle qui empêche le système de s’effondrer. Sans elle, un routeur monté sur un grand modèle répond lui-même aux questions qui lui semblent faciles, court-circuite vos spécialistes et produit des réponses sans les outils qu’ils auraient mobilisés. Vous vous retrouvez avec une architecture multi-agents dont la moitié des agents ne sert jamais.

Étape 3 : Définir les cibles de handoff

Reste à indiquer au routeur vers quels agents il a le droit de transférer.

router_agent = client.beta.agents.update(
    agent_id=router_agent.id,
    handoffs=[web_agent.id, finance_agent.id, calculator_agent.id]
)

Le paramètre handoffs prend une liste d’IDs d’agents, et le routeur peut transférer vers n’importe lequel d’entre eux. Ce n’est pas un privilège réservé au routeur : les spécialistes peuvent eux aussi déclarer leurs propres cibles.

# L'agent finance peut transférer au web search et au calculateur
finance_agent = client.beta.agents.update(
    agent_id=finance_agent.id,
    handoffs=[web_agent.id, calculator_agent.id]
)

Une chaîne comme routeur → finance → web search → calculator devient alors possible. L’agent financier, confronté à une question qui exige un taux à jour, ne rend pas la main au routeur : il va lui-même chercher le spécialiste dont il a besoin, exactement comme un consultant appelle un collègue plutôt que de repasser par l’accueil.

Étape 4 : Lancer la conversation

response = client.beta.conversations.start(
    agent_id=router_agent.id,
    inputs="Trouve le taux directeur actuel de la BCE et calcule l'intérêt composé sur 10 ans pour 100 000€."
)

# Analyser le flux
for entry in response.outputs:
    if entry.type == "agent.handoff":
        print(f"[HANDOFF] → {entry.to_agent}")
    elif entry.type == "tool.execution":
        print(f"[OUTIL] {entry.tool_name}")
    elif entry.type == "message.output":
        print(f"[RÉPONSE] {entry.content[:200]}...")

Lancez cette requête et observez la trace avant de regarder la réponse. Vous devez y voir deux handoffs et au moins une exécution d’outil ; si vous n’en voyez aucun, votre routeur a répondu de lui-même et c’est le moment de durcir ses instructions.

Le levier principal : la description

Quand le routage déraille, la description est presque toujours en cause. Le routeur ne connaît pas vos intentions, il ne lit que ce texte pour chaque agent candidat.

# Mauvais — trop vague
description="Un agent utile."

# Bon — le routeur sait quand transférer
description="Agent qui recherche des informations actuelles sur Internet et cite ses sources."

Écrivez donc chaque description comme une réponse à la question « dans quel cas dois-je appeler cet agent ? », et non comme un résumé de ce qu’il est. Le second exemple nomme la situation — informations actuelles, sur Internet — là où le premier laisse le routeur deviner. Vérifiez aussi qu’aucune paire de descriptions ne se chevauche : deux agents décrits en termes proches produiront un aiguillage instable d’une exécution à l’autre.

Exemple complet

Voici l’ensemble condensé, avec les handoffs passés directement à la création du routeur puisque les spécialistes existent déjà à ce moment-là.

from mistralai import Mistral
import os

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

# 1. Agents spécialistes
research = client.beta.agents.create(
    model="mistral-large-latest",
    name="research-agent",
    description="Recherche d'informations en ligne avec sources.",
    tools=[{"type": "web_search"}]
)

analyst = client.beta.agents.create(
    model="mistral-large-latest",
    name="analyst-agent",
    description="Analyse de données et calculs avec Python.",
    tools=[{"type": "code_interpreter"}]
)

writer = client.beta.agents.create(
    model="mistral-large-latest",
    name="writer-agent",
    description="Rédaction de rapports structurés et professionnels.",
    instructions="Rédigez des rapports clairs avec introduction, analyse et recommandations."
)

# 2. Routeur avec handoffs
router = client.beta.agents.create(
    model="mistral-large-latest",
    name="router",
    description="Orchestre les requêtes complexes.",
    instructions="Analysez la requête et transférez au spécialiste approprié.",
    handoffs=[research.id, analyst.id, writer.id]
)

# 3. Lancer
response = client.beta.conversations.start(
    agent_id=router.id,
    inputs="Analyse le marché européen du cloud computing en 2026 : taille, acteurs, tendances."
)

Points clés à retenir

  • Créez des agents spécialistes avec des descriptions précises orientées tâche
  • Le routeur utilise les descriptions pour choisir l’agent cible
  • client.beta.agents.update(handoffs=[...]) définit les cibles de transfert
  • Les handoffs peuvent être imbriqués (agent A → agent B → agent C)
  • La description de chaque agent est le levier principal de la qualité du routage