Correzione, ottobre 2026: una versione precedente di questo articolo riportava risultati numerici di un confronto fra sei modelli e rimandava a un banco di prova che non posso fornire né verificare. Ho ritirato quei numeri e le raccomandazioni basate su di essi. Questa versione presenta un esempio eseguibile e distingue la conformità allo schema dalla correttezza dei dati.

Quando un agente consegna la propria risposta a un programma, un JSON valido è importante. Ma output strutturato nasconde tre domande distinte: i byte sono analizzabili? I campi rispettano il contratto? I valori sono supportati dal testo di partenza? Un modello può superare i primi due controlli e fallire il terzo.

Partire dal contratto

Un programma deve estrarre da un breve resoconto una priorità, il numero di persone coinvolte e un riassunto. Ecco un esempio con Pydantic 2; per provarlo, installa il pacchetto con python -m pip install 'pydantic>=2,<3':

from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, ValidationError

class Extraction(BaseModel):
    model_config = ConfigDict(strict=True, extra="forbid")
    priority: Literal["low", "medium", "high"]
    affected_users: int = Field(ge=0)
    summary: str

source = "The report says no users were affected. Priority is low."
shape_valid_but_wrong = (
    '{"priority":"low","affected_users":12,'
    '"summary":"No users were affected."}'
)
record = Extraction.model_validate_json(shape_valid_but_wrong)
print(record.affected_users)              # 12: lo schema lo accetta
print(record.affected_users == 0)         # False: il resoconto dice altro

try:
    Extraction.model_validate_json(
        '{"priority":"low","affected_users":"twelve",'
        '"summary":"No users were affected."}'
    )
except ValidationError:
    print("Type validation rejected the string count")

Il primo oggetto contiene i campi e i tipi richiesti, ma 12 contraddice il resoconto. Pydantic fa il suo lavoro: verifica il contratto dichiarato, non le prove. Il secondo oggetto viene rifiutato perché il conteggio è una stringa. extra="forbid" rifiuta anche i campi inattesi. La documentazione sulla modalità strict chiarisce perché bisogna conoscere le regole di conversione predefinite.

Questo codice illustra un problema; non è un benchmark di modelli. Qui non viene eseguito alcun modello linguistico.

Il ruolo della grammatica

Un decoder vincolato limita i token successivi in base a una grammatica o a uno schema. Outlines documenta gli output JSON Schema e llama.cpp documenta le proprie grammatiche. Sono utili quando il programma successivo si aspetta una forma precisa. Non verificano che un numero compaia nella fonte, che un riassunto sia fedele o che una priorità sia giustificata.

Chiedere «rispondi in JSON» in un prompt non equivale a vincolare il decoder. Il prompt è una richiesta; la grammatica limita i token possibili. Un test deve specificare quale meccanismo usa. Occorre inoltre validare l’output completo al confine dell’applicazione.

Cosa servirebbe per un confronto reale

Per confrontare modelli in modo verificabile, pubblica prompt, risposte attese, schema, file e quantizzazioni esatti dei modelli, motore e versione dell’inferenza, impostazioni di decoding, codice di validazione e output grezzi. Specifica il numero di prove e come valuti le risposte ambigue. Distingui almeno questi errori:

  1. Errore di parsing: l’output non è JSON valido.
  2. Violazione del contratto: il JSON è leggibile ma campi, tipi, intervalli o valori ammessi non sono corretti.
  3. Errore di evidenza: il contratto è rispettato, ma un valore non è supportato dalla fonte o la contraddice.
  4. Errore operativo: timeout, troncamento o risposta incompleta.

Un tasso di successo al primo tentativo ha senso solo se numeratore, denominatore e definizione di errore sono espliciti. La conformità allo schema non misura la correttezza fattuale dell’estrazione. Se la grammatica elimina gli errori sintattici, la valutazione deve concentrarsi sulla qualità delle prove.

Nella pratica

Definisci uno schema piccolo ed esplicito, vincola la generazione se il backend lo supporta, quindi analizza e valida di nuovo all’ingresso dell’applicazione. Confronta i campi importanti con la fonte o con un sistema affidabile. Rendi visibili gli errori e decidi in base all’impatto se riprovare o chiedere una verifica. Non trasformare silenziosamente «ben formato» in «vero».

Senza un confronto riproducibile, non posso raccomandare un particolare modello locale per questo compito. La conclusione affidabile è più circoscritta: la struttura è una proprietà del contratto di output; la correttezza è un’affermazione sul mondo. Verifica entrambe.