Sortie JSON

Pour brancher un modèle sur du code, il faut une sortie exploitable machine. Le mode JSON force le modèle à ne produire qu'un objet JSON valide.

Activer le mode JSON#

Ajoutez response_format et demandez explicitement du JSON dans le prompt, les deux sont nécessaires :

import json
from openai import OpenAI

client = OpenAI(api_key="zir_ai_...", base_url="https://api.cloudzir.com/v1")

reponse = client.chat.completions.create(
    model="mina-low",
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": (
                "Tu extrais des informations de commandes. Réponds UNIQUEMENT en JSON, "
                'avec ce format exact : {"client": string, "produits": [{"nom": string, '
                '"quantite": number}], "total_da": number}'
            ),
        },
        {
            "role": "user",
            "content": "Bonjour, ici client-demo. Je voudrais 3 claviers et 2 souris, "
                       "j'ai payé 15 400 DA.",
        },
    ],
)

donnees = json.loads(reponse.choices[0].message.content)
print(donnees["client"], donnees["total_da"])

Résultat#

{
  "client": "client-demo",
  "produits": [
    {"nom": "clavier", "quantite": 3},
    {"nom": "souris", "quantite": 2}
  ],
  "total_da": 15400
}
Le prompt reste indispensable

response_format demande au moteur un JSON syntaxiquement valide, pas le schéma que vous attendez. Décrivez toujours la structure exacte dans le message système, avec un exemple.

Bonnes pratiques#

  • Montrez un exemple complet de sortie attendue dans le prompt système : c'est ce qui fiabilise le plus le format.
  • Nommez les champs en anglais ou sans accents : moins d'ambiguïté à la génération.
  • Prévoyez un champ « inconnu » plutôt que laisser le modèle inventer : {"total_da": null} est plus utile qu'un chiffre halluciné.
  • Donnez assez de max_tokens : un JSON tronqué est un JSON invalide (finish_reason: "length" vous le signale).
  • Validez côté code : jamais confiance aveugle.

Valider la structure#

from pydantic import BaseModel, ValidationError

class Produit(BaseModel):
    nom: str
    quantite: int

class Commande(BaseModel):
    client: str
    produits: list[Produit]
    total_da: float | None = None

try:
    commande = Commande.model_validate_json(reponse.choices[0].message.content)
except ValidationError as exc:
    # Une relance en signalant l'erreur suffit presque toujours
    print("Schéma invalide :", exc)

Relance automatique en cas de schéma invalide#

def extraire(client, messages, schema_modele, essais=2):
    for tentative in range(essais + 1):
        r = client.chat.completions.create(
            model="mina-low",
            response_format={"type": "json_object"},
            messages=messages,
        )
        contenu = r.choices[0].message.content
        try:
            return schema_modele.model_validate_json(contenu)
        except Exception as exc:
            if tentative == essais:
                raise
            messages = messages + [
                {"role": "assistant", "content": contenu},
                {"role": "user", "content": f"Ce JSON est invalide : {exc}. Renvoie UNIQUEMENT le JSON corrigé."},
            ]

Alternative : les appels d'outils#

Quand la structure est vraiment critique, un appel d'outil avec un parameters JSON Schema est plus contraignant que le mode JSON : le modèle doit remplir des arguments typés plutôt que rédiger librement un objet.

Limites actuelles#

  • {"type": "json_schema"} (sorties structurées strictes d'OpenAI) n'est pas encore supporté : la requête est refusée en 400. Utilisez json_object + validation, ou un appel d'outil.
  • Le mode JSON s'applique à la réponse finale ; en streaming, le JSON n'est complet qu'à la fin du flux.

Paramètres

Réponse API