Aller au contenu principal

Appel d'outils par programme

Avancé
What you'll learn
  • Comprendre ce qui se passe réellement quand Claude appelle votre outil depuis un bac à sable — et pourquoi votre outil s'exécute malgré tout sur votre propre machine
  • L'activer correctement avec allowed_callers, et savoir pourquoi ce n'est pas une frontière de sécurité
  • Connaître les vrais chiffres : ce que cela économise, sur quelles charges de travail, et là où cela vous coûte
  • Éviter les cinq modes de défaillance qui produisent des 400 et des TimeoutError en production

Le problème que cela résout

L'usage classique des outils est une conversation. Claude demande un appel d'outil, vous répondez, tout le résultat atterrit dans la fenêtre de contexte, Claude le lit et demande le suivant. Vingt recherches, c'est vingt passes d'inférence et vingt charges utiles brutes qui restent dans le contexte pour toujours.

L'essentiel de cette charge utile est du gaspillage. Si vous voulez savoir lesquels de vingt employés ont fait exploser leur budget de frais, Claude n'a pas besoin de chaque ligne de dépense — il a besoin de la poignée de noms. Mais dans l'usage classique des outils, les lignes de dépense doivent traverser le modèle pour être filtrées par lui.

L'appel d'outils par programme inverse cela. Claude écrit un script Python, le script appelle vos outils dans une boucle, filtre les résultats, et seul ce que le script imprime revient au modèle. Les données brutes n'entrent jamais dans la fenêtre de contexte.

Ce qui se passe réellement

Voici la partie que presque tous les résumés de cette fonctionnalité ratent : votre outil ne s'exécute pas dans le bac à sable. Le conteneur d'Anthropic n'a aucun accès à votre base de données.

Ce qui se passe réellement, c'est que le code Python de Claude se met en pause au milieu de son exécution, l'API vous renvoie l'appel à vous, et l'interpréteur reprend une fois que vous avez répondu :

Guided walkthrough1 of 5
  1. Il s'exécute dans le conteneur d'exécution de code. Vos outils apparaissent à ce code comme des fonctions Python async — une par outil, chacune prenant un unique dictionnaire d'arguments et renvoyant une chaîne de caractères.

Parce que les fonctions sont async, Claude peut se déployer en éventail avec asyncio.gather et solliciter dix outils simultanément — ce que l'usage classique des outils ne peut qu'approcher avec des blocs d'outils parallèles.

À quoi ressemble réellement le code généré par Claude

import json

rows = json.loads(await query_database({"sql": "<sql>"}))
top = sorted(rows, key=lambda r: r["revenue"], reverse=True)[:5]
print(f"Top 5 customers: {top}")

Notez le json.loads. La fonction outil renvoie une chaîne de caractères — le texte littéral du tool_result que vous renvoyez. Si la description de votre outil ne dit pas « renvoie une liste de lignes sous forme d'objets JSON », Claude n'a aucun moyen de savoir qu'il peut désérialiser la chose, et il traitera vos données comme un bloc opaque. La phrase sur le format de sortie dans la description de votre outil cesse d'être de la documentation et devient du code porteur. C'est la ligne au plus fort effet de levier que vous écrirez en adoptant cette fonctionnalité.

L'activer

Un champ sur l'outil que vous voulez appeler depuis du code, plus l'outil d'exécution de code dans la requête :

Activer l'appel par programme sur un outil

{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": { "type": "object", "properties": { "sql": { "type": "string" } }, "required": ["sql"] },
"allowed_callers": ["code_execution_20260120"]
}

allowed_callers prend trois formes :

ValeurSignification
["direct"]Usage classique des outils. C'est la valeur par défaut quand le champ est omis.
["code_execution_20260120"]Claude est guidé pour ne l'appeler que depuis du code.
["direct", "code_execution_20260120"]L'un ou l'autre. La documentation le déconseille — choisissez-en un, pour que Claude reçoive un signal sans ambiguïté.

Chaque bloc tool_use de la réponse porte désormais un caller : soit {"type": "direct"}, soit un appelant d'exécution de code dont le tool_id correspond au bloc server_tool_use qui a exécuté le script. C'est ainsi que vous attribuez un appel au script qui l'a émis.

Ce n'est pas une frontière de sécurité

La documentation est exceptionnellement franche sur ce point, et il vaut la peine de le répéter car il est facile de supposer le contraire : allowed_callers contrôle la façon dont l'outil est présenté à Claude. Ce n'est pas un blocage strict au niveau de l'API. Claude est fortement guidé pour le respecter — mais votre client doit rester prêt à recevoir un tool_use direct pour n'importe quel outil qu'il définit, et vous ne devez pas utiliser ce champ comme mécanisme d'autorisation. L'autorisation appartient à votre gestionnaire d'outil, là où elle a toujours été.

Les chiffres

Les chiffres publiés par Anthropic elle-même, pour que vous puissiez juger si la complexité en vaut la peine :

  • Sur des tâches de recherche complexes, l'usage moyen est passé de 43 588 à 27 297 tokens — une réduction de 37 %.
  • Sur les benchmarks GIA, la précision est montée de 46,5 % à 51,2 % ; sur la récupération de connaissances internes, de 25,6 % à 28,5 %. Moins de tokens et de meilleures réponses, car le modèle raisonne sur des conclusions au lieu de se noyer dans des charges utiles brutes.
  • Sur les benchmarks de recherche agentique (BrowseComp, DeepSearchQA), superposer l'appel par programme aux outils de recherche de base a amélioré les performances de 11 % en moyenne tout en utilisant 24 % de tokens d'entrée en moins.
  • Latence : orchestrer plus de 20 appels d'outils dans un seul bloc de code élimine plus de 19 passes d'inférence.

