Aller au contenu principal
Leçon 9 sur 16 · L'utilisation d'outils (tool use) avec la Messages API

Appels d'outils en parallèle : réduire l'attente

Leçon 9 sur 16 Mis à jour le 5 novembre 2026 Développeur
Sur cette page
  1. Objectifs
  2. Plusieurs appels dans une seule réponse
  3. Traiter chaque appel, en même temps
  4. Tous les résultats dans un seul message
  5. Une erreur est un résultat comme un autre
  6. Désactiver le parallélisme, si nécessaire
  7. À vous de jouer
  8. Points clés à retenir

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.

Trois appels, un seul message Quatre étapes : une réponse avec trois blocs tool_use, l'exécution en même temps des appels indépendants, un seul message user avec les trois tool_result, puis la réponse de Claude. APPELS PARALLÈLES Trois appels, un seul message 1 Réponse trois blocs tool_use 2 Exécution en même temps, s'ils sont indépendants 3 Un message trois tool_result, aucun texte avant 4 Claude répond avec les trois résultats Un message par résultat « apprend » à Claude à éviter les appels parallèles : tout part ensemble.
D'après la page Parallel tool use (documentation Claude).

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.

  1. Repérez une demande de votre agent qui déclenche plusieurs recherches indépendantes.
  2. Mesurez le temps total avec des appels exécutés un par un, puis en parallèle.
  3. 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_result par 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_use se place dans tool_choice, pas au premier niveau