Confronto dei output strutturati tra i principali provider di LLM: OpenAI, Gemini, Anthropic, Mistral e AWS Bedrock

API leggermente diverse richiedono un approccio speciale.

Indice

Ecco un confronto a fianco del supporto per l’output strutturato (ottenere JSON affidabili) presso i principali provider di LLM, oltre a esempi minimi in Python

colorised bars standing

Abbiamo già esaminato come richiedere l’output strutturato dall’LLM ospitato su Ollama. Una volta che il JSON è in transito, la validazione dell’output strutturato LLM in Python che regge analizza il parsing, i controlli Pydantic, i retry e i test nel tuo servizio. Qui esaminiamo come richiedere la stessa cosa da altri provider.

Matrice TL;DR

Provider “JSON mode” nativo Applicazione Schema JSON Impostazione tipica Note
OpenAI Sì Sì (di prima classe) response_format={"type":"json_schema", ...} Funziona tramite l’API Responses o Chat Completions; può anche effettuare function calling.
Google Gemini Sì Sì (di prima classe) response_schema= + response_mime_type="application/json" Restituisce JSON rigorosamente validato quando è definito uno schema.
Anthropic (Claude) Indiretto Sì (tramite Tool Use con schema JSON) tools=[{input_schema=...}] + tool_choice Costringe il modello a “chiamare” il tuo tool definito tramite schema; produce argomenti conformi allo schema.
Mistral Sì Parziale (solo JSON; nessuna schema lato server) response_format={"type":"json_object"} Garantisce il formato JSON, ma tu validi contro il tuo schema lato client.
AWS Bedrock (piattaforma) Varia per modello Sì (tramite schema Tool/Converse) toolConfig.tools[].toolSpec.inputSchema L’API Converse di Bedrock valida l’input del tool contro uno schema JSON.

Output Strutturato LLM - Informazioni Generali

L’output strutturato degli LLM si riferisce alla capacità dei grandi modelli linguistici (LLM) di generare risposte che aderiscono strettamente a un formato o una struttura predefinita e specifica, anziché produrre testo libero. Questo output strutturato può assumere formati come JSON, XML, tabelle o template, rendendo i dati leggibili dalle macchine, coerenti e facilmente analizzabili dal software per l’uso in varie applicazioni.

Gli output strutturati differiscono dagli output tradizionali degli LLM, che tipicamente generano testo naturale aperto. Invece, gli output strutturati applicano uno schema o un formato, come oggetti JSON con chiavi e tipi di valore definiti, o classi specifiche nell’output (ad esempio, risposte a scelta multipla, classi di sentiment o formati di riga di database). Questo approccio migliora l’affidabilità, riduce errori e allucinazioni e semplifica l’integrazione in sistemi come database, API o flussi di lavoro.

La generazione di output strutturati negli LLM spesso coinvolge tecniche come:

  • Specificare istruzioni dettagliate nel prompt per guidare il modello a produrre output nel formato desiderato.
  • Utilizzare strumenti di validazione e parsing come Pydantic in Python per garantire che l’output corrisponda allo schema.
  • A volte applicare vincoli di decodifica basati su grammatica o macchine a stati finiti per garantire la conformità a livello di token al formato.

I vantaggi degli output strutturati degli LLM includono:

  • Leggibilità da parte delle macchine e facilità di integrazione.
  • Ridotta variabilità e minor numero di errori.
  • Migliore prevedibilità e verificabilità per task che richiedono formati di dati coerenti.

Le sfide includono la progettazione di schemi efficaci, la gestione di dati annidati complessi e potenziali limitazioni nelle capacità di ragionamento rispetto alla generazione di testo libero.

Nel complesso, l’output strutturato consente agli LLM di essere più utili in applicazioni che richiedono dati precisi e formattati, anziché solo testo leggibile dall’uomo.

Esempi Python per Output Strutturato

Tutti gli snippet estraggono le informazioni dell’evento come JSON: {title, date, location}. Sostituisci chiavi/modelli a piacere.

1) OpenAI — Schema JSON (rigoroso)

from openai import OpenAI
import json

client = OpenAI()

schema = {
    "name": "Event",
    "schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date":  {"type": "string", "description": "YYYY-MM-DD"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    },
    "strict": True
}

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}
    ],
    response_format={"type": "json_schema", "json_schema": schema},
)