La forme du gain est révélatrice. Cela paie quand vous avez au moins 3 appels dépendants, une boucle, un filtre ou un déploiement en éventail. Cela ne rapporte rien — et vous coûte un conteneur — quand Claude n'a besoin que d'un seul appel d'outil et veut de toute façon lire la réponse entière.

Watch out
  • Claude Haiku 4.5 accepte les nouveaux types d'outils mais ne prend PAS en charge l'appel d'outils par programme ni la persistance d'état du REPL qui en dépend. Les versions plus récentes s'y comportent silencieusement comme code_execution_20250825. Si vous routez vers Haiku pour réduire les coûts, vous n'obtenez pas cette fonctionnalité — et aucune erreur ne vous le dira.

Ce que cela coûte

L'appel d'outils par programme est facturé comme de l'exécution de code, et l'exécution de code est facturée à l'heure-conteneur, pas à l'appel :

  • 1 550 heures gratuites par mois, par organisation.
  • Au-delà, 0,05 $ par heure, par conteneur.
  • Le temps d'exécution a un minimum de 5 minutes — un script de deux secondes facture quand même cinq minutes de conteneur.
  • Si vous attachez des fichiers à la requête, le temps d'exécution est facturé même si l'outil n'est jamais invoqué, car les fichiers sont préchargés sur un conteneur quoi qu'il arrive.
  • C'est gratuit quand la même requête utilise aussi la recherche web ou la récupération web (web_search_20260209 / web_fetch_20260209 ou plus récent).

Deux conséquences à intégrer. D'abord, le plancher de 5 minutes signifie que beaucoup de conteneurs éphémères est le schéma coûteux ; réutiliser un seul conteneur sur toute une session est le schéma économique. Ensuite, cette fonctionnalité n'est pas éligible à la rétention zéro des données (ZDR) — si le ZDR est une exigence contractuelle pour vous, c'est un arrêt net, pas un réglage.

Les cinq façons dont cela casse

Guided walkthrough1 of 5
  1. Quand des appels d'outils par programme sont en attente, votre message de réponse ne doit contenir QUE des blocs tool_result. Pas du texte plus des résultats d'outils. Pas des résultats d'outils suivis d'une phrase polie. Uniquement des blocs tool_result.

Les chaînes de version, décodées

Les trois versions d'exécution de code sont disponibles en disponibilité générale et ne nécessitent aucun en-tête bêta :

VersionCe qu'elle ajoute
code_execution_20250825La base. Bash + Python + opérations sur fichiers. Prise en charge sur tous les modèles actuels.
code_execution_20260120Ajoute la persistance d'état du REPL et l'appel d'outils par programme. C'est celle dont vous avez besoin.
code_execution_20260521Runtime identique à 20260120. La seule différence est que la description de l'outil informe Claude de la limite de 90 secondes de temps réel par cellule Python, afin qu'il puisse budgéter les cellules longues. Une cellule qui dépasse la limite renvoie un return_code non nul avec un statut detection_timeout.

Cette dernière ligne est un beau morceau de conception d'API à remarquer : un incrément de version dont toute la charge utile est un meilleur prompt pour le modèle. Les deux chaînes sont interchangeables dans allowed_callers, et les réponses étiquettent toujours l'appelant comme code_execution_20260120, quelle que soit celle que vous avez déclarée.

Le conteneur lui-même n'a aucun accès à Internet — Claude ne peut pas faire de pip install à l'exécution, vous disposez donc du jeu de bibliothèques préinstallées (pandas, numpy, scipy, scikit-learn, statsmodels et consorts) et de rien d'autre. Les conteneurs sont sauvegardés après environ cinq minutes d'inactivité, restaurables par ID, et expirent 30 jours après leur création.

Quand y recourir

Recourez à l'appel d'outils par programme quand le modèle est utilisé comme une boucle et un filtre plutôt que comme un raisonneur : recherches par lots sur N entités, arrêt anticipé dès qu'une condition est remplie, sélection conditionnelle d'outil selon un résultat intermédiaire, ou réduction d'un dump de logs de 200 Ko aux dix lignes qui comptent.

Recourez plutôt au Tool Search Tool quand votre problème est que les définitions dévorent votre contexte avant même le premier appel — marquez les outils defer_loading: true et Claude les charge à la demande. Les deux sont complémentaires, pas alternatifs : la recherche d'outils trouve le bon outil, l'appel par programme l'exécute à moindre coût. Si vos définitions d'outils dépassent environ 10 000 tokens, il vous faut probablement les deux.

Et si vous abordez le problème par l'autre bout — un agent dont le contexte se noie dans les résultats d'outils MCP — commencez par Coût en tokens de MCP et Ingénierie du contexte, car les tokens les moins chers restent ceux que vous n'envoyez jamais.

Check yourself

0/5
  1. Où votre outil s'exécute-t-il réellement lors d'un appel d'outil par programme ?
  2. Pouvez-vous compter sur allowed_callers pour empêcher qu'un outil soit invoqué directement ?
  3. Votre agent route vers Claude Haiku 4.5 pour économiser et passe code_execution_20260120. Que se passe-t-il ?
  4. Quand un appel d'outil par programme est en attente, que peut contenir votre message de réponse ?
  5. Un script de deux secondes s'exécute dans un conteneur neuf. Combien de temps d'exécution de code est facturé ?
Appuyez sur Entrée ou Espace pour retourner la carte. Utilisez les flèches gauche et droite pour naviguer entre les cartes.Terme affiché.
1 / 7

Sources et lectures complémentaires