Appel d'outils en parallèle : réduire la latence
Claude peut demander plusieurs outils dans une seule réponse, et votre code peut les exécuter ensemble. La vidéo vérifie un site de chantier pour Meridian, un cabinet de conseil en ingénierie fictif. Un appel d'outil à la fois prend 19,9 secondes, tandis que les appels d'outils parallèles prennent 10,8 secondes.
La démonstration de cette vidéo a été enregistrée en septembre 2026. Elle s'exécutait sur Claude Sonnet 5 (claude-sonnet-5). Les résultats peuvent différer sur des modèles plus récents.
Exécuter les appels d'outils un par un oblige votre utilisateur à attendre chacun d'eux à son tour.
Lorsque les outils que Claude souhaite utiliser ne dépendent pas les uns des autres, il peut tous les demander dans une seule réponse. Claude procède ainsi par défaut, votre boucle doit donc traiter chaque appel d'outil et renvoyer un résultat pour chacun.
Trois appels d'outils dans une seule réponse
L'utilisation d'outils parallèle signifie qu'une seule réponse contient plusieurs blocs tool_use.
Meridian dispose d'un script Python qui vérifie si un site de chantier est prêt pour le travail sur le terrain. La vérification du site nécessite trois recherches qui ne dépendent pas les unes des autres : le statut du projet, la météo sur le site et l'état du permis.
Voici la réponse de Claude à la vérification du site de Meridian, avec un bloc tool_use pour chaque outil qu'il souhaite utiliser.
{
"role": "assistant",
"stop_reason": "tool_use",
"content": [
{"type": "tool_use", "id": "toolu_01", "name": "get_project_status",
"input": {"job_number": "MEG-2026-0142"}},
{"type": "tool_use", "id": "toolu_02", "name": "get_site_weather",
"input": {"site": "Riverside"}},
{"type": "tool_use", "id": "toolu_03", "name": "get_permit_state",
"input": {"permit_id": "P-88213"}}
]
}Le stop_reason de la réponse est tool_use, et chaque appel d'outil possède son propre id.
Exécuter chaque appel d'outil
Une boucle écrite pour un seul appel d'outil ne lit que le premier bloc tool_use. Elle renvoie un résultat, deux appels d'outils n'obtiennent jamais le leur, et l'API rejette la requête. Il faut donc collecter chaque bloc tool_use de la réponse.
calls = [b for b in resp.content if b.type == "tool_use"]Exécutez ensuite ces appels d'outils en même temps. Chaque recherche passe la majeure partie de son temps à attendre un autre système. Donnez à chacun son propre thread, et les trois attentes se chevauchent au lieu de s'additionner.
with ThreadPoolExecutor() as pool:
results = list(pool.map(run_tool, calls))run_tool exécute un appel d'outil et renvoie son résultat, et les deux sections suivantes le construisent.
Exécuter des appels d'outils en même temps est généralement sûr lorsqu'ils se contentent de lire des données et ne dépendent pas les uns des autres, comme les recherches de Meridian. Lorsque les outils modifient des données, ou doivent s'exécuter dans un ordre défini, il peut être préférable de les exécuter les uns après les autres.
Renvoyer chaque résultat dans un seul message
Une fois les trois appels d'outils exécutés, Claude attend leurs résultats. Chaque résultat est renvoyé dans un bloc tool_result, et son tool_use_id contient l'id de l'appel d'outil auquel il répond.
result = {"type": "tool_result", "tool_use_id": call.id}Chaque bloc tool_use reçoit un tool_result, et ils sont tous placés dans le message utilisateur suivant.
messages.append({"role": "assistant", "content": resp.content})
messages.append({"role": "user", "content": results})
Un seul message utilisateur contenant les trois résultats permet à Claude de continuer à appeler les outils en parallèle.
Si votre boucle envoie plutôt chaque résultat dans son propre message, la conversation donne l'impression que Claude avait demandé un seul outil à la fois. C'est une raison courante pour laquelle Claude effectue moins d'appels d'outils parallèles.
Renvoyer un résultat d'erreur pour un appel d'outil défaillant
Exécuter trois appels d'outils à la fois signifie que l'un peut échouer tandis que les deux autres réussissent. Si votre code lève une exception et que rien ne la capture, votre boucle s'arrête, et les deux bons résultats sont perdus avec elle.
Renvoyez donc l'échec sous forme de résultat. Il s'agit d'un tool_result ordinaire avec le message d'erreur comme content, ainsi que is_error: true. Le message doit indiquer ce qui s'est mal passé et ce que Claude doit essayer ensuite.
try:
result["content"] = HANDLERS[call.name](**call.input)
except Exception as exc:
result["content"] = f"{call.name} failed: {exc}"
result["is_error"] = TrueHANDLERS est un dictionnaire associant les noms d'outils aux fonctions Python qui effectuent le travail de chaque outil. Chaque fonction renvoie une chaîne de caractères, car le content d'un tool_result est une chaîne de caractères ou une liste de blocs de contenu.
Assembler la boucle
Les extraits de code ci-dessus s'intègrent dans la boucle de Meridian ci-dessous. Le script a déjà configuré client, tools, prompt et HANDLERS.
Points clés à retenir
- Exécutez chaque appel d'outil d'une réponse, et exécutez-les en même temps lorsqu'ils ne dépendent pas les uns des autres.
- Renvoyez un
tool_resultpour chaque appel d'outil, et placez-les tous dans un seul message utilisateur. - N'envoyez pas chaque résultat dans son propre message, car cela réduit l'utilisation d'outils parallèle.
- Renvoyez un échec sous forme de résultat avec
is_error: true, afin qu'un appel d'outil défaillant n'arrête pas la boucle.