À la fin de ce guide, vous saurez appeler les sorties structurées d’OpenAI depuis votre code : fournir un schéma JSON au modèle, activer strict: true, lire une réponse conforme à la forme attendue, gérer les refus et la troncation, puis générer des collections de tests d’API dans Apidog pour vérifier que la charge utile respecte réellement le contrat.
Ce dont vous avez besoin avant de commencer
Les sorties structurées contraignent la génération du modèle afin que la sortie corresponde à un schéma JSON fourni. Avec strict: true, le modèle doit respecter les clés requises, les types, les énumérations et l’interdiction de champs supplémentaires si votre schéma l’exige.
Préparez :
- une clé API OpenAI exposée dans
OPENAI_API_KEY; - un modèle compatible avec l’application stricte des schémas ;
- un schéma JSON décrivant précisément la réponse attendue ;
- un test de validation côté API pour détecter les dérives.
Les prompts du type « réponds uniquement en JSON » ne suffisent pas pour du code de production. Ils peuvent produire du JSON invalide, ajouter du texte autour de l’objet ou changer un type. Les sorties structurées déplacent ce contrat dans le décodage du modèle.
Choisissez le bon modèle
Les sorties structurées sont disponibles sur les modèles récents d’OpenAI, à partir de la famille GPT-4o et sur la série GPT-5. La documentation d’OpenAI recommande de démarrer les nouveaux projets sur son modèle phare actuel (gpt-5.5 au moment de la rédaction).
Les anciens modèles, notamment ceux de l’ère gpt-3.5, peuvent prendre en charge le mode JSON sans appliquer strictement un schéma. Si votre code dépend de strict: true, vérifiez l’ID exact du modèle avant de déployer.
Deux fonctionnalités sont proches, mais différentes :
| Mode JSON | Sorties structurées strictes | |
|---|---|---|
| Paramètre | response_format: {"type":"json_object"} |
response_format avec type: "json_schema" et strict: true
|
| JSON valide | Oui | Oui |
| Conforme à votre schéma | Non | Oui |
| Champs requis appliqués | Non | Oui |
| Types et énumérations appliqués | Non | Oui |
| Validation aval | Toujours nécessaire | Recommandée |
Avec Chat Completions, vous utilisez response_format. Avec la nouvelle API Responses, le même concept se configure sous text.format avec type: "json_schema". Le schéma reste le même ; seule l’enveloppe de la requête change.
Effectuez votre première requête
Exemple : extraire un ticket de support utilisateur dans un objet typé.
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{ "role": "system", "content": "Extract the ticket into the schema." },
{ "role": "user", "content": "My checkout 500s every time I use a saved card. Started today. Account: acct_8842." }
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "support_ticket",
"strict": true,
"schema": {
"type": "object",
"properties": {
"summary": { "type": "string" },
"category": {
"type": "string",
"enum": ["billing", "bug", "account", "other"]
},
"severity": { "type": "integer" },
"account_id": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
}
},
"required": ["summary", "category", "severity", "account_id"],
"additionalProperties": false
}
}
}
}'
Points importants dans ce schéma :
-
requiredcontient toutes les propriétés ; -
additionalProperties: falsebloque les champs inventés ; -
categoryest limitée à une énumération ; -
account_idest toujours présent, mais peut valoirnull.
Lisez la réponse
Le contenu du message est une chaîne JSON conforme au schéma :
{
"summary": "Checkout returns HTTP 500 when paying with a saved card",
"category": "bug",
"severity": 3,
"account_id": "acct_8842"
}
Côté application, parsez le contenu uniquement après avoir vérifié que la réponse n’est pas un refus.
Exemple en Python :
import json
msg = response.choices[0].message
if msg.refusal:
handle_refusal(msg.refusal)
else:
ticket = json.loads(msg.content)
process_ticket(ticket)
Respectez le sous-ensemble JSON Schema pris en charge
Les sorties structurées acceptent un sous-ensemble du schéma JSON. Restez dans ces règles pour éviter les rejets ou les comportements inattendus.
1. La racine doit être un objet
Ne mettez pas un tableau au niveau racine.
À éviter :
[
{ "id": "ticket_1" }
]
À faire :
{
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" }
},
"required": ["id"],
"additionalProperties": false
}
}
},
"required": ["items"],
"additionalProperties": false
}
2. Toutes les propriétés doivent être requises
Les sorties structurées n’utilisent pas le facultatif classique. Pour modéliser une valeur absente, rendez le champ annulable.
"account_id": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
}
La clé account_id sera toujours présente, mais sa valeur pourra être null.
3. Interdisez les propriétés supplémentaires
Ajoutez additionalProperties: false sur chaque objet.
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["open", "closed"]
}
},
"required": ["status"],
"additionalProperties": false
}
4. Gardez le schéma raisonnable
Des limites s’appliquent : environ 100 propriétés d’objet et jusqu’à 5 niveaux d’imbrication. Si votre schéma devient trop profond, aplatissez-le ou divisez la tâche en plusieurs appels.
5. Ne confondez pas structure et validation métier
Certains mots-clés comme pattern, format, minLength ou minimum ne sont pas garantis par le modèle. Si vous devez valider une expression régulière, un format de date ou une plage numérique, faites-le dans votre code ou dans vos tests.
Si vous utilisez déjà des champs optionnels ou d’union avec oneOf/anyOf/allOf, le principe est le même : le schéma verrouille la forme, mais vos tests valident les règles métier.
Gérez les refus et la troncation
Une sortie peut ne pas respecter votre schéma dans trois cas principaux.
Refus de sécurité
Si le modèle refuse une requête, il peut renvoyer un champ refusal au lieu du contenu conforme au schéma.
msg = response.choices[0].message
if msg.refusal:
handle_refusal(msg.refusal)
else:
ticket = json.loads(msg.content)
Ne cherchez pas un refus en scannant du texte. Branchez votre logique sur le champ prévu.
Troncation
Si la réponse atteint max_tokens au milieu d’un objet, le JSON peut être incomplet. Prévoyez :
- une limite
max_tokenssuffisante ; - une gestion d’erreur au parsing ;
- un retry si la sortie est tronquée.
try:
ticket = json.loads(msg.content)
except json.JSONDecodeError:
retry_request()
Appels d’outils parallèles
Les sorties structurées ne supportent pas les appels d’outils parallèles. Si vous combinez outils et sortie structurée, définissez :
"parallel_tool_calls": false
Testez le contrat dans Apidog
Le mode strict aide à produire une réponse conforme. Il ne remplace pas les tests. Un modèle peut changer, un prompt peut évoluer, un champ peut être retiré du tableau required, ou un chemin de refus peut casser votre intégration.
C’est là qu’Apidog intervient.
La séparation des responsabilités est simple :
- OpenAI applique le schéma au moment de la génération ;
- Apidog valide la réponse reçue par rapport au contrat attendu ;
- votre CI échoue si la charge utile dérive.
Flux de travail recommandé
- Créez la requête OpenAI dans Apidog
Ajoutez l’appel Chat Completions avec le bloc response_format.
{
"type": "json_schema",
"json_schema": {
"name": "support_ticket",
"strict": true,
"schema": {
"type": "object",
"properties": {
"summary": { "type": "string" },
"category": {
"type": "string",
"enum": ["billing", "bug", "account", "other"]
},
"severity": { "type": "integer" },
"account_id": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
}
},
"required": ["summary", "category", "severity", "account_id"],
"additionalProperties": false
}
}
}
- Enregistrez la requête dans une collection
Une collection rend l’appel reproductible pour votre équipe et votre pipeline.
- Ajoutez des assertions
Vérifiez que :
-
categoryappartient à l’énumération ; -
severityest un entier ; -
account_idest une chaîne ounull; - aucun champ inattendu n’est présent.
Apidog peut valider la réponse par rapport au schéma JSON, ce qui permet de réutiliser le même contrat que celui transmis à OpenAI.
- Exécutez la collection en CI
Ajoutez la collection à votre pipeline. Chaque changement de modèle, de prompt ou de schéma relance la vérification.
- Mockez la réponse pour les consommateurs
Avant que l’appel OpenAI soit stable, créez une API mock qui renvoie des exemples conformes au schéma.
Votre frontend, vos tests d’intégration et vos services aval peuvent avancer sans dépendre de l’appel réel ni consommer de tokens.
Vous pouvez télécharger Apidog pour construire la requête, les assertions et la maquette au même endroit.
Questions fréquemment posées
Le mode JSON est-il déprécié ?
Non. Le mode JSON fonctionne toujours et garantit un JSON syntaxiquement valide. Il ne garantit pas la conformité à votre schéma.
Pour du nouveau code, utilisez les sorties structurées avec strict: true lorsque le modèle le supporte.
La racine du schéma peut-elle être un tableau ?
Non. Le niveau supérieur doit être un objet.
Utilisez une propriété comme items :
{
"type": "object",
"properties": {
"items": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["items"],
"additionalProperties": false
}
Comment rendre un champ facultatif ?
Vous ne le rendez pas facultatif au sens JSON Schema classique. Vous le rendez annulable.
{
"type": "object",
"properties": {
"phone": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
}
},
"required": ["phone"],
"additionalProperties": false
}
La clé phone sera toujours présente.
strict: true permet-il d’ignorer toute validation ?
Non. La structure est contrainte, mais certaines règles métier restent à vérifier : formats, regex, bornes numériques, cohérence entre champs, refus et troncation.
Si vous débutez avec ce format, ce manuel d’introduction au schéma JSON couvre les bases.
Quels modèles utiliser ?
Utilisez un modèle qui prend en charge les sorties structurées strictes, comme GPT-4o ou une version ultérieure, y compris la série GPT-5. Vérifiez toujours l’ID exact du modèle dans la documentation OpenAI avant de vous appuyer sur strict: true.
En résumé
Le workflow complet est :
- choisir un modèle compatible avec les sorties structurées ;
- envoyer une requête avec
response_format,json_schemaetstrict: true; - respecter le sous-ensemble JSON Schema pris en charge ;
- gérer explicitement
refusal, la troncation et les appels d’outils ; - valider la réponse dans vos tests ;
- exécuter ces tests en CI ;
- mocker le contrat pour les consommateurs aval.
Construisez la requête dans Apidog, validez la réponse par rapport au schéma, puis simulez l’API pour stabiliser le reste de votre stack. Le modèle promet la forme ; vos tests prouvent qu’elle reste conforme.

Top comments (0)