Aller au contenu principal

Structured Output : JSON garanti

Mis à jour le 29 juillet 2026

Structured Output : JSON garanti

Le Structured Output est l’une des fonctionnalités les plus puissantes de la Responses API. Au lieu d’espérer que le modèle retourne du JSON valide, vous le garantissez en fournissant un schéma : le modèle est alors contraint structurellement de le respecter. C’est ce qui transforme un modèle de langage en composant fiable d’une chaîne de traitement automatisée.

Le problème que cela résout

Demander du JSON dans le prompt fonctionne la plupart du temps, et c’est précisément ce qui rend l’approche dangereuse. Un jour sur cent, le modèle préface sa réponse d’une phrase d’introduction, entoure le tout d’un bloc de code Markdown ou renomme un champ — et votre json.loads() lève une exception en production, sur une requête que vous ne pourrez pas reproduire.

from openai import OpenAI
client = OpenAI()

# Sans structure — le modèle peut retourner n'importe quel format
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Donnez-moi les infos sur Paris en JSON."
)
print(response.output_text)
# Résultat imprévisible :
# Parfois du JSON valide, parfois du texte avec du JSON,
# parfois un format inattendu...

Le paramètre text change la nature de la garantie. Vous y déclarez un format json_schema accompagné du schéma attendu, et la sortie s’y conforme : la désérialisation devient une opération sûre.

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Donnez-moi les informations sur Paris.",
    text={
        "format": {
            "type": "json_schema",
            "name": "ville_info",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "nom": {"type": "string"},
                    "pays": {"type": "string"},
                    "population": {"type": "integer"},
                    "langue_officielle": {"type": "string"},
                    "monuments": {
                        "type": "array",
                        "items": {"type": "string"}
                    }
                },
                "required": ["nom", "pays", "population", "langue_officielle", "monuments"],
                "additionalProperties": False
            }
        }
    }
)

import json
data = json.loads(response.output_text)
print(f"Ville : {data['nom']}")
print(f"Population : {data['population']:,}")
print(f"Monuments : {', '.join(data['monuments'])}")

# Résultat GARANTI en JSON valide :
# Ville : Paris
# Population : 2,161,000
# Monuments : Tour Eiffel, Louvre, Notre-Dame, Arc de Triomphe, Sacre-Coeur

Pydantic plutôt que du JSON Schema à la main

Écrire le schéma à la main devient vite pénible et sujet aux fautes de frappe. Pydantic vous permet de le décrire sous forme de classe Python, d’en générer le JSON Schema avec model_json_schema(), puis de valider et typer la réponse avec model_validate_json(). Vous obtenez au bout de la chaîne un objet Python complet, avec l’autocomplétion de votre éditeur, plutôt qu’un dictionnaire anonyme.

from pydantic import BaseModel
from typing import Optional
import json

class Produit(BaseModel):
    nom: str
    prix: float
    categorie: str
    en_stock: bool
    description: Optional[str] = None

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Décrivez un ordinateur portable gaming haut de gamme.",
    text={
        "format": {
            "type": "json_schema",
            "name": "produit",
            "strict": True,
            "schema": Produit.model_json_schema()
        }
    }
)

# Parser et valider avec Pydantic
produit = Produit.model_validate_json(response.output_text)
print(f"Produit : {produit.nom}")
print(f"Prix : {produit.prix} EUR")
print(f"En stock : {'Oui' if produit.en_stock else 'Non'}")

# Résultat :
# Produit : ASUS ROG Strix G18
# Prix : 2499.99 EUR
# En stock : Oui

Rien ne vous limite aux structures plates. En composant les classes, vous décrivez des objets imbriqués et des listes d’objets, ce dont vous aurez besoin dès la première fiche client un peu réaliste : une entreprise contient des contacts, chaque contact possède une adresse.

from pydantic import BaseModel

class Adresse(BaseModel):
    rue: str
    ville: str
    code_postal: str
    pays: str

