Strict tool use : des appels que votre backend accepte
Sur cette page
Objectifs
À l’issue de cette leçon, vous serez capable de :
- Activer
strict: truepour que l’entrée d’un appel d’outil respecte toujours votre schéma - Générer le schéma depuis votre validateur pour que l’API et le backend restent d’accord
- Connaître les limites et les incompatibilités du mode strict
Le problème : un appel valide pour Claude, rejeté par votre code
Sans mode strict, input_schema décrit l’entrée attendue, mais rien ne l’impose. Votre backend rejette alors parfois un appel produit par Claude lui-même : une chaîne là où il fallait un entier, un champ obligatoire manquant, un format inattendu.
Comment fonctionne le mode strict
Avec strict: true, l’API compile d’abord votre JSON Schema en grammaire : un ensemble de règles qui disent quels jetons peuvent venir à chaque position. Quand Claude écrit l’entrée de l’outil, il ne peut choisir que des jetons que la grammaire autorise : c’est l’échantillonnage contraint par grammaire. Un champ entier reçoit un entier, non pas « presque toujours », mais toujours.
Deux lignes suffisent : le drapeau, au niveau de la définition (à côté de name et input_schema, hors du schéma), et additionalProperties: false, exigé sur chaque objet du schéma.
{
"name": "demander_visa",
"description": "Met en file un jeu de plans pour le visa d'un ingénieur habilité.",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"plan_id": {"type": "string"},
"revision": {"type": "integer"}
},
"required": ["plan_id", "revision"],
"additionalProperties": false
}
}
Les structured outputs sont disponibles en version stable : aucun en-tête bêta n’est nécessaire.
Générer le schéma depuis le validateur
Un appel peut respecter le schéma et être quand même rejeté, si le schéma écrit à la main a divergé des règles du backend : un numéro d’habilitation décrit comme une simple chaîne passera le schéma et échouera au validateur qui exige HAB- suivi de cinq chiffres. Écrivez les règles une fois, dans le validateur, et générez le schéma à partir de lui (Pydantic en Python, Zod en TypeScript) :
from anthropic import transform_schema
from pydantic import BaseModel, ConfigDict, Field
class DemandeVisa(BaseModel):
model_config = ConfigDict(extra="forbid") # produit additionalProperties: false
plan_id: str
revision: int
numero_habilitation: str | None = Field(default=None, pattern=r"^HAB-\d{5}$")
outil_visa = {
"name": "demander_visa",
"description": "…",
"input_schema": transform_schema(DemandeVisa),
"strict": True,
}
# À réception de l'appel :
demande = DemandeVisa.model_validate(appel.input)
La fonction transform_schema du SDK Python prépare le schéma pour l’API : elle retire les contraintes que la grammaire ne prend pas en charge, les reporte dans la description du champ, et ajoute additionalProperties: false à chaque objet. Le motif du numéro d’habilitation, lui, reste dans le schéma : une expression régulière simple comme ^HAB-\d{5}$ fait partie des motifs acceptés. Votre validateur continue de vérifier toutes les règles.
Le mode strict garantit la forme, pas la vérité : si l’information manque, un champ facultatif nullable revient vide plutôt qu’inventé. Votre validateur ne vérifie plus que les règles métier.
Ce qu’il faut savoir avant de déployer
- Des schémas simples. Les contraintes numériques (
minimum,maximum,multipleOf) et de longueur (minLength,maxLength) ne font pas partie de la grammaire : écrivez-les dans la description du champ, et validez côté client. Les helpers des SDK, commetransform_schemaoumessages.parse, les retirent du schéma envoyé et les reportent dans la description ; la validation dans votre code les vérifie encore. Les schémas récursifs ne sont pas pris en charge. - Des limites qui se cumulent. Au plus 20 outils stricts par requête, 24 paramètres facultatifs et 16 paramètres à types multiples (
anyOfou tableau de types), au total, sur tous les schémas stricts d’une requête. Quatre outils stricts à six paramètres facultatifs chacun atteignent déjà la limite. Au-delà des limites internes, l’API renvoie une erreur 400 « Schema is too complex for compilation » : répartissez les outils sur plusieurs requêtes ou réduisez les paramètres facultatifs. - Un cache de grammaire. La première requête avec un nouveau schéma paie la compilation ; la grammaire est ensuite mise en cache jusqu’à 24 heures après sa dernière utilisation. Seul le schéma est conservé, pas vos prompts ni les sorties : n’y placez donc pas de données sensibles, dans les noms de propriétés, les énumérations ou les motifs.
- Pas avec les citations. Les sorties structurées sont incompatibles avec les citations de documents : la combinaison renvoie une erreur 400.
Forcer un appel : attention aux modèles récents
Avec les modèles qui l’acceptent, tool_choice: {"type": "any"} associé à strict: true garantissait qu’un outil serait appelé avec une entrée valide. Mais Claude Opus 5.5, Claude Sonnet 5.5, Claude Fable 5.1 et Claude Mythos 5.1 refusent l’appel forcé : {"type": "any"} et {"type": "tool", ...} y renvoient une erreur 400 (Claude Opus 5 et Fable 5 l’acceptent encore). Sur ces modèles, laissez tool_choice sur auto, gardez strict: true, demandez explicitement l’outil dans le prompt, et vérifiez qu’un appel a bien eu lieu, avec une nouvelle tentative sinon (leçon 5). Si l’appel forcé ne servait qu’à extraire du JSON, utilisez plutôt les sorties JSON (output_config.format, leçon 6).
À vous de jouer
Rendre un outil strict.
- Choisissez l’outil dont les appels sont le plus souvent rejetés par votre backend.
- Générez son schéma depuis votre validateur, avec
additionalProperties: falsepartout, et ajoutezstrict: true. - Déplacez les bornes numériques et les longueurs dans les descriptions.
- Rejouez cent requêtes réelles avant et après, et comptez les rejets.
Sources officielles : Structured outputs (documentation Claude : limites des schémas, motifs, transformation par les SDK) · Strict tool use (documentation Claude) · Define tools — Forcing tool use (documentation Claude : modèles qui refusent l’appel forcé)
Points clés à retenir
strict: truecontraint l’échantillonnage à votre schéma : une entrée invalide devient impossible- Il exige
additionalProperties: falsesur chaque objet ; générez le schéma depuis votre validateur - Gardez le schéma simple : bornes et longueurs dans les descriptions, et attention aux limites cumulées (24 facultatifs, 16 unions)
- Sur les modèles qui refusent l’appel forcé,
auto+strict+ une consigne explicite, et une vérification qu’un appel a eu lieu