Come rendere portatili gli output LLM strutturati su OpenAI, Anthropic e Gemini
Una guida pratica alla progettazione di output LLM strutturati portabili su OpenAI, Anthropic e Gemini utilizzando schemi canonici, compilazione specifica del fornitore, convalida, limiti di riparazione e test contrattuali.
I risultati del LLM strutturato diventano fragili quando i team considerano il supporto dei fornitori come intercambiabile. OpenAI, Anthropic e Gemini possono tutti produrre dati strutturati, ma non espongono lo stesso contratto. La sintassi dello schema, gli inviluppi delle chiamate agli strumenti, le garanzie di rigore, il comportamento di rifiuto, il comportamento di streaming e le modalità di errore di convalida differiscono abbastanza da far sì che un parser che funziona con un modello possa fallire silenziosamente con un altro.
La soluzione pratica è smettere di considerare le funzionalità di output strutturato specifiche del provider come un contratto aziendale. Definisci un contratto di output canonico per ciascun flusso di lavoro, compilalo nel formato di richiesta supportato da ciascun provider, convalida ogni risposta rispetto allo schema canonico e testa ogni modello prima che gli sia consentito servire il traffico di produzione.
Che cos'è l'output strutturato portatile?
L'output strutturato portatile significa che l'applicazione può fare affidamento sulla stessa forma di risposta a livello aziendale anche quando cambia il fornitore del modello sottostante. Ciò non significa che ogni fornitore riceve esattamente lo stesso JSON schema. Inoltre, ciò non significa che la convalida da parte del provider sia sufficiente.
Un contratto trasferibile ha cinque livelli:
- Schema canonico: la forma dei dati del flusso di lavoro indipendente dal fornitore.
- Linting della portabilità: verifica che lo schema eviti costrutti non supportati, applicati in modo debole o rischiosi tra i provider.
- Compilazione del provider: traduzione dal contratto canonico in OpenAI, Anthropic, Gemini o nella superficie di output strutturato nativa di un altro provider.
- Convalida runtime: convalida lato applicazione dell'oggetto restituito rispetto allo schema canonico.
- Test del contratto: test ripetibili che dimostrano che ogni modello gestisce campi obbligatori, enumerazioni, rifiuti, troncamenti, richieste non valide e casi limite semantici prima delle modifiche al routing di produzione.
Questo modello è particolarmente importante per un LLM aziendale API in cui diversi team, tenant o flussi di lavoro dei partner dipendono da contratti di output prevedibili.
Fatti: l'output strutturato del provider non è uno standard
Prima di progettare il livello di portabilità, separa il comportamento documentato del fornitore dalle ipotesi.
OpenAI
OpenAI documenta gli output strutturati in modo diverso dalla modalità JSON. La modalità JSON ha lo scopo di garantire la validità JSON, mentre gli output strutturati sono destinati a corrispondere a uno schema JSON fornito. OpenAI supporta l'output strutturato tramite la formattazione della risposta e tramite la chiamata di funzione con rigorosa aderenza allo schema, ma il comportamento documentato include vincoli di modello e versione e un sottoinsieme di schema supportato. Gli schemi non supportati in modalità rigorosa possono produrre API errori e le applicazioni devono comunque gestire i casi limite.
Anthropic
Anthropic documenta l'utilizzo dello strumento Claude come chiamate di strumenti strutturati basati su strumenti forniti dallo sviluppatore con un input_schema. Il modello restituisce un blocco tool_use e l'applicazione esegue lo strumento richiesto. Assomiglia alla chiamata di funzione, ma non è lo stesso contratto di una risposta dello schema OpenAI in formato risposta JSON. La guida tecnica di Anthropic rileva inoltre che JSON Schema può definire struttura, campi obbligatori ed enumerazioni, ma non può esprimere modelli di utilizzo come quando includere parametri facoltativi o quali combinazioni di campi hanno senso dal punto di vista aziendale.
Gemini
La documentazione Gemini API diGoogle afferma che l'output strutturato può essere configurato per JSON risposte e che la modalità di output strutturato Gemini supporta un sottoinsieme della specifica JSON dello schema. Ciò è importante perché uno schema valido JSON Schema potrebbe comunque non essere adatto come schema di richiesta del provider portatile.
Osservabilità
Le convenzioni semantiche di OpenTelemetry GenAI includono attributi per provider, modello richiesto, modello di risposta, tipo di output, motivi di finitura, utilizzo del token, definizioni dello strumento, argomenti di chiamata dello strumento e campi di richiesta e risposta GenAI correlati. Questi campi possono supportare il debug tra provider quando gli errori di output strutturato sono difficili da distinguere da problemi di prompt, schema, modello o provider.
Passaggio 1: definisci lo schema aziendale canonico
Inizia dal flusso di lavoro aziendale, non dal fornitore API. Un flusso di lavoro di valutazione del supporto, un flusso di lavoro di estrazione delle fatture, una raccomandazione di approvazione o un endpoint di automazione rivolto ai partner dovrebbero avere ciascuno uno schema canonico che rappresenti ciò di cui l'applicazione ha bisogno a valle.
Ad esempio, un flusso di lavoro di valutazione dell'assistenza potrebbe utilizzare questa forma canonica:
{
"type": "object",
"additionalProperties": false,
"required": ["category", "priority", "summary", "requires_human_review"],
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "security", "account", "other"]
},
"priority": {
"type": "string",
"enum": ["low", "normal", "high", "urgent"]
},
"summary": {
"type": "string",
"minLength": 1,
"maxLength": 500
},
"requires_human_review": {
"type": "boolean"
},
"customer_risk_signals": {
"type": "array",
"items": { "type": "string" },
"maxItems": 5
}
}
}
Questo schema è il contratto considerato attendibile dal resto dell'applicazione. Da esso vengono generati formati di richiesta specifici del provider; non sono la fonte della verità.
Utilizza campi obbligatori espliciti, array limitati, stringhe limitate, enumerazioni chiuse e additionalProperties: false laddove il tuo validatore lo supporta. Evita di accettare oggetti nidificati arbitrari a meno che il sistema downstream non sia progettato per gestire chiavi arbitrarie.
Passaggio 2: aggiungi un Lint di portabilità dello schema Passaggio
Un linter di portabilità dovrebbe rifiutare schemi tecnicamente validi JSON Schema ma rischioso per l'utilizzo di più modelli. L'obiettivo non è l'espressività completa dello JSON schema. L'obiettivo è un contratto di output stabile che possa essere compilato in diverse superfici del fornitore senza indebolire la garanzia dell'applicazione.
I controlli utili della lanugine includono:
- Rifiuta le parole chiave di composizione non supportate a meno che il tuo compilatore non disponga di mappature esplicite del provider per esse.
- Rifiuta
objectcampi non vincolati che consentono proprietà arbitrarie. - Richiedi enumerazioni per i campi di classificazione che determinano il routing, la fatturazione, le approvazioni o lo stato visibile ai partner.
- Richiede
maxItemsper gli array utilizzati nell'automazione o nella visualizzazione dell'interfaccia utente. - Richiedi
maxLengthper i campi di testo che inseriscono database, code o API partner. - Segnala i campi facoltativi la cui assenza modifica il significato aziendale.
- Rifiuta schemi la cui nidificazione o dimensione supera i limiti noti del provider nella matrice di capacità.
Il linter deve essere eseguito in CI e prima della distribuzione dello schema. Non attendere finché una richiesta di produzione non riscontra un errore del provider perché una parola chiave dello schema non era supportata.
Passaggio 3: compilazione in formati di richiesta specifici del provider
La compilazione del provider traduce lo schema canonico nella API superficie effettivamente supportata da ciascun modello. Il compilatore dovrebbe essere deterministico e con versione. Dovrebbe anche lasciare una traccia di controllo che mostri quale versione dello schema canonico ha prodotto quale payload della richiesta del provider.
OpenAI compilazione
Per OpenAI, il compilatore può indirizzare gli output strutturati attraverso la formattazione della risposta per attività di estrazione e classificazione o una funzione rigorosa che richiede flussi di lavoro orientati all'azione. Il payload compilato deve includere il sottoinsieme supportato dello schema e le impostazioni di rigidità specifiche del provider, quando disponibili.
Anthropic compilazione
Per Anthropic, il compilatore spesso rappresenterà l'output desiderato come uno strumento con un input_schema. L'applicazione prevede quindi un blocco di utilizzo dello strumento e convalida l'input dello strumento rispetto allo schema canonico. Ciò significa che l'adattatore deve normalizzare l'inviluppo di risposta, non solo analizzare JSON.
Gemini compilazione
Per Gemini, il compilatore dovrebbe indirizzare la propria configurazione di risposta strutturata JSON utilizzando solo il sottoinsieme di schemi supportato. Se uno schema canonico utilizza vincoli che Gemini non applica in modo nativo, il compilatore dovrebbe registrare tali vincoli come richiedenti la convalida lato applicazione.
L'output dell'adattatore deve essere normalizzato in un tipo di risultato interno:
type StructuredResult = {
provider: "openai" | "anthropic" | "gemini";
model: string;
schema_version: string;
status: "ok" | "refusal" | "truncated" | "provider_error" | "invalid";
raw_response: unknown;
parsed_output?: unknown;
finish_reason?: string;
};
Questa normalizzazione evita che il codice del flusso di lavoro downstream dipenda dagli inviluppi del provider.
Passaggio 4: convalida ogni risposta rispetto allo schema canonico
La struttura lato fornitore è la prima linea di difesa, non il controllo finale di correttezza. Convalida sempre l'output analizzato rispetto allo schema canonico dopo che il modello ha risposto.
La convalida runtime dovrebbe produrre errori strutturati come:
missing_required_fieldinvalid_enumunexpected_propertytype_mismatcharray_too_longstring_too_longsemantic_rule_failed
Mantieni la convalida strutturale separata dalla convalida semantica. JSON Lo schema può controllare la forma, le proprietà richieste, le enumerazioni e i vincoli di tipo di base. Non può esprimere completamente regole aziendali come transizioni di stato valide, soglie di approvazione, policy specifiche del cliente o combinazioni di campi reciprocamente dipendenti.
Ad esempio, questo è un validatore semantico, non una regola dello schema:
if (output.priority === "urgent" && output.requires_human_review !== true) {
throw new SemanticValidationError(
"urgent tickets must require human review"
);
}
Inserisci questi controlli nel codice dell'applicazione dove possono utilizzare la configurazione del cliente, lo stato del flusso di lavoro, il contesto di autorizzazione e il controllo delle versioni dei criteri.
Passaggio 5: utilizza la riparazione delimitata, quindi chiude con errore
Un percorso di riparazione può risolvere piccoli difetti di output, ma può anche nascondere uno schema sistematico e segnalare problemi. Utilizza un tentativo di riparazione mirato con l'errore di convalida incluso. Se la risposta riparata continua a non superare la convalida, falla chiusa o indirizzala alla revisione.
Una pratica politica di riparazione è simile alla seguente:
- Tentativo di riparazione solo per tipi di errori idonei, come campi obbligatori mancanti, enumerazioni non valide o JSON. non valido
- Non riparare i rifiuti del fornitore, i blocchi di sicurezza, gli errori di autorizzazione o gli output sensibili alle policy.
- Includi la versione dello schema canonico e l'errore di convalida nella richiesta di riparazione.
- Consenti al massimo un tentativo di riparazione per flussi di lavoro visibili al cliente o che attivano azioni.
- Registra il conteggio delle riparazioni, le riparazioni riuscite, l'utilizzo del token e lo stato finale.
Per i job di estrazione interna a basso rischio, un secondo tentativo di riparazione può essere accettabile se la latenza non è importante. Per i flussi di lavoro che attivano pagamenti, modifiche di accesso, risposte API dei partner o azioni di conformità, una policy con chiusura senza errore è solitamente più sicura.
Passaggio 6: crea test del contratto per ciascun modello di produzione
I test del contratto decidono se un modello è idoneo per un flusso di lavoro. Dovrebbero essere eseguiti prima del lancio iniziale, prima degli aggiornamenti del modello e prima delle modifiche ai criteri di routing.
Come minimo, testa ciascun modello e adattatore del provider per:
- Output del percorso felice valido.
- Campi obbligatori mancanti.
- Valori enumerativi non validi.
- Proprietà extra quando lo schema è chiuso.
- Troppi elementi dell'array.
- Stringhe troppo lunghe.
- Rifiuto o incapacità di ottemperare.
- Output troncato.
- Mancata corrispondenza della chiamata dello strumento o chiamata dello strumento mancante.
- Output strutturalmente valido ma semanticamente sbagliato.
Utilizza suggerimenti contraddittori che spingono il modello a violare il contratto. Ad esempio, chiedigli di aggiungere un campo esplicativo aggiuntivo, restituire una priorità al di fuori dell'enumerazione o omettere un valore booleano richiesto. Il test viene superato solo se il provider più l'adattatore più il validatore producono lo stato dell'applicazione previsto.
Per ogni flusso di lavoro, mantieni un semplice record di compatibilità:
{
"workflow": "support_triage",
"schema_version": "2026-09-24",
"model": "provider-model-name",
"supports_strict_schema": true,
"supports_tool_calling": true,
"supports_streaming_structured_output": false,
"refusal_signal_normalized": true,
"contract_tests_passed": true,
"known_limitations": ["max schema depth 4"]
}
Questa matrice di capacità appartiene al piano di controllo, non alle costanti applicative sparse.
Passaggio 7: affidabilità dell'output strutturato dello strumento
Senza telemetria, i team non sono in grado di stabilire se gli errori derivano da incompatibilità del provider, deriva del modello, difetti dello schema, difetti del prompt, troncamento o validatori downstream.
Registra questi campi per ogni richiesta di output strutturato:
- Nome del flusso di lavoro e versione dello schema.
- Fornitore, modello richiesto e modello di risposta.
- Modalità di output, ad esempio schema di risposta, chiamata allo strumento o modalità JSON.
- Stato di convalida e tipo di errore.
- Conteggio e risultato delle riparazioni.
- Motivo finale e stato del rifiuto.
- Token di richiesta, token di completamento, costo totale e latenza.
- Versione dell'adattatore e versione del compilatore.
Utilizza questi dati per confrontare i modelli prima di modificare il percorso. Un modello più economico non è più economico se aumenta i tentativi di riparazione, la revisione manuale, gli errori del partner API o la gestione degli incidenti a valle.
Compromessi da decidere esplicitamente
Gli schemi con denominatore comune più basso migliorano la portabilità ma indeboliscono i vincoli. Se lo schema di richiesta del provider deve essere semplificato, mantenere controlli più severi nella convalida canonica e nei validatori semantici.
La compilazione specifica del provider preserva i punti di forza nativi ma aggiunge manutenzione. Di solito ne vale la pena per i flussi di lavoro di produzione, ma solo se i compilatori hanno una versione e sono coperti da test contrattuali.
Gli schemi rigidi riducono gli errori del parser ma richiedono una migliore progettazione dell'attività. Se l'attività è sottospecificata, la rigidità espone prima l'ambiguità. Questo è utile, ma può rallentare lo sviluppo iniziale.
La riparazione migliora i tassi di completamento ma aggiunge costi e latenza. La riparazione delimitata è un meccanismo di ripristino, non un sostituto della progettazione dello schema.
La chiamata degli strumenti e il formato della risposta JSON servono flussi di lavoro diversi. La chiamata degli strumenti è spesso più adatta ai flussi di lavoro orientati all'azione. Il formato della risposta JSON è più semplice per l'estrazione, la classificazione e il riepilogo. Una piattaforma multimodello può supportarli entrambi, ma la policy del flusso di lavoro deve essere esplicita.
Previsioni: dove sta andando la portabilità dell'output strutturato
Il supporto del provider per l'output strutturato probabilmente continuerà a migliorare, ma non si dovrebbe dare per scontato JSON la piena portabilità dello schema tra modelli. I fornitori hanno architetture di modelli, API cronologie, modelli di utilizzo degli strumenti e comportamenti di sicurezza diversi. Anche quando il supporto dello schema diventerà più forte, le applicazioni avranno comunque bisogno della convalida canonica perché la correttezza aziendale dipende da più della JSON forma.
I team dovrebbero aspettarsi che le decisioni relative al routing dell'output strutturato diventino maggiormente basate sui dati. Affidabilità, tasso di riparazione, gestione dei rifiuti, latenza e costi saranno importanti insieme alla qualità del modello. La domanda operativa si sposterà da "questo modello supporta JSON?" a "questo modello soddisfa il contratto di questo flusso di lavoro a un costo e a un tasso di fallimento accettabili?"
Elenco di controllo per l'implementazione
- Definisci uno schema canonico per flusso di lavoro.
- Schemi di versione e mantenimento della riproducibilità dei payload delle richieste del provider.
- Aggiungi CI linting per funzionalità dello schema non supportate o rischiose.
- Compila schemi canonici in formati specifici del provider.
- Normalizza gli inviluppi del provider in un tipo di risultato interno.
- Convalida ogni risposta analizzata rispetto allo schema canonico.
- Aggiungi validatori semantici per le regole aziendali.
- Consenti solo tentativi di riparazione limitati.
- Chiusura non riuscita o invio alla revisione per flussi di lavoro ad alto rischio.
- Esegui test del contratto prima di aggiungere o aggiornare i modelli di produzione.
- Mantieni una matrice di capacità del modello nel piano di controllo.
- Registra i campi di convalida, riparazione, costo, latenza, provider e modello per ogni richiesta.
Conclusione attuabile
La portabilità dell'output strutturato è un problema contrattuale API, non un problema di formattazione immediata. Il modello affidabile consiste nel definire uno schema aziendale canonico, renderlo portabile, compilarlo nella superficie nativa di ciascun fornitore, convalidare ogni risposta dopo che il fornitore l'ha restituita e utilizzare test contrattuali per decidere quali modelli sono idonei per la produzione.
Questa progettazione aggiunge un po' di lavoro tecnico iniziale, ma impedisce la modalità di errore più costosa: i sistemi downstream presuppongono un contratto stabile mentre il routing del modello, il comportamento del provider o l'applicazione dello schema cambiano silenziosamente al di sotto di essi.