Votre premier outil
Mis à jour le 29 juillet 2026
La leçon précédente a posé le workflow du Tool Use dans ses grandes lignes. Nous allons maintenant le mettre en pratique sur l’exemple le plus parlant qui soit : une calculatrice. L’exercice paraît trivial, il révèle pourtant exactement pourquoi les outils existent.
Le problème : Claude et les mathématiques
Un modèle de langage prédit des suites de tokens ; il ne pose pas une multiplication colonne par colonne. Sur des nombres à sept chiffres, cette différence devient visible. Demandez à Claude de multiplier 1 984 135 par 9 343 116 et observez le résultat.
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1000,
messages=[{"role": "user", "content": "Multiplie 1984135 par 9343116"}]
)
print(response.content[0].text)
Claude pourrait répondre 18593367726060, alors que la bonne réponse est 18538003464660. L’écart est de 55 milliards ! Le modèle a produit un nombre de la bonne longueur, avec des chiffres plausibles, et il s’est trompé. Aucun réglage de prompt ne corrigera ce comportement de façon fiable, parce que le problème n’est pas la formulation mais la nature de la tâche. En revanche, une fonction Python de trois lignes donne la réponse exacte à tous les coups.
La solution : un outil calculatrice
1. Écrire la fonction
Tout commence par du code ordinaire, sans la moindre référence à Claude. Cette fonction reçoit une opération et deux opérandes, et elle gère au passage le cas de la division par zéro — un réflexe à prendre dès le premier outil, car c’est vous qui portez la responsabilité des erreurs d’exécution.
def calculator(operation, operand1, operand2):
if operation == "add":
return operand1 + operand2
elif operation == "subtract":
return operand1 - operand2
elif operation == "multiply":
return operand1 * operand2
elif operation == "divide":
if operand2 == 0:
return "Erreur : division par zero"
return operand1 / operand2
2. Définir l’outil pour Claude
La définition qui décrit cette fonction au modèle repose sur trois champs. Le name identifie l’outil et n’accepte que des caractères alphanumériques, des tirets et des underscores ; c’est ce nom que Claude renverra. La description explique ce que fait l’outil et surtout dans quelles situations l’employer : ici, la mention « pour tout calcul mathématique » oriente le modèle vers l’outil plutôt que vers son calcul mental. L’input_schema, enfin, est un schéma JSON qui décrit les paramètres attendus, leur type et leur caractère obligatoire. Notez l’usage de enum sur l’opération : en énumérant les quatre valeurs acceptées, vous supprimez la possibilité que Claude invente un power ou un modulo que la fonction ne saurait pas traiter.
tools = [
{
"name": "calculator",
"description": "Effectue des operations arithmetiques de base. Utilisez cet outil pour tout calcul mathematique.",
"input_schema": {
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"],
"description": "L'operation a effectuer"
},
"operand1": {
"type": "number",
"description": "Le premier operande"
},
"operand2": {
"type": "number",
"description": "Le second operande"
}
},
"required": ["operation", "operand1", "operand2"]
}
}
]
3. Envoyer la requête avec l’outil
L’appel API est identique au précédent, à un paramètre près : tools. Le prompt de l’utilisateur, lui, ne change pas d’un caractère.
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1000,
tools=tools,
messages=[{"role": "user", "content": "Multiplie 1984135 par 9343116"}]
)
4. Inspecter la réponse
La réponse ne ressemble plus à ce que vous avez l’habitude de lire. Le stop_reason vaut "tool_use" et le contenu n’est pas du texte mais un ToolUseBlock. Prenez le temps d’examiner ses champs : l’id servira à corréler le résultat, le name indique quelle fonction appeler, et l’input contient les paramètres que Claude a extraits de la phrase en langage naturel.
print(response.stop_reason) # "tool_use"
print(response.content)
# [ToolUseBlock(
# id='toolu_xxx',
# input={'operand1': 1984135, 'operand2': 9343116, 'operation': 'multiply'},
# name='calculator',
# type='tool_use'
# )]
5. Exécuter l’outil et obtenir le résultat
Il ne reste qu’à câbler les deux mondes : lire les paramètres du bloc, les passer à la fonction Python, récupérer la valeur exacte.
tool_use = response.content[0]
tool_name = tool_use.name
tool_input = tool_use.input
result = calculator(
tool_input["operation"],
tool_input["operand1"],
tool_input["operand2"]
)
print(result) # 18538003464660 (la bonne reponse !)
Éviter les appels d’outils inutiles
Un effet de bord se manifeste rapidement en production : dès qu’un outil est disponible, Claude a tendance à vouloir s’en servir, y compris sur des questions qui n’en réclament aucun. Demandez la couleur des émeraudes à un modèle équipé d’une calculatrice et vous pourriez le voir tenter un calcul. Une phrase dans le system prompt suffit à rétablir le bon équilibre, et vous retrouvez alors un stop_reason valant "end_turn".
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1000,
tools=tools,
system="Vous avez accès a des outils, mais ne les utilisez que quand c'est nécessaire. Si un outil n'est pas requis, répondez normalement.",
messages=[{"role": "user", "content": "De quelle couleur sont les emeraudes ?"}]
)
# Claude repond normalement : "Les emeraudes sont vertes."
# stop_reason = "end_turn" (pas "tool_use")
Mise en pratique : décrire un outil d’inventaire
À votre tour de rédiger une définition d’outil. Voici une fonction de recherche dans un inventaire produit ; écrivez le tools correspondant.
def inventory_lookup(product_name, max_results=5):
"""Recherche des produits dans l'inventaire par nom."""
# ...
La fonction attend un product_name (string, requis), le nom du produit à chercher, et un max_results (integer, optionnel, valeur par défaut 5) qui plafonne le nombre de résultats. La subtilité tient au paramètre optionnel : il figure bien dans properties, mais pas dans required.
Voir la solution
tools = [{
"name": "inventory_lookup",
"description": "Recherche des produits dans l'inventaire par nom et retourne les résultats correspondants.",
"input_schema": {
"type": "object",
"properties": {
"product_name": {
"type": "string",
"description": "Le nom du produit a rechercher"
},
"max_results": {
"type": "integer",
"description": "Nombre maximum de résultats a retourner (défaut : 5)"
}
},
"required": ["product_name"]
}
}]