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

Le workflow complet du Tool Use

Leçon 3 sur 16 Mis à jour le 5 novembre 2026 Développeur
Sur cette page
  1. Rappel des 4 étapes
  2. Exemple : un assistant qui cite Wikipédia
  3. Les règles du bloc tool_result
  4. Automatiser la boucle
  5. À vous de jouer
  6. Points clés à retenir

Jusqu’ici, nous nous sommes arrêtés à mi-parcours : Claude demandait un outil, nous exécutions la fonction, et l’histoire s’achevait là. Le modèle n’a jamais vu le résultat, donc il n’a jamais pu rédiger de réponse. Cette leçon ferme la boucle : vous renvoyez le résultat dans un bloc tool_result, Claude rédige sa réponse, puis vous automatisez le tout dans une fonction qui enchaîne autant de tours d’outils que nécessaire.

Mise à jour du 5 novembre 2026. Cette leçon adapte le carnet « The complete tool use workflow » du cours Tool use d’Anthropic (dépôt anthropics/courses, écrit en 2024 pour Claude 3 Sonnet, archivé depuis le 15 septembre 2026). Trois changements par rapport au carnet. La bibliothèque wikipedia doit désormais s’identifier : avec son agent par défaut, l’API de Wikipédia répond 429 (vérifié le 5 novembre 2026), conformément à sa politique qui exige un User-Agent descriptif. Les modèles actuels connaissent le Masters 2024, posé en exemple par le carnet parce que Claude 3 l’ignorait ; on demande donc explicitement une réponse sourcée. Enfin, la réponse de Claude se lit par type de bloc, un bloc de réflexion pouvant précéder le texte et l’appel d’outil.

Rappel des 4 étapes

Le cycle complet du tool use Quatre étapes : fournir outils et prompt, Claude demande un outil (stop_reason tool_use), exécuter et renvoyer un tool_result, Claude rédige sa réponse ; retour à l'étape 2 tant que Claude demande un outil. WORKFLOW Le cycle complet du tool use 1 Requête outils + question dans messages 2 tool_use stop_reason tool_use ; bloc cherché par son type 3 tool_result exécuter la fonction, renvoyer le résultat apparié par son id 4 Réponse Claude rédige à partir du résultat ; texte lu par type de bloc tant que stop_reason vaut tool_use, avec un plafond de tours À chaque tour, la réponse de l'assistant est repassée telle quelle, blocs de réflexion compris : l'API les exige inchangés.
D'après le carnet « The complete tool use workflow » (anthropics/courses, carnet archivé) et la page Handle tool calls (documentation Claude).
  1. Fournir les outils et le prompt (requête API).
  2. Claude demande un outil (réponse API, stop_reason vaut "tool_use").
  3. Exécuter l’outil et renvoyer le résultat (requête API, message user portant un bloc tool_result).
  4. Claude formule sa réponse finale à partir du résultat (réponse API).

Exemple : un assistant qui cite Wikipédia

Le support de démonstration est un assistant qui répond en s’appuyant sur un article de Wikipédia : le schéma de toute question qui exige une source à jour ou vérifiable.

1. La fonction de recherche

La fonction cherche un terme, prend le premier article correspondant et tronque le contenu. Ce plafond de 5 000 caractères n’est pas cosmétique : chaque caractère renvoyé consomme du contexte et coûte des jetons, et un article complet peut en peser plusieurs dizaines de milliers. La fonction gère aussi les deux échecs courants, un terme ambigu et un article introuvable, en renvoyant un message plutôt qu’en plantant : Claude saura quoi en faire.

import wikipedia

# Wikipédia limite les clients sans User-Agent descriptif (réponse 429) : identifiez votre application
wikipedia.set_user_agent("MonAssistantDeFormation/1.0 (https://exemple.org/contact)")

def get_article(search_term):
    """Cherche un article Wikipédia et renvoie le début de son contenu."""
    results = wikipedia.search(search_term)
    if not results:
        return f"Aucun article trouvé pour « {search_term} »."
    try:
        page = wikipedia.page(results[0], auto_suggest=False)
    except wikipedia.exceptions.DisambiguationError as e:
        return "Terme ambigu, articles possibles : " + ", ".join(e.options[:5])
    except wikipedia.exceptions.PageError:
        return f"Article introuvable : {results[0]}."
    return page.content[:5000]  # limiter la taille renvoyée à Claude

auto_suggest=False vient du carnet : sans lui, la bibliothèque peut « corriger » un titre exact vers un autre article. Remplacez l’agent d’exemple par le nom et l’adresse de votre application. Les articles viennent de Wikipédia en anglais ; wikipedia.set_lang("fr") bascule sur l’édition française.

2. La définition de l’outil

Un seul paramètre suffit ici, le terme de recherche. La description ne parle ni de troncature ni de bibliothèque Python : elle décrit le service rendu, pas l’implémentation.

