Appels d'outils en parallèle : réduire l'attente
Sur cette page
Objectifs
À l’issue de cette leçon, vous serez capable de :
- Traiter tous les appels d’outils d’une réponse, et les exécuter en même temps quand ils sont indépendants
- Renvoyer les résultats dans la forme qui préserve les appels parallèles
- Renvoyer une erreur comme résultat, sans casser la boucle
Plusieurs appels dans une seule réponse
Exécuter les appels un par un fait attendre l’utilisateur à chaque fois. Or Claude peut demander plusieurs outils dans une même réponse : c’est le comportement par défaut. Prenez un script d’Ingénova qui vérifie si un chantier est prêt : il lui faut l’état du projet, la météo sur le site et l’état du permis. Ces trois recherches ne dépendent pas les unes des autres. Claude les demande dans une seule réponse, avec trois blocs tool_use ; stop_reason vaut tool_use, et chaque appel a son propre id.
Traiter chaque appel, en même temps
Votre boucle doit traiter chaque bloc tool_use de la réponse : si elle n’en exécute qu’un, les autres n’auront jamais de résultat, et l’API rejettera la requête suivante. Et comme chaque recherche passe surtout son temps à attendre un autre système, donnez-lui son propre fil d’exécution : les attentes se recouvrent au lieu de s’additionner.
from concurrent.futures import ThreadPoolExecutor
appels = [b for b in reponse.content if b.type == "tool_use"]
with ThreadPoolExecutor() as pool:
resultats = list(pool.map(executer_outil, appels))
messages.append({"role": "assistant", "content": reponse.content})
messages.append({"role": "user", "content": resultats})
Exécuter en parallèle est généralement sans risque quand les appels ne font que lire et ne dépendent pas les uns des autres. La documentation signale une nuance de modèle : dans les longues boucles d’agent, Claude Fable 5.1 peut faire moins d’appels parallèles que les modèles précédents, et une consigne de regroupement des lectures indépendantes y remédie. Quand ils modifient des données ou doivent suivre un ordre, exécutez-les à la suite : c’est à votre boucle d’en décider, pas au modèle.
Tous les résultats dans un seul message
Chaque résultat est un bloc tool_result dont le tool_use_id reprend l’id de l’appel auquel il répond. Mettez tous les résultats dans le même message utilisateur. Si votre boucle envoie chaque résultat dans son propre message, la conversation ressemble à une suite d’appels un par un, et Claude se met à faire moins d’appels parallèles. N’ajoutez pas non plus de texte avant les résultats dans ce message.
Une erreur est un résultat comme un autre
Trois appels simultanés, c’est la possibilité qu’un échoue pendant que les deux autres réussissent. Si une exception remonte sans être attrapée, la boucle s’arrête et les deux bons résultats sont perdus. Renvoyez l’échec comme un tool_result ordinaire, avec le message d’erreur en contenu et is_error: true ; dites ce qui s’est passé et ce que Claude peut essayer ensuite.
def executer_outil(appel):
resultat = {"type": "tool_result", "tool_use_id": appel.id}
try:
resultat["content"] = OUTILS[appel.name](**appel.input)
except Exception as exc:
resultat["content"] = f"{appel.name} a échoué : {exc}. Vérifiez les paramètres ou réessayez plus tard."
resultat["is_error"] = True
return resultat
Si vous sautez un appel parce qu’un précédent a échoué, renvoyez quand même un résultat d’erreur pour lui : Claude doit savoir que l’appel n’a pas eu lieu.
Désactiver le parallélisme, si nécessaire
Pour limiter Claude à un seul appel par réponse, utilisez disable_parallel_tool_use: true dans l’objet tool_choice ; ce n’est pas un paramètre de premier niveau de la requête.
tool_choice={"type": "auto", "disable_parallel_tool_use": True}
Si vous n’avez pas besoin d’une boucle sur mesure, le tool runner des SDK (en bêta) gère toute la boucle d’outils pour vous, avec des points d’intervention à chaque tour pour valider, journaliser ou modifier les résultats.
À vous de jouer
Chronométrer votre boucle.
- Repérez une demande de votre agent qui déclenche plusieurs recherches indépendantes.
- Mesurez le temps total avec des appels exécutés un par un, puis en parallèle.
- Vérifiez que vos résultats repartent dans un seul message, et qu’un appel en échec renvoie un résultat d’erreur.
Sources officielles : Parallel tool use (documentation Claude : un seul message de résultats,
disable_parallel_tool_use, Fable 5.1) · Handle tool calls (documentation Claude :is_error) · Tool runner (documentation Claude)
Points clés à retenir
- Traitez chaque appel d’une réponse, et exécutez en même temps ceux qui sont indépendants ; à la suite ceux qui modifient ou dépendent d’un autre
- Un
tool_resultpar appel, tous dans le même message utilisateur : c’est ce qui préserve les appels parallèles - Une erreur se renvoie comme résultat avec
is_error: true, pour ne pas arrêter la boucle disable_parallel_tool_usese place danstool_choice, pas au premier niveau