Menu

Output strutturato: JSON affidabile da un LLM

L'output strutturato consiste nel chiedere al modello una risposta con una forma fissa, come JSON, una tabella o un modello, così che un programma o una persona possa usarla senza rimaneggiarla. Dai lo schema, di' cosa fare con i valori mancanti e valida il risultato nel codice.

Ogni prompt qui sotto è modificabile: cambialo, poi aprilo in ChatGPT, Claude o un'altra app di AI.

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.

Estrai i dettagli principali da questa segnalazione di bug: "Dall'aggiornamento di ieri l'app si chiude quando tocco Esporta su un progetto con più di 50 foto. I progetti più piccoli si esportano senza problemi. Uso Android 14, versione dell'app 3.2.0. Mi sta bloccando una consegna a un cliente."
Try it
Example replyReplies vary between models and runs.

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.

Estrarre i dati di contatto in JSON
Fill in
Parts
Estrai i dati di contatto dalla firma email qui sotto.
Restituisci un oggetto JSON con esattamente queste chiavi: { "name": "string", "job_title": "string o null", "company": "string o null", "email": "string o null", "phone": "string o null" }
Usa null per qualsiasi valore che non è nel testo. Non indovinare e non completare valori parziali. Restituisci solo il JSON.
Priya Nair | Responsabile Dati, Northwind Labs | priya.nair@northwind.example
Try it
Example replyReplies vary between models and runs.
{
  "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.

Tabella
Confronta liste, tuple e set di Python in una tabella markdown con queste colonne: Tipo, Ordinato, Mutabile, Ammette duplicati, Uso tipico. Una riga per tipo. Nessun testo fuori dalla tabella.
Try it
Example replyReplies vary between models and runs.
TipoOrdinatoMutabileAmmette duplicatiUso tipico
listSìSìSìUna sequenza a cui aggiungi, togli o ordini elementi
tupleSìNoSìUn gruppo fisso di valori, come delle coordinate
setNoSìNoEliminare 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.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA