Structured Outputs
Gli output strutturati vincolano la risposta di un modello di IA a uno schema, come JSON Schema, durante la decodifica, così le applicazioni possono analizzare ogni risposta in modo affidabile.
Gli output strutturati sono risposte del modello vincolate a un formato predefinito, di solito uno Schema JSON che specifica le chiavi obbligatorie, i relativi tipi e i valori ammessi, così che il software possa analizzare ogni risposta senza ulteriori correzioni. Se richiesto con cortesia, un modello linguistico di grandi dimensioni (LLM) in genere produce JSON, ma può comunque aggiungere una frase in prosa, scrivere male una chiave o interrompersi a metà di un oggetto, e ognuno di questi casi interrompe il programma che legge la risposta. Gli output strutturati eliminano questo rischio grazie alla decodifica vincolata: a ogni passaggio di generazione, il motore di inferenza consente solo i token che mantengono l'output valido rispetto allo schema. Quando OpenAI ha aggiunto questa funzionalità alla propria API nell'agosto 2024, ha riferito che gpt-4o-2024-08-06 con gli output strutturati aveva ottenuto il 100% nella valutazione della conformità a schemi JSON complessi, contro meno del 40% per GPT-4 (gpt-4-0613) senza questa funzionalità.
Modalità JSON e output strutturati#
Le API dei provider offrono due livelli di garanzia. La modalità JSON, introdotta da OpenAI nel novembre 2023, garantisce solo che la risposta sia sintatticamente un JSON valido, perciò le chiavi possono comunque mancare o avere nomi errati. Gli output strutturati garantiscono invece che la risposta corrisponda allo schema fornito. Nella Structured Outputs API di OpenAI, gli sviluppatori definiscono lo schema come modello Pydantic in Python o oggetto Zod in JavaScript, mentre un rifiuto viene restituito in un campo separato anziché interrompere il formato. Gli output strutturati di Claude di Anthropic compilano ogni schema in una grammatica, con un aumento della latenza al primo utilizzo dello schema, e memorizzano in cache la grammatica compilata per 24 ore. Entrambi i provider supportano solo un sottoinsieme di JSON Schema; per esempio, quello di Anthropic non supporta schemi ricorsivi né limiti numerici come minimum.
Come funziona la decodifica vincolata#
A ogni passaggio, un LLM produce un punteggio, o logit, per ciascun token del proprio vocabolario e la decodifica seleziona un token dalla distribuzione softmax risultante. La decodifica vincolata assegna prima un valore meno infinito al punteggio di ogni token che comprometterebbe il formato, impedendone così la selezione. Brandon Willard e Rémi Louf hanno mostrato come eseguire questa operazione in modo efficiente in Efficient Guided Generation for Large Language Models: un'espressione regolare o uno Schema JSON viene compilato in un automa a stati finiti e un indice, creato una sola volta sul vocabolario, elenca i token validi per ogni stato, così che a ogni passaggio basti una consultazione. La libreria open source Outlines implementa il metodo e motori di serving come vLLM offrono la generazione guidata da grammatiche per i modelli aperti.
L'esempio seguente applica manualmente il metodo a GPT-2 di Hugging Face Transformers. Il testo fisso dello schema, comprese parentesi graffe, chiavi e virgolette, viene scritto dal codice e, per ogni valore, il modello può scegliere solo tra i token consentiti dallo schema:
import json
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")
prompt = "Review: The battery died after two days and support never replied.\nJSON:"
# Decodifica greedy non vincolata: niente impone che la risposta rispetti uno schema
ids = tokenizer(prompt, return_tensors="pt").input_ids
free = model.generate(ids, max_new_tokens=16, do_sample=False, pad_token_id=tokenizer.eos_token_id)
print("free:", repr(tokenizer.decode(free[0, ids.shape[1] :])))
def choose(text, options):
"""Return the option GPT-2 scores highest after text, with every other token masked out."""
logits = model(tokenizer(text, return_tensors="pt").input_ids).logits[0, -1]
allowed = torch.tensor([tokenizer.encode(option)[0] for option in options])
masked = torch.full_like(logits, float("-inf"))
masked[allowed] = logits[allowed]
return tokenizer.decode(masked.argmax())
# Decodifica vincolata: lo schema scrive le chiavi e il modello può scegliere solo valori consentiti
with torch.no_grad():
text = prompt + ' {"sentiment": "'
text += choose(text, ["positive", "negative"]) + '", "stars": '
text += choose(text, ["1", "2", "3", "4", "5"]) + "}"
print("constrained:", json.loads(text[len(prompt) :]))Stampa free: ' The battery died after two days and support never replied.\n\nJSON: The' e constrained: {'sentiment': 'positive', 'stars': 1}. Se lasciato a sé, GPT-2 ripete la recensione invece di scrivere JSON, mentre la versione vincolata è sempre analizzabile. Il risultato analizzato mostra anche il limite della tecnica: il modello da 124 milioni di parametri ha giudicato positivamente, con una stella, una batteria scarica e un'assistenza silenziosa. Lo schema ha fissato il formato, ma i valori al suo interno derivavano comunque dal giudizio del modello.
Usi e compromessi#
- Estrazione dei dati: Estrarre nomi, date, totali e voci dettagliate da fatture, contratti o email, inserendoli direttamente nelle righe di un database.
- Chiamata di funzioni: Una chiamata a uno strumento è un output strutturato il cui schema corrisponde ai parametri dello strumento. L'uso rigoroso degli strumenti nelle API di OpenAI e Anthropic applica la stessa garanzia agli argomenti degli strumenti.
- Flussi di lavoro agentici: Decisioni di instradamento rappresentate da valori enum e risultati intermedi passati tra i passaggi di una catena di prompt come oggetti tipizzati.
- Modelli visione-linguaggio: Rilevamenti restituiti come elenchi di etichette e coordinate delle riquadri, direttamente utilizzabili dal codice di disegno o valutazione.
L'annotazione intelligente basata sulle classi in Ultralytics Platform si basa su quest'ultimo tipo di output. Chiede ai modelli di visione di provider come OpenAI, Anthropic e Google di rilevare le classi di un dataset e aggiunge ogni oggetto restituito come riquadro di delimitazione modificabile; ogni risposta deve quindi indicare una classe e fornire coordinate in un formato analizzabile dal codice. Questi modelli non restituiscono punteggi di confidenza, quindi Platform nasconde le impostazioni di confidenza e IoU quando uno di essi è selezionato. La classifica VLM confronta la precisione con cui questi modelli rilevano gli oggetti nei dataset del mondo reale, ordinando i riquadri di ciascun modello in base all'ordine in cui vengono elencati.
I formati rigorosi possono ridurre l'accuratezza nei compiti di ragionamento. In Let Me Speak Freely?, Zhi Rui Tam e colleghi hanno riscontrato un calo significativo nel ragionamento degli LLM in presenza di restrizioni di formato, con riduzioni maggiori al crescere della rigidità dei vincoli. L'API di OpenAI genera le chiavi nell'ordine indicato dallo schema, quindi una soluzione comune è inserire un campo di ragionamento in testo libero prima del campo della risposta, così che il modello scriva la propria catena di pensiero prima di formulare la risposta vincolata.










