Tensoria
Parlez-nous de votre projet : 07 82 80 51 40
Outils & Modèles Par

Structured output JSON et constrained decoding : JSON fiable avec un LLM

structured output JSON LLM constrained decoding - garantir un JSON valide avec un modèle de langage

Obtenir un JSON garanti valide avec un LLM, c'est possible - et ce n'est plus une question de chance ou de prompt engineering avance. Le constrained decoding masque les tokens syntaxiquement invalides token par token pendant la génération : le modèle ne peut physiquement pas produire un JSON malformé. En production, les solutions modernes (XGrammar, Outlines, OpenAI Strict Mode) atteignent moins de 0,1% d'echecs de parsing contre 8 a 15% sans enforcement. Le gain est immédiat, mesurable, et applicable aussi bien aux grands modèles qu'aux SLM déployés en local.

Cet article couvre tout le spectre : pourquoi le JSON casse en pratique, la différence entre JSON mode et function calling, le fonctionnement du constrained decoding et des grammaires GBNF, les bibliothèques de référence en 2026 (XGrammar, Outlines, LLGuidance, Guidance), la validation avec Pydantic et JSON Schema, l'impact réel sur la latence, et les cas d'usage concrets en extraction documentaire.

Pourquoi le JSON casse en pratique avec un LLM

Un LLM génère du texte token par token, de gauche à droite, sans connaissance de la structure globale qu'il est en train de construire. Quand on lui demande "réponds en JSON", il produit des tokens qui ressemblent à du JSON - mais rien dans son architecture ne garantit la conformité syntaxique.

Les erreurs les plus fréquentes en production :

  • Virgule finale dans un objet ou un tableau. Les modèles la génèrent souvent par analogie avec Python. JSON interdit les trailing commas - le parseur plante.
  • Guillemets simples au lieu de doubles. JSON impose les guillemets doubles. Les modèles qui ont vu beaucoup de Python produisent parfois des guillemets simples.
  • Champs manquants ou surnuméraires. Le modèle oublie un champ obligatoire, ou invente un champ qui n'est pas dans le schéma.
  • Types incorrects. Un montant retourné comme string "1250" au lieu de number 1250, un booléen retourné comme "true" (string) au lieu de true.
  • Texte hors JSON. Le modèle ajoute une phrase d'introduction ou une explication après le bloc JSON - le parseur échoue sur le suffixe.
  • Troncature. Sur des sorties longues, la génération s'arrête en milieu de structure si la fenêtre de contexte est saturée.

Sans enforcement, les études de production mesuraient en 2024-2025 un taux d'echec de parsing entre 8 et 15% selon les modèles et la complexité des schémas (JSONSchemaBench, arXiv 2501.10868). Sur un pipeline d'extraction de 10 000 documents par jour, c'est entre 800 et 1 500 documents qui nécessitent un retry ou un traitement manuel. Le constrained decoding fait tomber ce taux en dessous de 0,1%.

Distinction clé

Le constrained decoding garantit la conformité syntaxique, pas la justesse sémantique. Un JSON parfaitement valide peut contenir des valeurs incorrectes (mauvais montant, mauvaise entité, mauvaise catégorie). La fiabilité syntaxique est le prérequis, pas la fin. La qualité sémantique dépend du modèle, du prompt et des données d'entraînement.

JSON mode, function calling, structured output strict : quelles differences

Les trois approches sont souvent confondues. Elles ne donnent pas les mêmes garanties.

JSON mode

Le JSON mode est l'approche la plus simple. On indique au modèle (via le paramètre response_format: {"type": "json_object"} chez OpenAI, ou via le prompt) de produire du JSON. Le modèle fait de son mieux, mais aucune contrainte technique n'est appliquée pendant la génération. Résultat : le JSON est valide dans la grande majorité des cas, mais les erreurs structurelles (champs manquants, types incorrects) persistent. C'est nettement mieux que rien, mais insuffisant pour un pipeline de production sans retry.

Function calling / tool calling

Le function calling (OpenAI) ou tool use (Anthropic) va plus loin : vous déclarez un schéma précis (noms de champs, types, propriétés requises) et le modèle est orienté vers ce schéma pendant l'inférence. En mode non-strict, les grandes APIs ajoutent un post-processing pour corriger les erreurs mineures. Les taux d'echec de parsing chutent à 1-3%. Mais la conformité n'est pas garantie à 100%, notamment sur les champs optionnels et les enums complexes.

Structured output strict (constrained decoding)