tools = [{
    "name": "get_article",
    "description": "Recherche un article Wikipédia par son titre et renvoie le début de son contenu, à jour.",
    "input_schema": {
        "type": "object",
        "properties": {
            "search_term": {
                "type": "string",
                "description": "Le terme de recherche, idéalement le titre de l'article Wikipédia"
            }
        },
        "required": ["search_term"]
    }
}]

3. Envoyer le prompt avec l’outil

On conserve la liste messages dans une variable, car elle va s’enrichir à chaque tour et devra être renvoyée intégralement. La question demande une réponse sourcée : Sonnet 5.5 refuse l’appel forcé d’un outil (tool_choice à any ou tool, leçon 5), et c’est la demande explicite qui l’oriente vers la recherche.

from anthropic import Anthropic

client = Anthropic()

messages = [
    {"role": "user", "content": "D'après Wikipédia, qui a remporté le Masters de golf 2024, et avec combien de coups d'avance ?"}
]

response = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=4096,
    tools=tools,
    messages=messages
)
print(response.stop_reason)  # "tool_use"

4. Gérer la demande d’outil de Claude

C’est l’étape nouvelle. La réponse contient un bloc tool_use, souvent précédé d’une phrase (« Je consulte l’article… ») et parfois d’un bloc de réflexion ; on le cherche donc par son type. On exécute la fonction, puis on prolonge l’historique de deux messages : la réponse de l’assistant telle quelle, et un message user porteur du résultat.

tool_block = next(block for block in response.content if block.type == "tool_use")
article_content = get_article(tool_block.input["search_term"])

# 1) la réponse de Claude, sans rien retirer : blocs de réflexion compris
messages.append({"role": "assistant", "content": response.content})

# 2) le résultat de l'outil, apparié par son identifiant
messages.append({
    "role": "user",
    "content": [
        {
            "type": "tool_result",
            "tool_use_id": tool_block.id,
            "content": article_content
        }
    ]
})

Repasser response.content sans le modifier n’est pas qu’une commodité : quand Claude a réfléchi avant d’appeler l’outil, l’API exige de retrouver ses blocs de réflexion complets et inchangés au tour suivant, parce que la réponse finale prolonge ce même raisonnement.

5. Obtenir la réponse finale de Claude

Le second appel est en tout point semblable au premier, à ceci près que l’historique contient désormais la matière première. On garde tools dans la requête : Claude peut vouloir chercher un second article.

final_response = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=4096,
    tools=tools,
    messages=messages
)

# un bloc de réflexion peut précéder le texte : on lit le premier bloc de type text
print(next(block.text for block in final_response.content if block.type == "text"))

Avec l’article « 2024 Masters Tournament », Claude répond que Scottie Scheffler l’a emporté avec quatre coups d’avance sur Ludvig Åberg, et peut le citer.

Les règles du bloc tool_result

Le point qui provoque le plus d’erreurs en début de projet mérite qu’on s’y arrête. Le résultat d’un outil ne voyage pas dans un rôle spécial : c’est un message user dont le content est une liste de blocs.

{
    "role": "user",
    "content": [
        {
            "type": "tool_result",
            "tool_use_id": "toolu_xxx",   # l'id du bloc tool_use
            "content": "Le contenu renvoyé par l'outil",
            # "is_error": True,           # si l'exécution a échoué
        }
    ]
}
Les règles du bloc tool_result Quatre tuiles : apparier par tool_use_id, enchaîner immédiatement avec les tool_result en premier, renvoyer un résultat pour chaque tool_use, signaler un échec avec is_error ; le contenu renvoyé reste non fiable. RENVOYER UN RÉSULTAT Les règles du bloc tool_result 01 Apparier tool_use_id reproduit à l'identique l'id du bloc tool_use. tool_use_id 02 Enchaîner Le message user suit la réponse de l'assistant ; ses tool_result viennent en premier, le texte après. Ordre Sans message intercalé 03 Tout renvoyer Un tool_result par tool_use ; plusieurs demandes, un seul message de résultats. Complet Un message 04 Signaler is_error: true quand l'exécution échoue : Claude peut réessayer ou expliquer. is_error Le contenu renvoyé (article, page web, courriel) n'est pas fiable : il reste dans le tool_result, jamais dans le prompt système.
D'après la page Handle tool calls (documentation Claude, novembre 2026).

Le tool_use_id reproduit à l’identique l’id du bloc tool_use : c’est lui qui apparie la demande et la réponse. Le message de résultats suit immédiatement la réponse de l’assistant, sans message intercalé, et dans son content les blocs tool_result viennent en premier, un éventuel texte après eux. Chaque tool_use appelle son tool_result : si Claude a demandé deux outils et que vous n’en renvoyez qu’un, la requête est rejetée. Le content accepte une chaîne, une liste de blocs (texte, image) ou des documents ; quand l’exécution échoue, is_error: true le dit à Claude, qui peut réessayer autrement ou expliquer l’échec. Enfin, un résultat d’outil transporte souvent du contenu que vous ne maîtrisez pas, un article, une page web, un courriel : la documentation demande de le traiter comme non fiable et de le laisser dans le tool_result, jamais dans le prompt système, pour limiter les injections de consignes.

