Menu

Sortie structurée : obtenir un JSON fiable d'un LLM

La sortie structurée consiste à demander au modèle une réponse de forme fixe, comme du JSON, un tableau ou un modèle, pour qu'un programme ou une personne puisse l'utiliser sans la remanier. Donnez le schéma, dites quoi faire des valeurs manquantes et validez le résultat dans le code.

Chaque prompt de cette page se modifie, puis s'ouvre dans ChatGPT, Claude ou une autre application d'IA.

Une sortie structurée est une réponse dont vous avez décidé la forme à l'avance : un objet JSON avec des clés nommées, un tableau aux colonnes fixes, ou un modèle avec les mêmes titres à chaque fois. Il vous en faut une dès qu'un programme lit la réponse, et elle aide aussi quand c'est une personne qui lit, car toutes les réponses se ressemblent et sont faciles à parcourir ou à comparer.

L'astuce consiste à décrire la forme si précisément que le modèle n'a plus rien à choisir. Le mot "JSON" seul n'est pas une description. Le bloc ci-dessous extrait des informations d'un rapport de bug. Comparez une demande des "informations clés" avec une demande qui détaille le schéma.

Extrais les informations clés de ce rapport de bug : "Depuis la mise à jour d'hier, l'application plante quand je touche Exporter sur un projet de plus de 50 photos. Les projets plus petits s'exportent sans problème. Je suis sous Android 14, version 3.2.0 de l'appli. Ça bloque ma livraison client."
Try it
Example replyReplies vary between models and runs.

Voici les informations clés du rapport de bug :

  • Problème : l'application plante lors de l'export de projets
  • Déclencheur : toucher Exporter sur des projets de plus de 50 photos
  • Depuis : la dernière mise à jour
  • Plateforme : Android 14
  • Version de l'appli : 3.2.0
  • Impact : élevé, puisque cela bloque une livraison client

Les projets plus petits semblent s'exporter sans problème.

La réponse en texte libre est exacte et lisible, mais deux exécutions n'utiliseront jamais les mêmes libellés, la gravité est une phrase plutôt qu'une valeur, et un programme devrait deviner où commence chaque champ. La réponse JSON peut aller directement dans un outil de suivi des bugs. Notez que la réponse est quand même arrivée dans un bloc de code : les applications de chat entourent en général le JSON ainsi, ce qui compte quand vous l'analysez dans le code.

Comment demander du JSON

Une bonne demande de JSON répond à toutes les questions que le modèle trancherait sinon à votre place :

  • Chaque clé, écrite exactement. Écrivez les noms de clés entre guillemets tels qu'ils doivent apparaître. Dites "exactement ces clés" pour que le modèle n'en ajoute pas.
  • Le type de chaque valeur. Chaîne, nombre, booléen, tableau de chaînes, objet imbriqué.
  • Les valeurs autorisées pour tout champ à ensemble fixe, comme la gravité ou la catégorie. Sans la liste, vous obtenez "High", "high", "severe" et "P1" sur quatre exécutions.
  • Quoi faire quand l'entrée ne contient pas une valeur. Si vous ne dites pas "null si non précisé", le modèle a tendance à combler le trou par une supposition plausible, et une version d'appli devinée ressemble exactement à une vraie.
  • Rien autour. "Renvoie uniquement le JSON, sans texte avant ni après" supprime la phrase d'introduction aimable.

Quand la forme est imbriquée ou inhabituelle, montrer un objet d'exemple complet marche mieux que de la décrire. C'est le prompting few-shot appliqué au format. Gardez des valeurs d'exemple nettement différentes de la vraie entrée pour que le modèle ne les recopie pas.

Un prompt d'extraction réutilisable

Ce bloc reprend la même demande découpée en parties. Désactivez la partie format et le modèle renvoie quand même du JSON, puisque les contraintes le demandent, mais il choisit ses propres noms de clés, comme title au lieu de job_title, et le code qui attend vos clés casse. La partie contraintes est ce qui l'empêche de compléter un numéro de téléphone à moitié écrit ou de déduire une entreprise d'un domaine d'e-mail. Collez une vraie signature dans le champ d'entrée pour le tester.

Extraire des coordonnées en JSON
Fill in
Parts
Extrais les coordonnées de la signature d'e-mail ci-dessous.
Renvoie un objet JSON avec exactement ces clés : { "name": "string", "job_title": "string ou null", "company": "string ou null", "email": "string ou null", "phone": "string ou null" }
Utilise null pour toute valeur absente du texte. Ne devine pas et ne complète pas les valeurs partielles. Renvoie uniquement le JSON.
Priya Nair | Responsable des données, Northwind Labs | priya.nair@northwind.example
Try it
Example replyReplies vary between models and runs.
{
  "name": "Priya Nair",
  "job_title": "Responsable des données",
  "company": "Northwind Labs",
  "email": "priya.nair@northwind.example",
  "phone": null
}

Tableaux et modèles fixes

La sortie structurée ne sert pas qu'aux programmes. Quand vous lisez vous-même la réponse, un tableau markdown ou un modèle fixe apporte le même avantage : vous savez où se trouvera chaque information avant de regarder.

Tableau
Compare les listes, tuples et ensembles (sets) de Python dans un tableau markdown avec ces colonnes : Type, Ordonné, Modifiable, Doublons autorisés, Usage typique. Une ligne par type. Aucun texte en dehors du tableau.
Try it
Example replyReplies vary between models and runs.
TypeOrdonnéModifiableDoublons autorisésUsage typique
listOuiOuiOuiUne séquence à laquelle on ajoute, retire ou que l'on trie
tupleOuiNonOuiUn groupe fixe de valeurs, comme des coordonnées
setNonOuiNonSupprimer les doublons et tester rapidement l'appartenance

