Le workflow complet du Tool Use
Mis à jour le 29 juillet 2026
Objectifs
- Implémenter le workflow complet en 4 étapes
- Écrire des messages
tool_resultcorrectement structurés - Construire un chatbot qui utilise des outils
Rappel des 4 étapes
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 pour l’utilisateur. Cette leçon ferme la boucle. Le cycle complet reste celui que vous connaissez :
- Fournir les outils et le prompt (requête API)
- Claude demande un outil (réponse API)
- Exécuter l’outil et renvoyer le résultat (requête API)
- Claude formule sa réponse finale (réponse API)
Exemple : recherche Wikipedia
Le support de démonstration sera un assistant capable de répondre à des questions factuelles récentes en consultant Wikipedia — typiquement le genre de question sur laquelle un modèle seul se trompe ou hésite.
1. La fonction de recherche
La fonction cherche un terme, prend le premier article correspondant et tronque le contenu. Ce plafonnement à 5 000 caractères n’est pas cosmétique : chaque caractère renvoyé consomme du contexte et coûte des tokens, et un article Wikipedia complet peut peser plusieurs dizaines de milliers de caractères.
import wikipedia
def get_article(search_term):
"""Recherche un article Wikipedia et retourne son contenu."""
results = wikipedia.search(search_term)
if not results:
return "Aucun article trouve."
page = wikipedia.page(results[0])
return page.content[:5000] # Limiter la taille
2. La définition de l’outil
Un seul paramètre suffit ici, le terme de recherche. Remarquez que la description ne parle pas de troncature ni de bibliothèque Python : elle décrit le service rendu, pas l’implémentation.
tools = [{
"name": "get_article",
"description": "Recherche et retourne le contenu d'un article Wikipedia.",
"input_schema": {
"type": "object",
"properties": {
"search_term": {
"type": "string",
"description": "Le terme de recherche pour trouver l'article Wikipedia"
}
},
"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 requête suivante.
messages = [
{"role": "user", "content": "Qui a gagne le tournoi des Masters 2024 ?"}
]
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1000,
tools=tools,
messages=messages
)
4. Gérer la demande d’outil de Claude
C’est l’étape nouvelle. Claude a répondu par un ToolUseBlock ; il faut en extraire les paramètres, exécuter la fonction, puis reconstruire l’historique de conversation avec trois messages : la question initiale de l’utilisateur, la réponse de l’assistant telle quelle — vous repassez response.content sans le modifier —, et enfin un message user porteur du résultat.
# Extraire la demande d'outil
tool_block = next(b for b in response.content if b.type == "tool_use")
tool_name = tool_block.name
tool_input = tool_block.input
# Executer la fonction
article_content = get_article(tool_input["search_term"])
# Construire le message tool_result
messages = [
{"role": "user", "content": "Qui a gagne le tournoi des Masters 2024 ?"},
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_block.id,
"content": article_content
}
]
}
]
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. Claude lit l’extrait d’article et rédige une réponse en langage naturel.
final_response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1000,
tools=tools,
messages=messages
)
print(final_response.content[0].text)
Structure du message 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 ToolUseBlock
"content": "Le contenu retourne par l'outil"
}
]
}
Trois règles gouvernent ce bloc. Le tool_use_id doit reproduire à l’identique l’id du ToolUseBlock émis par Claude : c’est lui qui apparie la demande et la réponse, et une valeur inventée provoque une erreur d’API. Le content accepte aussi bien une chaîne qu’une liste de blocs de contenu, ce qui autorise par exemple le renvoi d’une image. Enfin, chaque tool_use présent dans la réponse de Claude appelle son tool_result correspondant : si le modèle a demandé deux outils et que vous n’en renvoyez qu’un, la requête est rejetée.
Implémentation complète automatisée
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, et ne rend la main qu’au moment où Claude produit enfin du texte.
def ask_with_tools(question, tools, tool_functions):
"""
Pose une question a Claude avec des outils disponibles.
tool_functions : dict mappant nom_outil -> fonction
"""
messages = [{"role": "user", "content": question}]
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=4096,
tools=tools,
messages=messages
)
# Boucle tant que Claude veut utiliser des outils
while response.stop_reason == "tool_use":
# Ajouter la réponse de Claude a l'historique
messages.append({"role": "assistant", "content": response.content})
# Traiter chaque demande d'outil
tool_results = []
for block in response.content:
if block.type == "tool_use":
# Executer l'outil
func = tool_functions[block.name]
result = func(**block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
})
# Renvoyer les résultats
messages.append({"role": "user", "content": tool_results})
# Nouvelle requête
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=4096,
tools=tools,
messages=messages
)
# Retourner la réponse textuelle finale
return response.content[0].text
L’appel devient alors une seule ligne, et toute la mécanique disparaît derrière l’interface.
answer = ask_with_tools(
"Qui a gagne le tournoi des Masters 2024 ?",
tools=tools,
tool_functions={"get_article": get_article}
)
print(answer)
Aller plus loin : un assistant de recherche documentaire
Reprenez ce pattern pour construire un assistant de recherche documentaire. Il accepte un sujet, demande à Claude de proposer des titres d’articles Wikipedia pertinents, vérifie via un outil que ces articles existent réellement, puis sauvegarde les liens retenus dans un fichier markdown. L’intérêt de l’exercice 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 (string) et une liste de article_titles (array de strings). Claude générera les titres, l’outil vérifiera leur existence sur Wikipedia.