Comparatif
Structured Outputs et JSON Schema : fiabiliser vos API LLM en 2026
Quand une PME connecte un LLM a son systeme d’information — CRM, ERP, facturation — la question n’est plus “le modele comprend-il ma demande ?” mais “le modele retourne-t-il exactement la structure de donnees que mon code attend ?”. Un JSON mal forme, un champ manquant, un type inattendu : et c’est le pipeline entier qui casse. Les Structured Outputs resolvent ce probleme en forcant le LLM a produire du JSON strictement conforme a un schema predefini. Voici comment les implementer en 2026 avec Claude, OpenAI et Mistral.
Pourquoi les Structured Outputs changent la donne pour les PME
Avant les Structured Outputs, integrer un LLM dans un workflow automatise relevait du bricolage : on ajoutait “reponds en JSON” dans le prompt, on priait pour que le modele ne glisse pas un commentaire en langage naturel autour du JSON, et on empilait des try/catch cote code pour gerer les reponses mal formees.
Le taux d’echec typique d’un prompt “reponds en JSON” sans contrainte de schema : 5 a 15 % des appels produisent un JSON invalide ou incomplet, selon la complexite de la tache. Sur 1 000 appels par jour, cela represente 50 a 150 erreurs a gerer — inacceptable pour un workflow de production.
Les Structured Outputs eliminent cette classe d’erreurs en imposant un JSON Schema au niveau de l’API. Le modele ne peut physiquement pas retourner une reponse qui viole le schema. Le resultat : un taux d’adherence structurelle de 100 % (garanti par OpenAI) ou quasi-100 % (Claude et Mistral via tool_use et function calling).
Quatre cas d’usage concrets pour PME
- Extraction de factures : un PDF de facture fournisseur entre, un objet JSON sort avec
numero_facture,date,montant_ht,montant_ttc,lignes[]. Le schema garantit que chaque champ existe et a le bon type. - Normalisation CRM : un email de prospect arrive, le LLM extrait
nom,entreprise,email,telephone,besoin_exprimedans un format standard injectable directement dans Salesforce ou HubSpot. - Qualification de leads : le modele analyse une conversation commerciale et retourne un score structure (
score_budget,score_autorite,score_besoin,score_timing) sur une echelle definie dans le schema. - Generation de rapports : le LLM produit un objet JSON avec
titre,resume,sections[],recommandations[]qui alimente un template de rapport PDF ou PowerPoint.
Pour aller plus loin sur l’evaluation des LLM pour ces taches metier, consultez notre framework d’evaluation en 6 criteres.
Comparatif : trois approches en 2026
Claude (Anthropic) — tool_use avec JSON Schema
L’API Claude utilise le mecanisme tool_use pour les Structured Outputs. Le principe : vous definissez un “outil” dont le input_schema est un JSON Schema standard, puis vous forcez le modele a appeler cet outil.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6-20260514",
max_tokens=1024,
tools=[{
"name": "extraire_facture",
"description": "Extrait les donnees structurees d'une facture",
"input_schema": {
"type": "object",
"properties": {
"numero_facture": {"type": "string"},
"date": {"type": "string", "format": "date"},
"montant_ht": {"type": "number"},
"montant_ttc": {"type": "number"},
"fournisseur": {"type": "string"}
},
"required": ["numero_facture", "date", "montant_ht",
"montant_ttc", "fournisseur"]
}
}],
tool_choice={"type": "tool", "name": "extraire_facture"},
messages=[{
"role": "user",
"content": "Extrais les donnees de cette facture : [contenu]"
}]
)
# La reponse contient un bloc tool_use avec le JSON conforme
facture = response.content[0].input
Le point cle : tool_choice force l’appel de l’outil, ce qui garantit une reponse JSON conforme au schema. Sans cette option, Claude pourrait repondre en texte libre.
OpenAI — response_format json_schema
OpenAI propose une approche differente avec response_format: {type: "json_schema"}. Le schema est defini directement dans le format de reponse, sans passer par un outil.
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-5.5",
response_format={
"type": "json_schema",
"json_schema": {
"name": "facture",
"strict": True,
"schema": {
"type": "object",
"properties": {
"numero_facture": {"type": "string"},
"date": {"type": "string"},
"montant_ht": {"type": "number"},
"montant_ttc": {"type": "number"},
"fournisseur": {"type": "string"}
},
"required": ["numero_facture", "date", "montant_ht",
"montant_ttc", "fournisseur"],
"additionalProperties": False
}
}
},
messages=[{
"role": "user",
"content": "Extrais les donnees de cette facture : [contenu]"
}]
)
facture = json.loads(response.choices[0].message.content)
OpenAI garantit 100 % d’adherence au schema quand strict: True est active. C’est la garantie la plus forte du marche en 2026.
Mistral — function calling
Mistral supporte le function calling avec JSON mode, une approche similaire a celle de Claude mais avec sa propre syntaxe.
from mistralai import Mistral
client = Mistral(api_key="votre_cle")
response = client.chat.complete(
model="mistral-large-latest",
messages=[{
"role": "user",
"content": "Extrais les donnees de cette facture : [contenu]"
}],
tools=[{
"type": "function",
"function": {
"name": "extraire_facture",
"description": "Extrait les donnees d'une facture",
"parameters": {
"type": "object",
"properties": {
"numero_facture": {"type": "string"},
"date": {"type": "string"},
"montant_ht": {"type": "number"},
"montant_ttc": {"type": "number"},
"fournisseur": {"type": "string"}
},
"required": ["numero_facture", "date", "montant_ht",
"montant_ttc", "fournisseur"]
}
}
}],
tool_choice="any"
)
Mistral propose egalement un response_format: {"type": "json_object"} pour du JSON libre (sans schema strict), utile pour le prototypage rapide.
Tableau comparatif
| Critere | Claude (tool_use) | OpenAI (json_schema) | Mistral (function calling) |
|---|---|---|---|
| Mecanisme | Tool avec input_schema | response_format strict | Function calling |
| Garantie schema | Quasi-100 % (force via tool_choice) | 100 % (garanti par le moteur) | Quasi-100 % (force via tool_choice) |
| Schemas imbriques | Oui | Oui (strict mode) | Oui |
| Cout supplementaire | ~200-500 tokens/schema | ~200-500 tokens/schema | ~200-500 tokens/schema |
| Prompt caching | Oui (-90 % sur schema cache) | Non equivalent natif | Non |
| Avantage cle | Caching + contexte 1M tokens | Garantie 100 % stricte | Open-weights, souverainete |
Si vous envisagez de migrer entre ces fournisseurs, notre checklist de migration GPT vers Claude couvre les adaptations necessaires cote Structured Outputs.
Gestion des erreurs : quand le modele ne peut pas se conformer
Meme avec des Structured Outputs, des cas limites existent :
- Refus du modele : si le contenu viole les politiques de securite, le modele peut refuser de repondre. OpenAI retourne un
refusaldans la reponse ; Claude retourne un bloctextau lieu detool_use. - Contenu insuffisant : si le document source ne contient pas les informations demandees, le modele doit remplir les champs obligatoires avec des valeurs par defaut ou des marqueurs (
null, chaine vide). Definissez explicitement ce comportement dans votre schema ("nullable": true) et dans le prompt systeme. - Timeout ou rate limit : les appels API peuvent echouer avant que le modele ne produise sa reponse. Implementez un retry avec backoff exponentiel (3 tentatives, delais de 1s, 2s, 4s).
Bonne pratique : encapsulez chaque appel API dans un bloc de validation qui verifie (1) le code HTTP, (2) la presence du bloc structure dans la reponse, (3) la conformite du JSON au schema via une validation client.
Validation cote client : Zod et Pydantic
Les Structured Outputs garantissent la conformite structurelle (le JSON respecte le schema). Ils ne garantissent pas la validite semantique (les valeurs ont du sens). C’est pourquoi la validation cote client est indispensable.
Pydantic (Python)
from pydantic import BaseModel, EmailStr, field_validator
from datetime import date
class Facture(BaseModel):
numero_facture: str
date: date
montant_ht: float
montant_ttc: float
fournisseur: str
@field_validator('montant_ttc')
@classmethod
def ttc_superieur_ht(cls, v, info):
if 'montant_ht' in info.data and v < info.data['montant_ht']:
raise ValueError('TTC doit etre >= HT')
return v
# Validation apres reception de la reponse LLM
facture = Facture(**response_json) # Leve une exception si invalide
Zod (TypeScript)
import { z } from 'zod';
const FactureSchema = z.object({
numero_facture: z.string().min(1),
date: z.string().date(),
montant_ht: z.number().positive(),
montant_ttc: z.number().positive(),
fournisseur: z.string().min(1),
}).refine(
(data) => data.montant_ttc >= data.montant_ht,
{ message: "TTC doit etre >= HT" }
);
// Validation apres reception
const facture = FactureSchema.parse(responseJson);
La double validation — schema LLM + validation client — est le pattern le plus robuste pour la production. Le schema LLM attrape les erreurs de structure, la validation client attrape les erreurs de logique metier.
Impact sur les couts : ce qu’il faut anticiper
Le schema JSON est transmis au modele sous forme de tokens. Voici les ordres de grandeur :
| Type de schema | Tokens supplementaires | Surcout par appel (Sonnet 4.6) |
|---|---|---|
| Schema plat, 5 champs | ~200 tokens | ~0,0006 $ |
| Schema imbrique, 15 champs | ~800 tokens | ~0,0024 $ |
| Schema complexe, 30+ champs | ~2 000 tokens | ~0,006 $ |
Sur 1 000 appels par jour avec un schema moyen (800 tokens), le surcout mensuel est d’environ 72 $ avec Sonnet 4.6 (3 $/M tokens entree). Avec le prompt caching d’Anthropic (reduction de 90 % sur les cache hits), ce surcout tombe a ~7 $/mois — negligeable.
Conseil : activez systematiquement le prompt caching sur les definitions d’outils et le system prompt si vous utilisez Claude. Consultez notre guide sur les connecteurs et integrations Claude pour l’entreprise pour la mise en place cote infrastructure.
Strategie d’adoption : du simple au complexe
Phase 1 — Validation du concept (semaine 1)
- Choisissez un seul cas d’usage (extraction d’emails, par exemple).
- Definissez un schema plat avec 3 a 5 champs.
- Testez avec Claude Haiku 4.5 (1 $/M tokens entree) pour minimiser les couts d’experimentation.
- Mesurez le taux de conformite sur 100 appels.
Phase 2 — Complexification du schema (semaines 2-3)
- Ajoutez des objets imbriques et des tableaux (
lignes_facture[],contacts[]). - Implementez la validation Pydantic ou Zod cote client.
- Montez en gamme vers Sonnet 4.6 si la qualite d’extraction sur votre tache metier l’exige.
- Activez le prompt caching pour optimiser les couts.
Phase 3 — Production et monitoring (semaine 4+)
- Ajoutez des logs structures pour chaque appel (latence, tokens, taux d’erreur).
- Implementez un circuit breaker avec fallback multi-fournisseur (Claude en primaire, OpenAI ou Mistral en secondaire).
- Mettez en place des alertes sur le taux d’echec de validation Pydantic/Zod (seuil recommande : < 1 %).
Bonnes pratiques a retenir
- Commencez par des schemas plats. Les objets imbriques augmentent la complexite et le risque d’erreur semantique. Validez d’abord sur un schema simple avant d’imbriquer.
- Forcez toujours l’appel d’outil (
tool_choicesur Claude et Mistral,strict: Truesur OpenAI). Sans cette contrainte, le modele peut revenir au texte libre. - Validez deux fois : le schema LLM pour la structure, Zod/Pydantic pour la semantique.
- Cachez vos schemas. Avec le prompt caching d’Anthropic, la definition d’outil est facturee une seule fois puis reutilisee a -90 % du cout.
- Prevoyez les refus. Definissez dans votre code un comportement clair quand le modele refuse de repondre (contenu sensible, informations insuffisantes).
- Documentez vos schemas. Le champ
descriptionde chaque propriete du JSON Schema est lu par le modele : plus la description est precise, meilleure est l’extraction.
FAQ
Quelle est la difference entre tool_use (Claude) et json_schema (OpenAI) pour les Structured Outputs ?
Claude utilise le mecanisme tool_use avec un JSON Schema defini dans la configuration de l’outil. En forcant l’appel d’un outil specifique (tool_choice: {type: 'tool', name: 'mon_outil'}), Claude retourne systematiquement un objet JSON conforme au schema. OpenAI propose response_format: {type: 'json_schema'} qui garantit 100 % d’adherence au schema directement dans la reponse du modele, sans passer par un outil. Les deux approches produisent du JSON fiable, mais l’implementation cote code differe.
Les Structured Outputs augmentent-ils le cout des appels API ?
Oui, legerement. Le schema JSON est injecte dans le prompt systeme ou la definition d’outil, ce qui consomme des tokens supplementaires en entree. Pour un schema simple (5-10 champs), comptez 200-500 tokens de surcharge. Pour un schema complexe imbrique, cela peut atteindre 1 000-2 000 tokens. Avec le prompt caching d’Anthropic (reduction jusqu’a 90 % sur les tokens caches), cette surcharge devient negligeable si vous repetez le meme schema sur plusieurs appels.
Faut-il valider cote client meme avec des Structured Outputs garantis ?
Oui, toujours. Meme si le modele garantit la conformite structurelle (types, champs requis), il ne garantit pas la validite semantique. Un champ “email” peut contenir une chaine de caracteres syntaxiquement valide mais fausse. Utilisez Zod (TypeScript) ou Pydantic (Python) pour valider a la fois la structure et les contraintes metier (format email, plage de dates, valeurs enumerees).
Par quoi commencer pour une PME qui debute avec les Structured Outputs ?
Commencez par un cas d’usage simple avec un schema plat (sans imbrication) : par exemple, extraire nom, email et numero de telephone d’un email entrant. Testez avec Claude Haiku 4.5 (le moins cher a 1 $/M tokens entree) pour valider le concept. Une fois le pipeline stable, complexifiez le schema (objets imbriques, tableaux) et montez en gamme vers Sonnet 4.6 si la qualite d’extraction l’exige.