Executive summary
Quando un sistema software deve elaborare automaticamente la risposta di un modello di linguaggio, un sistema di intelligenza artificiale che genera testo, ha bisogno che quella risposta abbia una struttura prevedibile e verificabile, non testo libero. Questo articolo analizza le tecniche che permettono di vincolare la generazione di testo affinché l'output rispetti uno schema predefinito, esaminando i meccanismi interni, le differenze tra le principali implementazioni commerciali e open-source, e i compromessi che emergono tra la rigidità della struttura e la qualità del contenuto generato. L'analisi mostra che le garanzie strutturali più solide si ottengono intervenendo direttamente nel processo di generazione del testo, ma che questo intervento può ridurre la capacità del modello di ragionare su problemi complessi. La scelta dell'approccio dipende dal tipo di compito: per l'estrazione di dati strutturati l'impatto è trascurabile, mentre per compiti che richiedono ragionamento articolato è preferibile separare la fase di ragionamento dalla fase di strutturazione.
Background
Il problema fondamentale degli structured output nasce dalla natura stessa dei modelli di linguaggio: un LLM genera testo un token alla volta, selezionando a ogni passo il token successivo da una distribuzione di probabilità sull'intero vocabolario. Nulla nel meccanismo di generazione garantisce che la sequenza risultante rispetti una struttura formale, un oggetto JSON valido, un record conforme a uno schema, o una risposta con campi tipizzati. Per un essere umano che legge una risposta, una parentesi mancante o un campo in più è un dettaglio trascurabile; per un programma che deve parsare quella risposta e agire di conseguenza, è un errore fatale [1].
Il formato che si è affermato come standard de facto per definire la struttura attesa è JSON Schema, una specifica formale mantenuta dalla JSON Schema Organization che permette di descrivere la forma di un documento JSON: tipi dei campi, campi obbligatori e opzionali, vincoli sui valori, strutture annidate e composizione tramite operatori logici (allOf, anyOf, oneOf). La versione corrente è Draft 2020-12, con un percorso di standardizzazione IETF in corso [2]. JSON Schema è il vocabolario utilizzato da tutti i principali provider di LLM per specificare la struttura desiderata degli output, ed è alla base delle definizioni di tool nel Model Context Protocol (MCP) [3] e nelle API di function calling di OpenAI [4], Anthropic [5] e Google [6].
Prima dell'introduzione di meccanismi dedicati, l'unico approccio disponibile era il prompting: istruire il modello tramite il prompt a produrre output in un formato specifico. Questo approccio è intrinsecamente probabilistico, il modello può "dimenticare" di chiudere una parentesi, aggiungere campi non richiesti, o produrre JSON sintatticamente invalido. I tassi di conformità con il solo prompting variano significativamente in funzione della complessità dello schema, del modello e della lunghezza della risposta. A titolo indicativo, OpenAI ha riportato che il proprio JSON mode (un passo intermedio tra prompting puro e constrained decoding) raggiungeva circa l'86% di conformità su schema complessi prima dell'introduzione degli Structured Outputs [11]. Per applicazioni che richiedono affidabilità al 100%, come pipeline di data extraction, comunicazione tra agenti, o integrazione con API tipizzate, questa soglia non è accettabile.
La ricerca sugli structured output si colloca all'intersezione tra teoria dei linguaggi formali e generazione neurale del testo. L'idea di vincolare la generazione a una grammatica formale non è nuova, risale almeno ai lavori su grammar-guided neural generation degli anni 2010 per il semantic parsing, ma la sua applicazione pratica ai LLM moderni ha richiesto soluzioni ingegneristiche specifiche per gestire la tokenizzazione sub-word, i vocabolari da centinaia di migliaia di token, e le esigenze di latenza delle applicazioni di produzione [1, 7].
Architettura: dal decoding libero alla generazione vincolata
Il meccanismo base: logit masking
Il principio fondamentale del constrained decoding è elegante nella sua semplicità: a ogni passo di generazione, prima di campionare il token successivo, si identificano i token che porterebbero a una sequenza invalida rispetto alla grammatica target, e si impostano i loro logit a $-\infty$. La distribuzione di probabilità risultante è quindi rinormalizzata sui soli token validi, garantendo che ogni token generato sia compatibile con la struttura attesa [1, 7].
Formalmente, dato un modello di linguaggio $p(x_t | x_{