Aller au contenu principal
Leçon 8 sur 16 · L'utilisation d'outils (tool use) avec la Messages API

Strict tool use : des appels que votre backend accepte

Leçon 8 sur 16 Mis à jour le 5 novembre 2026 Développeur
Sur cette page
  1. Objectifs
  2. Le problème : un appel valide pour Claude, rejeté par votre code
  3. Comment fonctionne le mode strict
  4. Générer le schéma depuis le validateur
  5. Ce qu’il faut savoir avant de déployer
  6. Forcer un appel : attention aux modèles récents
  7. À vous de jouer
  8. Points clés à retenir

Objectifs

À l’issue de cette leçon, vous serez capable de :

  • Activer strict: true pour 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.

Du schéma à l'entrée garantie Quatre étapes : le JSON Schema de l'outil, compilé en grammaire et gardé en cache jusqu'à 24 heures, puis l'échantillonnage contraint, et une entrée toujours conforme à la forme du schéma, que votre validateur contrôle encore sur le fond. STRICT TOOL USE Du schéma à l'entrée garantie 1 Schéma additionalProperties false partout 2 Grammaire compilée, en cache 24 h 3 Échantillonnage seuls les jetons permis 4 Entrée conforme à la forme Le mode strict garantit la forme, pas la vérité : votre validateur vérifie encore les règles métier.
D'après les pages Strict tool use et Structured outputs (documentation Claude).

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, comme transform_schema ou messages.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 (anyOf ou 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.

  1. Choisissez l’outil dont les appels sont le plus souvent rejetés par votre backend.
  2. Générez son schéma depuis votre validateur, avec additionalProperties: false partout, et ajoutez strict: true.
  3. Déplacez les bornes numériques et les longueurs dans les descriptions.
  4. 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: true contraint l’échantillonnage à votre schéma : une entrée invalide devient impossible
  • Il exige additionalProperties: false sur 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