Automatiser la boucle

Recopier ce câblage à la main devient vite fastidieux, d’autant que Claude peut enchaîner plusieurs tours d’outils avant d’être satisfait. La fonction ci-dessous généralise le mécanisme : elle boucle tant que stop_reason vaut "tool_use", exécute chaque outil demandé grâce à un dictionnaire qui associe un nom à une fonction Python, renvoie tous les résultats d’un tour dans un seul message, et ne rend la main qu’au moment où Claude produit sa réponse.

def ask_with_tools(question, tools, tool_functions, max_turns=10):
    """Pose une question à Claude avec des outils.
    tool_functions : dictionnaire nom_outil -> fonction Python."""
    messages = [{"role": "user", "content": question}]
    response = client.messages.create(model="claude-sonnet-5-5", max_tokens=4096,
                                      tools=tools, messages=messages)

    turns = 0
    while response.stop_reason == "tool_use" and turns < max_turns:
        turns += 1
        messages.append({"role": "assistant", "content": response.content})

        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                try:
                    result = tool_functions[block.name](**block.input)
                    tool_results.append({"type": "tool_result", "tool_use_id": block.id,
                                         "content": str(result)})
                except Exception as e:  # outil inconnu ou en échec : on le dit à Claude
                    tool_results.append({"type": "tool_result", "tool_use_id": block.id,
                                         "content": f"Erreur : {e}", "is_error": True})

        messages.append({"role": "user", "content": tool_results})
        response = client.messages.create(model="claude-sonnet-5-5", max_tokens=4096,
                                          tools=tools, messages=messages)

    # réponse finale : le premier bloc de type text, après une éventuelle réflexion
    return next((block.text for block in response.content if block.type == "text"), "")

L’appel devient une seule ligne, et toute la mécanique disparaît derrière l’interface. Le garde-fou max_turns évite qu’un outil qui renvoie toujours la même erreur fasse tourner la boucle indéfiniment.

answer = ask_with_tools(
    "D'après Wikipédia, qui a composé la musique du film Challengers ?",
    tools=tools,
    tool_functions={"get_article": get_article}
)
print(answer)

Le SDK Python propose aussi un Tool Runner qui prend en charge cette boucle à votre place ; l’écrire une fois à la main reste le meilleur moyen de comprendre ce qu’il fait, et de garder la main quand il faut contrôler chaque exécution.

À vous de jouer

Doser l’usage de l’outil. Le carnet montrait l’excès inverse : Claude consultait Wikipédia même pour « Qui a écrit le roman Life of Pi ? » (L’Histoire de Pi en français), qu’il connaît. Ajoutez à ask_with_tools un paramètre system et essayez deux consignes : « consulte toujours l’article avant de répondre » et « n’utilise l’outil que pour des faits récents ou à vérifier ». Posez à chaque fois une question de culture générale et une question sur un événement de cette année, et comptez les appels.

Une liste de lectures vérifiée. Construisez un assistant qui reçoit un sujet, demande à Claude de proposer des titres d’articles Wikipédia pertinents, vérifie par un outil que ces articles existent, puis enregistre les liens retenus dans un fichier Markdown. L’intérêt est de faire coopérer la connaissance du modèle, qui suggère les pistes, et l’outil, qui tranche sur ce qui existe vraiment.

Voir un indice

Définissez un outil generate_wikipedia_reading_list qui accepte un topic (chaîne) et une liste article_titles (tableau de chaînes). La fonction essaie wikipedia.page(titre, auto_suggest=False) pour chaque titre, garde ceux qui existent, et écrit - [titre](url) dans output/{topic}.md. Renvoyez à Claude la liste des titres retenus et écartés : il pourra en proposer d’autres.

Sources officielles : 04_complete_workflow.ipynb (anthropics/courses, archivé) · Handle tool calls, Tool Runner et Thinking (documentation Claude) · Wikimedia APIs : rate limits (politique d’accès de Wikipédia)

Points clés à retenir

  • Le cycle complet : outils et prompt, demande d’outil (stop_reason vaut "tool_use"), exécution et tool_result, réponse finale.
  • On repasse la réponse de l’assistant telle quelle, blocs de réflexion compris, puis un message user dont les blocs tool_result viennent en premier.
  • tool_use_id apparie demande et résultat ; chaque tool_use reçoit son tool_result ; is_error: true signale un échec.
  • Le contenu renvoyé par un outil est non fiable : il reste dans le tool_result.
  • Une boucle tant que stop_reason vaut "tool_use", avec un plafond de tours, automatise le tout ; on lit la réponse finale par type de bloc.

Crédits et licence — Adaptation française du notebook « 04_complete_workflow » du tutoriel Tool use d’Anthropic (dépôt GitHub anthropics/courses, archivé en lecture seule le 15 septembre 2026), publié sous licence CC BY-NC 4.0 ; adaptation remaniée et mise à jour par Corsen, diffusée sous la même licence.