class Contact(BaseModel):
    nom: str
    prenom: str
    email: str
    telephone: str
    adresse: Adresse

class Entreprise(BaseModel):
    nom: str
    secteur: str
    employes: int
    contacts: list[Contact]

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Générez une fiche entreprise fictive dans le secteur tech "
          "avec 2 contacts.",
    text={
        "format": {
            "type": "json_schema",
            "name": "entreprise",
            "strict": True,
            "schema": Entreprise.model_json_schema()
        }
    }
)

entreprise = Entreprise.model_validate_json(response.output_text)
print(f"Entreprise : {entreprise.nom} ({entreprise.secteur})")
for c in entreprise.contacts:
    print(f"  - {c.prenom} {c.nom} : {c.email}")

Deux usages qui reviennent partout

Le premier est l’extraction : transformer un texte rédigé en enregistrement exploitable. L’annonce d’une conférence, écrite en français courant, devient une ligne de base de données avec un titre, une date, un lieu et un nombre de participants — sans expression régulière, et sans casser dès que la formulation change.

class Evenement(BaseModel):
    titre: str
    date: str
    lieu: str
    participants: int

texte = """
La conférence PyCon France 2026 se tiendra les 15 et 16 novembre
au Palais des Congrès de Lyon. Plus de 800 développeurs Python
sont attendus pour cette édition.
"""

response = client.responses.create(
    model="gpt-5.6-terra",
    input=f"Extrayez les informations de cet événement :\n{texte}",
    text={
        "format": {
            "type": "json_schema",
            "name": "evenement",
            "strict": True,
            "schema": Evenement.model_json_schema()
        }
    }
)

evt = Evenement.model_validate_json(response.output_text)
print(f"{evt.titre} - {evt.date} a {evt.lieu} ({evt.participants} participants)")
# Résultat : PyCon France 2026 - 15-16 novembre 2026 a Lyon (800 participants)

Le second est la classification. En déclarant une énumération, vous restreignez le modèle à un vocabulaire fermé : il ne pourra pas répondre « plutôt positif » ni « positive » là où votre code attend positif. C’est la différence entre une valeur exploitable directement et une valeur à normaliser après coup.

from enum import Enum

class Sentiment(str, Enum):
    positif = "positif"
    negatif = "negatif"
    neutre = "neutre"

class Analyse(BaseModel):
    sentiment: Sentiment
    confiance: float
    mots_cles: list[str]

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Analysez ce commentaire : 'Super service, livraison rapide !'",
    text={
        "format": {
            "type": "json_schema",
            "name": "analyse_sentiment",
            "strict": True,
            "schema": Analyse.model_json_schema()
        }
    }
)

analyse = Analyse.model_validate_json(response.output_text)
print(f"Sentiment : {analyse.sentiment.value} ({analyse.confiance:.0%})")
# Résultat : Sentiment : positif (95%)

Ce que le mode strict exige

La garantie a une contrepartie : quand strict: True, le schéma doit respecter un sous-ensemble précis de JSON Schema. Ces contraintes sont la première source d’erreurs 400 chez les débutants, autant les connaître avant de les rencontrer :

  • Toutes les propriétés doivent être dans required
  • additionalProperties doit être False
  • Les types supportés : string, number, integer, boolean, array, object, null
  • Pas de oneOf, anyOf avec des types mixtes
  • Utilisez Optional[str] qui se traduit par {"anyOf": [{"type": "string"}, {"type": "null"}]}

La règle sur required surprend souvent : un champ facultatif ne s’exprime pas en le retirant de la liste, mais en autorisant la valeur null, exactement ce que produit Optional[str] côté Pydantic.

Points clés à retenir

  • Le Structured Output garantit que la réponse respecte votre schéma JSON
  • Utilisez text={"format": {"type": "json_schema", ...}} dans la Responses API
  • Pydantic est l’outil idéal pour définir et valider les schémas
  • Activez strict: True pour la conformité garantie du schéma
  • Idéal pour l’extraction de données, la classification et la génération structurée