Votre premier outil
Sur cette page
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 montre pourtant exactement pourquoi les outils existent. Vous en sortez capable d’écrire une fonction, de la décrire à Claude, de lire sa demande d’outil et d’exécuter la fonction avec les paramètres qu’il a choisis.
Mise à jour du 5 novembre 2026. Cette leçon adapte le carnet « Your first simple tool » du cours Tool use d’Anthropic (dépôt anthropics/courses, écrit en 2024 pour Claude 3 Haiku, archivé depuis le 15 septembre 2026). Le code a été relu contre la documentation actuelle : il utilise Claude Sonnet 5.5, qui réfléchit par défaut avant de répondre. Deux conséquences pour votre code : la réponse peut commencer par un bloc de réflexion, on lit donc les blocs par leur type et jamais par leur position ; et
max_tokensdoit laisser la place à cette réflexion. Le nom d’un outil accepte désormais jusqu’à 128 caractères (64 dans le carnet).
Le problème : Claude et les mathématiques
Un modèle de langage prédit des suites de jetons ; 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-5",
max_tokens=16000, # la réflexion adaptative, active par défaut, compte dans max_tokens
messages=[{"role": "user", "content": "Multiplie 1984135 par 9343116"}]
)
# la réponse est une liste de blocs : un bloc de réflexion peut précéder le texte
print(next(block.text for block in response.content if block.type == "text"))
Dans le carnet, Claude 3 Haiku répondait par exemple 18593367726060, alors que la bonne réponse est 18538003464660 : 55 milliards d’écart, pour un nombre de la bonne longueur aux chiffres plausibles. Avec la réflexion adaptative, active par défaut sur Sonnet 5.5, le modèle pose souvent le calcul et peut tomber juste, mais au prix de nombreux jetons de réflexion et sans garantie. Le problème n’est pas la formulation du prompt mais la nature de la tâche : une fonction Python de trois lignes donne la réponse exacte à tous les coups, instantanément et pour presque rien.
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 : 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 zéro"
return operand1 / operand2
return f"Erreur : opération inconnue {operation}"
Testez-la seule avant d’y brancher Claude : calculator("add", 10, 3) doit renvoyer 13, calculator("divide", 200, 25) doit renvoyer 8.0. Un outil qui se trompe tout seul fera se tromper Claude avec lui.
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 ; il doit respecter l’expression ^[a-zA-Z0-9_-]{1,128}$ (lettres, chiffres, tirets et soulignés, 1 à 128 caractères), et c’est ce nom que Claude renverra. La description explique ce que fait l’outil et surtout dans quelles situations l’employer : ici, « pour tout calcul arithmétique » 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 enum sur l’opération : en énumérant les quatre valeurs acceptées, vous retirez à Claude la possibilité d’inventer un power ou un modulo que la fonction ne saurait pas traiter.
tools = [
{
"name": "calculator",
"description": "Effectue des opérations arithmétiques de base sur deux nombres. Utilisez cet outil pour tout calcul arithmétique.",
"input_schema": {
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"],
"description": "L'opération à effectuer"
},
"operand1": {
"type": "number",
"description": "Le premier opérande"
},
"operand2": {
"type": "number",
"description": "Le second opérande"
}
},
"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-5",
max_tokens=4096,
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 contient un ToolUseBlock. Prenez le temps d’examiner ses champs : l’id servira à apparier le résultat à la demande, 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)
# [… un éventuel bloc de réflexion ou de texte, puis :
# 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 : trouver le bloc tool_use dans la liste (il n’est pas forcément le premier : un bloc de réflexion ou une phrase de Claude peuvent le précéder), lire ses paramètres, les passer à la fonction Python, récupérer la valeur exacte.
tool_use = next(block for block in response.content if block.type == "tool_use")
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 réponse)
Cette leçon s’arrête là, comme le carnet : Claude a choisi l’outil et ses paramètres, votre code a calculé. Il n’a pas encore vu le résultat, donc il n’a pas rédigé de réponse ; la leçon 3 ferme la boucle en lui renvoyant un tool_result.
Éviter les appels d’outils inutiles
Dès qu’un outil est disponible, un modèle peut vouloir s’en servir sur des questions qui n’en réclament aucun. Dans le carnet, Claude 3 Haiku, interrogé sur la couleur des émeraudes, appelait la calculatrice pour additionner 0 et 0. Les modèles actuels font beaucoup moins ce genre d’excès de zèle, mais la parade du carnet reste utile : une phrase dans le prompt système, et vous retrouvez un stop_reason valant "end_turn".
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=4096,
tools=tools,
system="Vous avez accès à des outils, mais ne les utilisez que lorsque c'est nécessaire. Si aucun outil n'est requis, répondez normalement.",
messages=[{"role": "user", "content": "De quelle couleur sont les émeraudes ?"}]
)
print(response.stop_reason) # "end_turn" : Claude répond en texte, sans appeler l'outil
À vous de jouer
Décrire un outil d’inventaire. Voici une fonction de recherche dans un inventaire ; écrivez le tools correspondant. Elle attend un product_name (chaîne, obligatoire), le nom du produit à chercher, et un max_results (entier, facultatif, 5 par défaut) qui plafonne le nombre de résultats. La subtilité tient au paramètre facultatif : il figure dans properties, mais pas dans required. Le carnet, lui, rendait les deux paramètres obligatoires ; essayez les deux variantes et observez ce que Claude envoie quand l’utilisateur ne précise pas de nombre.
def inventory_lookup(product_name, max_results=5):
"""Recherche des produits dans l'inventaire par nom."""
# ...
Voir la solution
tools = [{
"name": "inventory_lookup",
"description": "Recherche des produits dans l'inventaire par nom et renvoie les résultats correspondants.",
"input_schema": {
"type": "object",
"properties": {
"product_name": {
"type": "string",
"description": "Le nom du produit à rechercher"
},
"max_results": {
"type": "integer",
"description": "Nombre maximum de résultats à renvoyer (5 par défaut)"
}
},
"required": ["product_name"]
}
}]
Tester la contrainte enum. Demandez à Claude « Combien font 2 puissance 10 ? » avec la calculatrice telle quelle. Lisez son tool_use : quelle opération choisit-il, et que renvoie votre fonction ? Ajoutez ensuite power à la fonction et à l’enum, et relancez.
Sources officielles : 02_your_first_simple_tool.ipynb (anthropics/courses, archivé) · Define tools et Handle tool calls (documentation Claude)
Points clés à retenir
- Un outil, c’est d’abord une fonction ordinaire, testée seule ; Claude choisit de l’appeler, votre code l’exécute.
- La définition tient en trois champs :
name(jusqu’à 128 caractères),description(ce que fait l’outil et quand l’employer),input_schema(schéma JSON des paramètres). enumborne les valeurs possibles ;requiredsépare paramètres obligatoires et facultatifs.- Une demande d’outil se reconnaît à
stop_reason == "tool_use"; on cherche le bloctool_usepar son type, jamais par sa position. - Sur les modèles qui réfléchissent par défaut,
max_tokensdoit laisser de la place à la réflexion.
Crédits et licence — Adaptation française du notebook « 02_your_first_simple_tool » 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.