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"])curl https://api.cloudzir.com/v1/chat/completions \
-H "Authorization: Bearer $CLOUDZIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mina-low",
"response_format": {"type": "json_object"},
"messages": [
{"role": "system", "content": "Réponds UNIQUEMENT en JSON : {\"ville\": string, \"pays\": string}"},
{"role": "user", "content": "Où se trouve Tizi Ouzou ?"}
]
}'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)import { z } from "zod";
const Commande = z.object({
client: z.string(),
produits: z.array(z.object({ nom: z.string(), quantite: z.number().int() })),
total_da: z.number().nullable(),
});
const parsed = Commande.safeParse(JSON.parse(reponse.choices[0].message.content));
if (!parsed.success) {
console.error("Schéma invalide :", parsed.error.issues);
}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 en400. Utilisezjson_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.