data = json.loads(resp.choices[0].message.content)
print(data)

La funzionalità Structured Outputs di OpenAI applica questo schema lato server.


2) Google Gemini — schema di risposta + MIME JSON

import google.generativeai as genai
from google.genai import types

# Configura con la tua chiave API
# genai.configure(api_key="your-api-key")

schema = types.Schema(
    type=types.Type.OBJECT,
    properties={
        "title": types.Schema(type=types.Type.STRING),
        "date": types.Schema(type=types.Type.STRING),
        "location": types.Schema(type=types.Type.STRING),
    },
    required=["title", "date", "location"],
    additional_properties=False,
)

resp = genai.generate_content(
    model="gemini-2.0-flash",
    contents="Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=schema,
    ),
)

print(resp.text)  # già JSON valido secondo lo schema

Gemini restituirà JSON rigoroso conforme a response_schema.


3) Anthropic (Claude) — Tool Use con schema JSON

from anthropic import Anthropic
import json

client = Anthropic()

tool = {
    "name": "extract_event",
    "description": "Return event details.",
    "input_schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date": {"type": "string"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    }
}

msg = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=256,
    tools=[tool],
    tool_choice={"type": "tool", "name": "extract_event"},  # forza lo schema
    messages=[{"role": "user", "content":
        "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
)

# Claude "chiamerà" il tool; recupera gli argomenti (che corrispondono al tuo schema)
tool_use = next(b for b in msg.content if b.type == "tool_use")
print(json.dumps(tool_use.input, indent=2))

Claude non ha un interruttore generico per la “modalità JSON”; invece, l’Tool Use con uno input_schema ti fornisce argomenti validati e conformi allo schema (e puoi forzare il suo utilizzo).


4) Mistral — modalità JSON (validazione lato client)

from mistralai import Mistral
import json

client = Mistral()

resp = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role":"user","content":
        "Return JSON with keys title, date (YYYY-MM-DD), location for: "
        "'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
    response_format={"type": "json_object"}  # garantisce JSON valido
)

data = json.loads(resp.choices[0].message.content)
print(data)
# Suggerimento: valida `data` contro il tuo Pydantic/JSON Schema localmente.

json_object di Mistral applica la forma JSON (non il tuo schema esatto) — valida lato client.


5) AWS Bedrock — Schema Tool API Converse (indipendente dal modello)

import boto3, json

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"

tools = [{
    "toolSpec": {
        "name": "extract_event",
        "inputSchema": {
            "json": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "date": {"type": "string"},
                    "location": {"type": "string"}
                },
                "required": ["title","date","location"],
                "additionalProperties": False
            }
        }
    }
}]

resp = bedrock.converse(
    modelId=model_id,
    toolConfig={"tools": tools},
    toolChoice={"tool": {"name": "extract_event"}},  # forza lo schema
    messages=[{"role":"user","content":[{"text":
        "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}]}],
)

# Estrai il contenuto toolUse
tool_use = next(
    c["toolUse"] for c in resp["output"]["message"]["content"] if "toolUse" in c
)
print(json.dumps(tool_use["input"], indent=2))

Bedrock valida l’input del tool rispetto al tuo schema JSON e molti modelli ospitati (ad esempio, Claude) lo supportano tramite Converse.


Guida pratica e Validazione

  • Se vuoi le garanzie lato server più forti: output strutturati di OpenAI o schema di risposta di Gemini.
  • Se sei già su Claude/Bedrock: definisci un Tool con uno schema JSON e forzarne l’uso; leggi gli argomenti del tool come il tuo oggetto tipizzato.
  • Se usi Mistral: abilita json_object e valida localmente (ad esempio, con Pydantic).

Modello di validazione (funziona per tutti)

from pydantic import BaseModel, ValidationError

class Event(BaseModel):
    title: str
    date: str
    location: str

try:
    event = Event.model_validate(data)  # `data` da qualsiasi provider
except ValidationError as e:
    # gestisci / riprova / chiedi al modello di correggere con e.errors()
    print(e)

Collegamenti utili

Iscriviti

Ricevi nuovi articoli su sistemi, infrastruttura e ingegneria AI.