IA-BRIEF TERMINAL · ÉDITION N°173
LUN 22 JUIN 2026 20:32 UTC+1

Comparatif

Structured Outputs et JSON Schema : fiabiliser vos API LLM en 2026

Publié
MAJ
Par Stefan
Lecture 11 min

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

  1. 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.
  2. Normalisation CRM : un email de prospect arrive, le LLM extrait nom, entreprise, email, telephone, besoin_exprime dans un format standard injectable directement dans Salesforce ou HubSpot.
  3. 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.
  4. 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

CritereClaude (tool_use)OpenAI (json_schema)Mistral (function calling)
MecanismeTool avec input_schemaresponse_format strictFunction calling
Garantie schemaQuasi-100 % (force via tool_choice)100 % (garanti par le moteur)Quasi-100 % (force via tool_choice)
Schemas imbriquesOuiOui (strict mode)Oui
Cout supplementaire~200-500 tokens/schema~200-500 tokens/schema~200-500 tokens/schema
Prompt cachingOui (-90 % sur schema cache)Non equivalent natifNon
Avantage cleCaching + contexte 1M tokensGarantie 100 % stricteOpen-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 refusal dans la reponse ; Claude retourne un bloc text au lieu de tool_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 schemaTokens supplementairesSurcout 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

  1. 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.
  2. Forcez toujours l’appel d’outil (tool_choice sur Claude et Mistral, strict: True sur OpenAI). Sans cette contrainte, le modele peut revenir au texte libre.
  3. Validez deux fois : le schema LLM pour la structure, Zod/Pydantic pour la semantique.
  4. Cachez vos schemas. Avec le prompt caching d’Anthropic, la definition d’outil est facturee une seule fois puis reutilisee a -90 % du cout.
  5. Prevoyez les refus. Definissez dans votre code un comportement clair quand le modele refuse de repondre (contenu sensible, informations insuffisantes).
  6. Documentez vos schemas. Le champ description de 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.

Sources primaires