Structured Outputs avec les APIs OpenAI-compatible : guide JSON Schema
Guide pratique des JSON Schemas stricts avec Responses et Chat Completions, validation, gestion des échecs et tests de compatibilité.
Structured Outputs permet de demander un JSON conforme à un schéma défini, au lieu de simplement demander au modèle de « répondre en JSON ». Avec une API OpenAI-compatible, le même JSON Schema peut être utilisé via Responses ou Chat Completions, mais les champs d’enveloppe diffèrent et le support doit être vérifié sur le modèle et la route exacts.
Le modèle de production sûr comporte trois couches : utiliser un schéma strict lorsqu’il est pris en charge, parser et valider de nouveau le résultat dans l’application, puis appliquer des règles métier déterministes. La conformité au schéma réduit les erreurs de format ; elle ne garantit pas l’exactitude sémantique ou factuelle.
Structured Outputs n’est pas JSON mode
| Méthode | JSON valide | Schéma imposé | Usage courant |
|---|---|---|---|
| Prompt seul | Non garanti | Non | Prototype tolérant les erreurs de parsing |
| JSON mode | Oui si pris en charge et terminé | Non | JSON flexible validé par l’application |
Structured Outputs avec strict: true |
Oui, avec gestion de fin et refusal | Oui, dans le sous-ensemble supporté | Extraction typée et workflows applicatifs |
Le guide OpenAI Structured Outputs recommande cette méthode plutôt que JSON mode quand le modèle et l’endpoint la prennent en charge. Le format structuré sert à produire un objet de réponse prévisible ; Function Calling demande une action à l’application. Les deux peuvent partager un Schema, mais pas le contrat de Call IDs et de résultats.
Définir le schéma avant de choisir l’endpoint
Une classification de tickets nécessite quatre champs :
{
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
}
Rendre toutes les propriétés required et utiliser additionalProperties: false stabilise l’objet consommé en aval. Cela ne rend pas tous les keywords JSON Schema portables. OpenAI documente un sous-ensemble ; un autre fournisseur peut en prendre en charge un autre, ou ne pas proposer de Strict Schema Mode.
Envoyer le schéma avec Responses API
Responses place le schéma sous text.format :
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"input": "The customer was charged twice and wants a refund.",
"text": {
"format": {
"type": "json_schema",
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
Envoyez ce body à POST https://modelflare.dev/v1/responses avec Authorization: Bearer <YOUR_API_KEY>. Le contenu généré se trouve dans des output items typés. Un SDK peut proposer un helper de texte ou parsed output ; un client HTTP doit néanmoins localiser le texte terminé et le parser explicitement.
Envoyer le même schéma avec Chat Completions
Chat Completions utilise response_format.json_schema :
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"messages": [
{ "role": "user", "content": "The customer was charged twice and wants a refund." }
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
Le JSON se trouve généralement sous forme de string dans choices[0].message.content et doit être parsé.
| Objectif | Responses API | Chat Completions |
|---|---|---|
| Conteneur | text.format |
response_format.json_schema |
| Type | text.format.type |
response_format.type |
| Nom | text.format.name |
response_format.json_schema.name |
| Schéma | text.format.schema |
response_format.json_schema.schema |
| Strict | text.format.strict |
response_format.json_schema.strict |
| Résultat | Output items / helper | choices[0].message.content |
La couche de compatibilité Modelflare convertit les deux wrappers sur les routes OpenAI/Codex prises en charge. Elle ne crée toutefois pas la fonction Structured Outputs dans un modèle upstream qui en est dépourvu. Pour les familles en Raw Chat Completions Pass-through, le contrat du fournisseur reste la source de vérité.
Valider séparément structure et sens
{
"category": "billing",
"priority": 2,
"requires_human": true,
"summary": "Customer reports a duplicate charge and requests a refund."
}
Validation structurelle
Un validateur JSON Schema confirme propriétés required, absence de propriétés inattendues, types, enum et plages. Même avec Strict Adherence, cette validation protège contre route non supportée, erreur d’intégration, truncation et évolution future du contrat.
Validation sémantique et métier
Le schéma ne sait pas si le client a réellement été débité deux fois, si priority: 2 est correct ou qui peut autoriser un remboursement. Traitez l’objet comme une classification proposée, comparez-le aux données de référence et imposez permissions et limites financières dans du code déterministe. Un objet valide ne doit jamais contourner autorisation, billing ou sécurité.
Gérer les sorties incomplètes ou exceptionnelles
Refusals
Vérifiez Status et représentation du refusal avant de rechercher le JSON. Un refus de sécurité n’est pas une erreur de parsing.
Truncation et limites
Si la génération s’arrête avant la fin de l’objet, le schéma ne peut pas reconstituer le suffixe. Contrôlez Completion Status et Stop Reason et configurez un Output Limit suffisant mais borné.
Keywords non pris en charge
Commencez avec Objects, Arrays, Primitive Types, Enums, Required et Additional Properties explicites. Vérifiez la documentation avant References, récursion, Unions complexes ou validations avancées.
Latence du premier schéma
Certains fournisseurs prétraitent et mettent en cache un nouveau schéma. Réutilisez des schémas stables et versionnés et mesurez séparément First Use et Steady State.
Modèle ou route incompatible
Un endpoint peut accepter Chat Completions, refuser json_schema ou ignorer strict. Un 200 contenant du JSON ne prouve pas Strict Adherence. Testez entrées invalides et cas limites.
Exécuter une matrice de compatibilité
| Test | Preuve à conserver |
|---|---|
| Objet required minimal | Status, modèle, route, objet parsé et validation |
| Chaque valeur enum | Production et parsing de chaque valeur |
| Information absente | Valeur de repli bornée ou clarification |
| Entrée de sécurité | Représentation du refusal et traitement |
| Output Limit faible | Completion Status et truncation |
| Fonction non supportée | Erreur explicite ou contrainte ignorée |
| Wrappers Responses et Chat | Objets applicatifs équivalents |
| Schéma stable répété | First Use face à Steady State |
Conservez Schema, Prompt, Region, Streaming Mode et Output Limit constants lors d’une comparaison et notez la date du test.
Choisir l’endpoint après avoir testé le workflow
Responses convient aux output items typés, aux streaming events Responses et aux workflows Tool plus larges. Chat Completions convient à une intégration stable par messages lorsque response_format est pris en charge sur la route. Voir Responses API vs Chat Completions.
Dans tous les cas :
- conservez une Source of Truth versionnée pour le JSON Schema ;
- changez uniquement le wrapper, pas le sens ;
- validez la réponse terminée ;
- appliquez ensuite les règles métier déterministes ;
- surveillez séparément parsing, refusal, truncation et compatibilité.
Pour la migration, utilisez le guide OpenAI-Compatible API ; pour les streams incomplets, le guide AI API Streaming. Consultez Models & Pricing, puis testez Structured Outputs sur la route exacte de l’API Key.
Structured Outputs transforme la réponse en interface applicative plus fiable, mais ne remplace ni vérification factuelle, ni autorisation, ni logique de facturation. « OpenAI-compatible » reste une affirmation à tester fonction par fonction.