Le structured output strict - implémenté par OpenAI depuis août 2024, Anthropic en beta novembre 2025 puis en GA début 2026, Gemini en 2024 - applique un masquage de tokens pendant la génération. À chaque étape, le modèle ne peut choisir que parmi les tokens compatibles avec l'état courant de la grammaire. Le JSON produit est syntaxiquement conforme à 100%. Les taux d'echec de parsing mesurés en production :

Approche Taux d'echec parsing Overhead tokens Garantie schéma
Texte libre + parsing 15-30% 0 Aucune
JSON mode 2-8% 20-50 Syntaxe JSON basique
Function calling (non-strict) 1-3% 60-150 Partielle (best-effort)
OpenAI Structured Output strict <0,1% 80-120 Complète (schéma + types)
Anthropic tool use strict <0,2% 150-300 Complète
Constrained decoding local (XGrammar) <0,01% 0 (natif) Complète + latence minimale

Le constrained decoding : comment ca marche techniquement

Le principe est simple a comprendre, meme sans plonger dans le code. Un LLM génère a chaque étape une distribution de probabilités sur tout le vocabulaire (les logits). Normalement, on échantillonne dans cette distribution pour choisir le prochain token.

Le constrained decoding ajoute une étape entre le calcul des logits et l'échantillonnage : un masque binaire est appliqué sur le vocabulaire. Les tokens qui ne sont pas compatibles avec l'état courant de la grammaire voient leur probabilité mise a 0. Seuls les tokens valides restent candidats.

Les grammaires GBNF dans llama.cpp

GBNF (GGML Backus-Naur Form) est le format de grammaire utilisé par llama.cpp pour le constrained decoding. Il permet de décrire n'importe quelle structure : JSON, SQL, expressions régulières, formats propriétaires. Llama.cpp inclut un convertisseur JSON Schema vers GBNF (common/json-schema-to-grammar.cpp) qui supporte les types de base (string, number, boolean, null, array, object), les contraintes (minLength, maxLength, pattern, enum), et les combinateurs (oneOf, anyOf, allOf).

Le résultat : même un modèle 3B déployé localement avec llama.cpp produit un JSON syntaxiquement parfait si on lui fournit la grammaire correspondante. L'extraction documentaire avec un SLM - qui échouait souvent sur la syntaxe - devient beaucoup plus robuste.

XGrammar : le moteur de référence en 2026

XGrammar (Dong et al., MLSys 2025, arXiv 2411.15100) a résolu le problème de performance des approches précédentes. L'insight clé : dans un vocabulaire typique de LLM, ~99% des tokens sont "context-independent" - leur validité ne dépend pas de l'état courant du parseur. Ces tokens peuvent être entierement pré-calculés en bitmasks. Seul 1% des tokens nécessite un calcul dynamique a chaque étape.

Résultat : XGrammar atteint une latence inférieure a 40 microsecondes par token pour la génération JSON, soit un overhead quasi nul par rapport a une génération sans contrainte. C'est 3x plus rapide qu'Outlines sur JSON Schema, et jusqu'a 100x plus rapide sur les grammaires contexte-libre complexes. XGrammar est devenu le backend par défaut de vLLM, SGLang, TensorRT-LLM et MLC-LLM courant 2025-2026.

Les bibliothèques du marché : Outlines, XGrammar, LLGuidance, Guidance

Pour les équipes qui déploient leurs propres modèles, plusieurs bibliothèques sont disponibles selon le contexte.

Outlines (dottxt-ai)

Outlines est la bibliothèque Python qui a popularisé le structured output pour les modèles HuggingFace. Elle supporte JSON Schema, regex, grammaires EBNF, et les modèles Pydantic directement. Interface simple : on passe un modèle Pydantic ou un JSON Schema, Outlines gère la contrainte pendant la génération. Après la réécriture du noyau en Rust (outlines-core), les performances sont nettement améliorées, mais XGrammar reste plus rapide sur les schémas complexes. Outlines est le bon choix pour un projet de prototypage rapide avec des modèles HuggingFace.

XGrammar (MLC AI)

XGrammar est le choix pour la production a fort volume. Son intégration native dans vLLM signifie qu'aucune configuration spéciale n'est nécessaire pour les équipes qui utilisent déja vLLM comme serveur d'inférence : le constrained decoding est activé par défaut sur les requêtes avec un JSON Schema. XGrammar-2 (présenté a ACM CAIS '26 en mai 2026) étend le support aux schémas dynamiques pour les architectures agentiques.

LLGuidance (Microsoft)

LLGuidance est la bibliothèque de Microsoft, implémentée en Rust avec des bindings Python. Excellente couverture JSON Schema, vitesse comparable a XGrammar, et intégration native dans llama.cpp depuis fin 2024. C'est le backend alternatif a GBNF pour ceux qui cherchent une couverture JSON Schema plus complete que ce que GBNF permet nativement.

