L'output strutturato è una risposta con una forma che hai deciso in anticipo: un oggetto JSON con chiavi dal nome preciso, una tabella con colonne fisse o un modello con gli stessi titoli ogni volta. Ti serve ogni volta che un programma legge la risposta, e aiuta anche quando la legge una persona, perché tutte le risposte hanno lo stesso aspetto e sono facili da scorrere o confrontare.
Il trucco è descrivere la forma con una precisione tale che al modello non resti niente da scegliere. La parola "JSON" da sola non è una descrizione. Il blocco qui sotto estrae i dettagli da una segnalazione di bug. Confronta una richiesta dei "dettagli principali" con una che specifica lo schema.
Ecco i dettagli principali della segnalazione:
- Problema: l'app si chiude durante l'esportazione dei progetti
- Causa scatenante: toccare Esporta su progetti con più di 50 foto
- Da quando: dopo l'ultimo aggiornamento
- Piattaforma: Android 14
- Versione dell'app: 3.2.0
- Impatto: alto, perché blocca una consegna a un cliente
I progetti più piccoli sembrano esportarsi senza problemi.
La risposta in testo libero è corretta e si legge bene, ma non ci saranno due esecuzioni con le stesse etichette, la gravità è una frase e non un valore, e un programma dovrebbe indovinare dove inizia ogni campo. La risposta JSON può finire direttamente in un bug tracker. Nota che la risposta è arrivata comunque dentro un blocco di codice: le app di chat di solito racchiudono il JSON così, e questo conta quando lo analizzi nel codice.
Come chiedere JSON
Una buona richiesta di JSON risponde a tutte le domande a cui il modello risponderebbe altrimenti al posto tuo:
- Ogni chiave, scritta esattamente. Scrivi i nomi delle chiavi tra virgolette così come devono comparire. Di' "esattamente queste chiavi" così il modello non ne aggiunge altre.
- Il tipo di ogni valore. Stringa, numero, booleano, array di stringhe, oggetto annidato.
- I valori ammessi per ogni campo con un insieme fisso, come la gravità o la categoria. Senza l'elenco, in quattro esecuzioni ottieni "High", "high", "severe" e "P1".
- Cosa fare quando l'input non ha un valore. Se non dici "null se non indicato", il modello tende a riempire il vuoto con un'ipotesi plausibile, e una versione dell'app inventata sembra identica a una vera.
- Niente intorno. "Restituisci solo il JSON, senza testo prima o dopo" elimina la frase di apertura cordiale.
Quando la forma è annidata o insolita, mostrare un oggetto di esempio completo funziona meglio che descriverla. È il few-shot prompting applicato al formato. Usa nell'esempio valori chiaramente diversi da quelli dell'input reale, così il modello non li copia.
Un prompt di estrazione riutilizzabile
Questo blocco contiene la stessa richiesta divisa in parti. Disattiva la parte sul formato e il modello restituisce comunque JSON, perché lo chiedono i vincoli, ma sceglie da solo i nomi delle chiavi, come title invece di job_title, e il codice che si aspetta le tue chiavi si rompe. È la parte dei vincoli a impedirgli di completare un numero di telefono scritto a metà o di dedurre un'azienda dal dominio di un'email. Incolla una firma vera nel campo di input per provarlo.
{
"name": "string",
"job_title": "string o null",
"company": "string o null",
"email": "string o null",
"phone": "string o null"
}{
"name": "Priya Nair",
"job_title": "Responsabile Dati",
"company": "Northwind Labs",
"email": "priya.nair@northwind.example",
"phone": null
}
Tabelle e modelli fissi
L'output strutturato non serve solo ai programmi. Quando leggi tu la risposta, una tabella markdown o un modello fisso ti danno lo stesso vantaggio: sai dove si troverà ogni informazione prima ancora di guardare.
| Tipo | Ordinato | Mutabile | Ammette duplicati | Uso tipico |
|---|---|---|---|---|
| list | Sì | Sì | Sì | Una sequenza a cui aggiungi, togli o ordini elementi |
| tuple | Sì | No | Sì | Un gruppo fisso di valori, come delle coordinate |
| set | No | Sì | No | Eliminare duplicati e verificare velocemente l'appartenenza |
Un modello funziona allo stesso modo per testi più lunghi: dai i titoli in ordine e di' cosa va sotto ciascuno. "Rispondi con tre etichette in grassetto: Causa, Soluzione, Come verificare" produce le stesse tre etichette ogni volta, e questo rende facile confrontare un gruppo di risposte.
La modalità JSON nell'API
Diverse API di modelli hanno un'impostazione che forza JSON sintatticamente valido. Nell'SDK Python di OpenAI è response_format. La modalità JSON richiede che la parola "JSON" compaia da qualche parte nei tuoi messaggi, quindi il prompt di sistema qui sotto la nomina ed elenca le chiavi.
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)
La modalità JSON garantisce che il testo si possa analizzare (a meno che la risposta non venga troncata al limite di token), non che corrisponda al tuo schema. Una chiave può ancora mancare o una gravità può ancora essere "urgent". Diversi provider accettano anche un JSON Schema completo, come formato di output o come schema di input nella definizione di uno strumento (funzione), e alcune di queste modalità vincolano la risposta allo schema. Controlla nella documentazione del tuo provider il parametro esatto, perché queste funzionalità cambiano da un'API all'altra.
Valida il risultato nel codice
Tratta il JSON del modello come tratti qualsiasi input che arriva da fuori dal tuo programma: fai il parsing, poi controllalo. Il parsing intercetta la sintassi rotta. Il controllo intercetta un oggetto valido con il contenuto sbagliato.
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
Quando il controllo fallisce, spesso basta un solo nuovo tentativo: invia al modello il suo stesso output insieme all'elenco dei problemi e chiedi il JSON corretto. Limita il numero di tentativi e registra i fallimenti, perché un campo che fallisce spesso è segno che il prompt non è chiaro. Nei progetti più grandi, una libreria di validazione come Pydantic o un validatore JSON Schema sostituisce la funzione scritta a mano.
Altre due abitudini evitano errori silenziosi. Imposta il limite di token di output abbastanza alto per la risposta più lunga che ti aspetti, perché un oggetto troncato non si può mai analizzare. E quando l'input è lungo o arriva dagli utenti, separalo dalle tue istruzioni con delimitatori o tag XML, così è meno probabile che il testo al suo interno venga letto come un'istruzione. Se un solo prompt deve sia ragionare sia produrre JSON, valuta il prompt chaining: lascia che un passaggio ragioni in testo libero e che un secondo passaggio trasformi il risultato nella struttura.
Domande frequenti
Come faccio a far restituire JSON a ChatGPT?
Di' che la risposta deve essere JSON, elenca ogni chiave con il suo tipo e indica i valori ammessi per ogni campo con un insieme fisso. Aggiungi "Restituisci solo il JSON, senza testo prima o dopo". Nell'API attiva anche la modalità JSON con response_format={"type": "json_object"}, che richiede che la parola JSON compaia nei tuoi messaggi.
Perché il modello aggiunge testo intorno al JSON?
I modelli di chat sono addestrati per essere colloquiali, quindi spesso aprono con una frase come "Ecco il JSON" o racchiudono l'oggetto in un blocco di codice markdown. Chiedi solo il JSON, e nel codice usa la modalità JSON dell'API oppure togli il blocco di codice che lo circonda prima di fare il parsing.
Cos'è la modalità JSON?
La modalità JSON (JSON mode) è un'impostazione dell'API che fa produrre al modello JSON sintatticamente valido, purché la risposta non venga troncata dal limite di token di output. Non fa seguire al modello il tuo schema: le chiavi possono ancora mancare, essere scritte male o avere il tipo sbagliato. Alcuni provider offrono anche una modalità più rigorosa che riceve un JSON Schema completo e vincola l'output a quello.
Un LLM può restituire sempre JSON valido?
Non con il solo prompt. Anche un'istruzione chiara ogni tanto fallisce, e una risposta troncata dal limite di token di output è sempre non valida. Fai il parsing di ogni risposta con un vero parser JSON, controlla i campi che ti servono e riprova, oppure fallisci in modo evidente, quando il controllo non passa.