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

Appels d'outils en streaming : fini les attentes muettes

Leçon 10 sur 16 Mis à jour le 5 novembre 2026 Développeur
Sur cette page
  1. Objectifs
  2. Huit secondes devant une roue qui tourne
  3. La forme d’un appel d’outil en streaming
  4. Afficher l’outil choisi dès son ouverture
  5. Le streaming fin des entrées volumineuses
  6. À vous de jouer
  7. Points clés à retenir

Objectifs

À l’issue de cette leçon, vous serez capable de :

  • Lire la séquence d’événements d’un appel d’outil en streaming
  • Afficher l’outil choisi dès que son nom arrive
  • Savoir quand activer le streaming fin des entrées (eager_input_streaming)

Huit secondes devant une roue qui tourne

Votre agent met huit secondes à répondre, et l’utilisateur regarde une roue de chargement pendant les huit. Pendant ce temps, Claude choisit un outil, écrit son entrée, votre code exécute l’outil, puis Claude rédige la réponse. Sans streaming, votre code ne voit chaque réponse qu’une fois terminée : l’interface n’a rien à montrer. Avec le streaming, la réponse arrive morceau par morceau, sous forme d’événements envoyés par le serveur (server-sent events), et l’écran peut dire ce qui se passe : « Recherche du projet… » dès que Claude a choisi l’outil.

La forme d’un appel d’outil en streaming

Un bloc tool_use arrive en trois temps :

  1. content_block_start ouvre le bloc. Il contient déjà le nom de l’outil, mais une entrée vide (input: {}), simple marque de place.
  2. content_block_delta arrive une ou plusieurs fois, avec un delta de type input_json_delta portant un fragment partial_json de l’entrée JSON.
  3. content_block_stop ferme le bloc.

Puis l’événement message_delta porte stop_reason: "tool_use" : c’est le signal pour exécuter l’outil.

Un appel d'outil en streaming Frise à quatre jalons : content_block_start avec le nom de l'outil et une entrée vide, des content_block_delta de type input_json_delta avec des fragments, content_block_stop, puis message_delta avec stop_reason tool_use. STREAMING Un appel d'outil en streaming 1 content_block_start le nom de l'outil, entrée vide 2 input_json_delta des fragments de JSON 3 content_block_stop on analyse le JSON ici 4 message_delta stop_reason : tool_use Un tampon par index de bloc : plusieurs appels peuvent arriver dans le même tour.
D'après les pages Streaming messages et Fine-grained tool streaming (documentation Claude).

Les fragments se coupent n’importe où, au milieu d’une clé, d’une valeur ou d’une séquence d’échappement, et certains peuvent être vides. Règle : accumulez les fragments dans un tampon, et n’analysez le JSON qu’au content_block_stop. Si Claude fait plusieurs appels dans le même tour, chaque bloc a son propre index : gardez un tampon par index, sinon les fragments s’entremêlent.

Afficher l’outil choisi dès son ouverture

Avec un SDK officiel, client.messages.stream(...) fait l’accumulation pour vous. Votre code n’a qu’à réagir à l’ouverture du bloc, puis récupérer la réponse complète avec stream.get_final_message(), qui renvoie le même type d’objet que client.messages.create(...) : le reste de la boucle ne change pas.

STATUTS = {
    "consulter_projet": "Recherche du projet…",
    "consulter_permis": "Vérification du permis…",
}

with client.messages.stream(
    model="claude-opus-5-5",
    max_tokens=16000,
    tools=OUTILS,
    messages=messages,
) as stream:
    for event in stream:
        if event.type == "content_block_start" and event.content_block.type == "tool_use":
            afficher_statut(STATUTS.get(event.content_block.name, "Traitement en cours…"))
    reponse = stream.get_final_message()

afficher_statut est votre fonction d’affichage. Les modèles actuels peuvent aussi produire des blocs de réflexion, que ce code ignore. Connaître la couche brute reste utile : c’est elle que vous déboguerez le jour où quelque chose cloche sur le réseau.

Le streaming fin des entrées volumineuses

Par défaut, l’API met en mémoire tampon et valide chaque paramètre avant d’en émettre les fragments. Pour une petite entrée, c’est invisible ; pour un outil qui reçoit un fichier entier, un bloc de code ou un document, rien n’arrive tant que le paramètre n’est pas complet. Ajouter eager_input_streaming: true sur un outil défini par vous lève cette mise en tampon : les fragments arrivent dès qu’ils sont générés.

La contrepartie : l’API ne valide plus l’entrée. L’entrée accumulée peut être coupée par max_tokens ou contenir du JSON invalide, et les analyseurs tolérants des SDK peuvent renvoyer un objet tronqué sans lever d’erreur. Validez donc l’entrée contre le schéma avant d’exécuter l’outil, vérifiez stop_reason (max_tokens, refusal) avant de lancer quoi que ce soit, et renvoyez une erreur à Claude si l’entrée est invalide. N’activez ce mode que sur une requête en streaming, et jamais sur les outils serveur.

À vous de jouer

Une ligne d’état.

  1. Dans votre agent, remplacez messages.create(...) par messages.stream(...) et get_final_message().
  2. Associez à chaque outil une phrase lisible pour l’utilisateur, affichée au content_block_start.
  3. Mesurez le délai avant le premier affichage, avant et après.

Sources officielles : Streaming messages (documentation Claude : événements, input_json_delta) · Fine-grained tool streaming (documentation Claude : eager_input_streaming, validation à votre charge)

Points clés à retenir

  • Un appel d’outil en streaming arrive avec le nom d’abord, puis l’entrée en fragments input_json_delta
  • Un tampon par index de bloc, et l’analyse du JSON seulement à content_block_stop
  • Dès content_block_start, affichez l’outil choisi en mots compréhensibles ; get_final_message() garde le reste de la boucle inchangé
  • eager_input_streaming pour les entrées volumineuses, à condition de valider vous-même l’entrée