Guidance (Microsoft)

Guidance est un framework de plus haut niveau : il permet d'entrelacer génération libre et génération contrainte dans un meme prompt, de faire des branchements conditionnels selon les sorties intermédiaires, et de construire des pipelines de génération complexes. Plus expressif qu'Outlines mais aussi plus verbeux. Utile pour les pipelines d'extraction multi-étapes ou certaines parties du document sont libres et d'autres structurées.

Comparatif synthétique

Bibliothèque Backend Latence JSON JSON Schema Intégration
XGrammar C++ / Python <40 µs/token Complète vLLM, SGLang, TRT-LLM, MLC
LLGuidance Rust / Python ~40-60 µs/token Très complète llama.cpp, vLLM
Outlines Python (Rust core) ~80-200 µs/token Bonne HuggingFace, vLLM
GBNF llama.cpp C++ ~50-100 µs/token Partielle (Draft 7) llama.cpp natif
Guidance Python Variable Bonne HuggingFace, OpenAI

Validation avec Pydantic et JSON Schema : le pipeline complet

Le constrained decoding garantit la syntaxe. La validation Pydantic garantit les contraintes métier que la grammaire ne peut pas exprimer. Les deux couches sont complémentaires.

Le pattern standard en 2026

Le pipeline typique pour une extraction documentaire en production :

  • Définir un modèle Pydantic avec les types, les champs obligatoires, les contraintes (plages numériques, formats regex, enums).
  • Exporter en JSON Schema (MonModele.model_json_schema()) pour le passer au moteur de constrained decoding.
  • Générer avec contrainte : le LLM produit un JSON syntaxiquement conforme au schéma.
  • Valider avec Pydantic (MonModele.model_validate_json(sortie)) pour les contraintes sémantiques que la grammaire ne couvre pas.
  • Gérer les erreurs sémantiques : si la validation Pydantic échoue malgré le constrained decoding, c'est une erreur de fond (le modèle a produit une valeur valide syntaxiquement mais incohérente). Décider du retry ou du fallback selon le cas d'usage.

La bibliothèque instructor (Python) encapsule ce pipeline et gère les retries automatiques avec extraction du message d'erreur Pydantic comme feedback au modèle. Elle supporte OpenAI, Anthropic, Gemini et les modèles locaux via Ollama ou vLLM.

Conseil de production

Ne pas définir des schémas trop complexes d'un coup. Un schéma avec 20 champs imbriqués sur 3 niveaux est plus difficile a respecter sémantiquement qu'un schéma en 5 champs plats, meme avec constrained decoding. Préférer plusieurs extractions successives simples a une seule extraction monolithique complexe - la latence totale est souvent comparable et la fiabilité sémantique est nettement supérieure.

Limites du constrained decoding sur les schémas JSON Schema

Certaines contraintes JSON Schema sont difficiles ou impossibles a exprimer comme grammaires régulières : les dépendances conditionnelles (if/then/else), la validation croisée entre champs, les contraintes numériques précises (minimum, maximum, multipleOf). Les bibliothèques gèrent ces cas différemment : llama.cpp/GBNF les ignore silencieusement (seule la structure est contrainte), XGrammar et LLGuidance ont une meilleure couverture mais pas exhaustive. La validation Pydantic en aval reste donc indispensable pour ces cas.

Cas d'usage : extraction documentaire avec un SLM

L'extraction documentaire est le cas d'usage ou structured output et SLM se combinent le mieux. Voici ce que ca donne en pratique sur un pipeline concret.

Extraction de factures et bons de commande

Tâche typique : extraire d'une facture PDF (converti en texte) les champs suivants - numéro de facture, date, fournisseur, montant HT, TVA, montant TTC, ligne de produits avec quantité et prix unitaire. Un schéma Pydantic modélise cette structure, exporté en JSON Schema pour le constrained decoding.

Avec un SLM 7-8B (Qwen2.5-7B ou Ministral 8B) et XGrammar via vLLM, les mesures observées en production :

  • Taux de JSON valide syntaxiquement : proche de 100% (vs 78-85% sans constrained decoding avec le meme modèle)
  • Précision sémantique sur les champs numériques : 92-96% selon la qualité du texte extrait du PDF
  • Latence par document : 300-800 ms selon la longueur (SLM 8B sur GPU A10G)
  • Cout d'inférence : ~0,02-0,05 € par document en self-hosting, vs 0,15-0,40 € avec GPT-4o

Classification avec schéma d'énumération

Le constrained decoding est particulièrement efficace pour les tâches de classification : on définit un enum JSON Schema avec les catégories possibles, et le modèle ne peut produire que l'une de ces catégories - jamais une catégorie inventée, jamais une faute de frappe. Pour la classification de tickets support (classification de tickets avec CamemBERT et GPT), c'est un gain immédiat sans fine-tuning.

Extraction d'entités nommées structurées

L'extraction d'entités (personnes, organisations, montants, dates, clauses contractuelles) bénéficie du constrained decoding pour garantir que chaque entité extraite est correctement typée et positionnée dans le JSON. Couplé a un SLM spécialisé via fine-tuning (voir notre article sur le fine-tuning SLM pour le function calling et les agents), c'est l'architecture la plus efficace en cout/performance pour l'extraction documentaire sur des types de documents métier récurrents.

Extraction documentaire sur mesure

Vous avez des documents métier a extraire ? On définit ensemble le bon modèle, le bon schéma et la bonne architecture pour votre volume et vos contraintes.

Réserver un échange

Pour aller plus loin

En résumé : structured output, le prérequis de tout pipeline LLM en production

Le JSON qui casse en production n'est pas une fatalité. Le constrained decoding - via GBNF dans llama.cpp, XGrammar dans vLLM, ou les modes stricts des grandes APIs - élimine le problème a la source avec un overhead de latence quasi nul.

La validation Pydantic en aval complète le dispositif en capturant les erreurs sémantiques que la grammaire ne peut pas anticiper. Ensemble, ces deux couches donnent un pipeline d'extraction documentaire fiable et auditable.

Pour les équipes qui travaillent avec des SLM déployés en local, c'est encore plus précieux : le constrained decoding compense la tendance des petits modèles a produire des structures imparfaites. Un SLM 7B avec XGrammar et un bon schéma Pydantic surpasse souvent un LLM frontier sans enforcement sur les tâches d'extraction structurée répétitives - a une fraction du cout. Voir notre benchmark SLM vs LLM sur tâche métier pour les chiffres concrets.

Passer à l'action

Vous voulez appliquer ça dans votre entreprise ?

En quelques minutes, identifiez les cas d'usage IA les plus rentables pour votre métier. Sans engagement, et sans jargon.

Demander un devis

Articles liés

Outils & Modèles

SLM embarqué : interroger la doc technique aéro sans cloud

SLM embarqué offline pour la documentation technique aéronautique (AMM, IPC, ATA) : architecture RAG local, choix du modèle, matériel atelier, contraintes ITAR/EAR. Guide pratique.

Lire l'article
Outils & Modèles

Optimiser la latence d'un LLM : speculative decoding et vLLM

Réduire la latence et augmenter le débit d'un LLM auto-hébergé : TTFT, continuous batching, PagedAttention, speculative decoding, quantization et choix GPU expliqués.

Lire l'article
Outils & Modèles

Benchmark SLM vs LLM : évaluer un modèle IA sur votre tâche métier

Méthode complète pour benchmarker un SLM contre un LLM sur votre propre tâche métier : jeu de test, métriques, coût, latence, reproductibilité. Arbitrage concret.

Lire l'article
Outils & Modèles

MCP Model Context Protocol : ce que ça change en entreprise

MCP Model Context Protocol en entreprise : architecture client/serveur, primitives tools/resources/prompts, sécurité des données et gouvernance.

Lire l'article
Outils & Modèles

Top SLM 2026 : les meilleurs petits modèles de langage

Comparatif des meilleurs SLM 2026 : Ministral, Phi-4-mini, Qwen2.5, Gemma 3, SmolLM2, Llama 3.2. Tailles, licences, VRAM, cas d'usage et RGPD pour les PME.

Lire l'article
Outils & Modèles

SLM vs LLM : quel modèle d'IA choisir en PME

SLM vs LLM : comparatif décisionnel complet. Coûts, latence, VRAM, souveraineté, cas d'usage. Quand le petit modèle gagne — et quand le LLM reste indispensable.

Lire l'article
Anas Rabhi, ingénieur IA et data scientist, fondateur de Tensoria
Anas Rabhi Ingénieur IA, fondateur de Tensoria ianas.fr

Je suis ingénieur IA et data scientist, fondateur de Tensoria. Depuis plus de 6 ans, j'accompagne les entreprises dans l'exploitation concrète de l'IA pour leur métier : assistants internes basés sur RAG, agents IA en production, automatisations sur mesure, traitement intelligent de documents. J'interviens du cadrage initial à la mise en production, sur stacks LLM modernes (Mistral, Claude, GPT) et infrastructures souveraines quand la confidentialité l'exige.