Un modèle fonctionne de la même façon pour un texte plus long : donnez les titres dans l'ordre et dites ce qui va sous chacun. "Réponds avec trois intitulés en gras : Cause, Correction, Comment vérifier" produit les trois mêmes intitulés à chaque fois, ce qui rend une série de réponses facile à comparer.

Le mode JSON dans l'API

Plusieurs API de modèles ont un réglage qui impose un JSON syntaxiquement valide. Dans le SDK Python d'OpenAI, c'est response_format. Le mode JSON exige que le mot "JSON" figure quelque part dans vos messages, c'est pourquoi le prompt système ci-dessous le nomme et liste les clés.

import json
from openai import OpenAI

client = OpenAI()
MODEL = "your-model-id"  # e.g. from your provider's model list

report_text = "Since yesterday's update the app crashes when I tap Export..."

response = client.chat.completions.create(
    model=MODEL,
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": (
                "Extract the bug report into JSON with the keys "
                "summary (string), severity (one of low, medium, high, critical) "
                "and steps_to_reproduce (array of strings)."
            ),
        },
        {"role": "user", "content": report_text},
    ],
)

data = json.loads(response.choices[0].message.content)

Le mode JSON garantit que le texte s'analyse (sauf si la réponse est coupée à la limite de tokens), pas qu'il respecte votre schéma. Une clé peut encore manquer, ou une gravité valoir "urgent". Plusieurs fournisseurs acceptent aussi un JSON Schema complet, comme format de sortie ou comme schéma d'entrée d'une définition d'outil (fonction), et certains de ces modes contraignent la réponse au schéma. Consultez la documentation de votre fournisseur pour le paramètre exact, car ces fonctions diffèrent d'une API à l'autre.

Validez le résultat dans le code

Traitez le JSON du modèle comme n'importe quelle entrée venue de l'extérieur de votre programme : analysez-le, puis vérifiez-le. L'analyse attrape une syntaxe cassée. La vérification attrape un objet valide au contenu erroné.

ALLOWED_SEVERITIES = {"low", "medium", "high", "critical"}

def problems(data):
    if not isinstance(data, dict):
        return ["the answer must be a JSON object"]
    found = []
    if not isinstance(data.get("summary"), str):
        found.append("summary must be a string")
    if data.get("severity") not in ALLOWED_SEVERITIES:
        found.append("severity must be low, medium, high or critical")
    steps = data.get("steps_to_reproduce")
    if not isinstance(steps, list) or not all(isinstance(s, str) for s in steps):
        found.append("steps_to_reproduce must be an array of strings")
    return found

Quand la vérification échoue, une seule relance suffit souvent : renvoyez au modèle sa propre sortie avec la liste des problèmes et demandez un JSON corrigé. Limitez le nombre de relances et journalisez les échecs, car un champ qui échoue souvent signale un prompt peu clair. Dans les projets plus importants, une bibliothèque de validation comme Pydantic ou un validateur JSON Schema remplace la fonction écrite à la main.

Deux autres habitudes évitent les erreurs silencieuses. Réglez la limite de tokens de sortie assez haut pour la plus grande réponse attendue, car un objet tronqué ne s'analyse jamais. Et quand l'entrée est longue ou vient des utilisateurs, séparez-la de vos consignes avec des délimiteurs ou des balises XML pour que le texte qu'elle contient ait moins de chances d'être lu comme une consigne. Si un même prompt doit à la fois raisonner et produire du JSON, envisagez le chaînage de prompts : une étape réfléchit en texte libre, une seconde transforme le résultat en structure.

Questions fréquentes

Comment faire en sorte que ChatGPT réponde en JSON ?

Dites que la réponse doit être du JSON, listez chaque clé avec son type et donnez les valeurs autorisées pour tout champ à ensemble fixe. Ajoutez "Renvoie uniquement le JSON, sans texte avant ni après." Dans l'API, activez aussi le mode JSON avec response_format={"type": "json_object"}, qui exige que le mot JSON figure dans vos messages.

Pourquoi le modèle ajoute-t-il du texte autour du JSON ?

Les modèles de chat sont entraînés à converser, ils commencent donc souvent par une phrase du genre "Voici le JSON" ou entourent l'objet d'un bloc de code markdown. Demandez le JSON seul, et dans le code, utilisez le mode JSON de l'API ou retirez le bloc de code qui l'entoure avant de l'analyser.

Qu'est-ce que le mode JSON ?

Le mode JSON est un réglage d'API qui oblige le modèle à produire un JSON syntaxiquement valide, tant que la réponse n'est pas coupée par la limite de tokens de sortie. Il n'oblige pas le modèle à suivre votre schéma : des clés peuvent encore manquer, être mal orthographiées ou avoir le mauvais type. Certains fournisseurs proposent aussi un mode plus strict qui prend un JSON Schema complet et y contraint la sortie.

Un LLM peut-il toujours renvoyer un JSON valide ?

Pas avec un prompt seul. Même une consigne claire échoue de temps en temps, et une réponse coupée par la limite de tokens de sortie est toujours invalide. Analysez chaque réponse avec un vrai parseur JSON, vérifiez les champs dont vous avez besoin, et relancez ou échouez explicitement quand la vérification ne passe pas.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER