Confronto strutturato degli output tra i principali fornitori di LLM - OpenAI, Gemini, Anthropic, Mistral e AWS Bedrock

API leggermente diverse richiedono un approccio speciale.

Indice

Ecco un confronto a fianco a fianco del supporto per output strutturato (ottenere un JSON affidabile) tra i principali fornitori di LLM, più esempi minimi in Python

barre colorate in piedi

Abbiamo già visto come richiedere output strutturato dall’LLM ospitato su Ollama. Qui rivediamo come richiederlo da altri fornitori.

TL;DR matrix

Fornitore “Modalità JSON” nativa Enforce JSON Schema Knob tipico Note
OpenAI Sì (prima classe) response_format={"type":"json_schema", ...} Funziona tramite l’API Responses o Chat Completions; è possibile anche eseguire la chiamata a funzioni.
Google Gemini Sì (prima classe) response_schema= + response_mime_type="application/json" Restituisce JSON validato in modo rigoroso quando è impostato lo schema.
Anthropic (Claude) Indiretto Sì (tramite Tool Use con JSON schema) tools=[{input_schema=...}] + tool_choice Obbliga il modello a “chiamare” il tuo strumento definito nello schema; restituisce argomenti con forma di schema.
Mistral Parziale (solo JSON; nessuno schema server-side) response_format={"type":"json_object"} Garantisce JSON, ma tu validi contro il tuo schema client-side.
AWS Bedrock (piattaforma) Varia per modello Sì (tramite Tool/Converse schema) toolConfig.tools[].toolSpec.inputSchema L’API Converse di Bedrock valida l’input dello strumento contro uno schema JSON.

Output strutturato degli LLM - Informazioni generali

L’output strutturato degli LLM si riferisce alla capacità dei modelli linguistici di grandi dimensioni (LLMs) di generare risposte che rispettano rigorosamente un formato o una struttura definita, invece di produrre testo libero. Questo output strutturato può essere in formati come JSON, XML, tabelle o modelli, rendendo i dati leggibili da macchina, coerenti e facilmente analizzabili dal software per l’utilizzo in varie applicazioni.

Gli output strutturati differiscono dagli output tradizionali degli LLM, che tipicamente generano testo naturale aperto. Al contrario, gli output strutturati impongono 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 sentimenti o formati di righe di database). Questo approccio migliora l’affidabilità, riduce gli errori e le illusioni, e semplifica l’integrazione in sistemi come database, API o flussi di lavoro.

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

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

I vantaggi degli output strutturati degli LLM includono:

  • Leggibilità da parte della macchina e facilità di integrazione.
  • Riduzione della variabilità e degli errori.
  • Maggiore prevedibilità e verificabilità per compiti che richiedono formati di dati coerenti.

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

In generale, l’output strutturato consente agli LLM di essere più utili in applicazioni che richiedono dati precisi e formattati, piuttosto che solo testo leggibile dall’uomo.

Esempi di output strutturato in Python

Tutti gli snippet estraggono evento info come JSON: {title, date, location}. Sostituisci le chiavi/modelli come ti piace.

1) OpenAI — JSON Schema (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": "Estrai l'evento da: 'PyData Sydney è il 2025-11-03 a Darling Harbour.'"}
    ],
    response_format={"type": "json_schema", "json_schema": schema},
)

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

La funzionalità Output Strutturati di OpenAI impone questo schema sul 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="Estrai l'evento da: 'PyData Sydney è il 2025-11-03 a Darling Harbour.'",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=schema,
    ),
)

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

Gemini restituirà JSON rigoroso che si conforma a response_schema.


3) Anthropic (Claude) — Utilizzo dello strumento con JSON schema

from anthropic import Anthropic
import json

client = Anthropic()

tool = {
    "name": "extract_event",
    "description": "Restituisce i dettagli dell'evento.",
    "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":
        "Estrai l'evento da: 'PyData Sydney è il 2025-11-03 a Darling Harbour.'"}],
)

# Claude "chiamerà" lo strumento; prendi gli argomenti (che corrispondono allo 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, Utilizzo dello strumento con un input_schema ti dà argomenti validati e conformi allo schema (e puoi forzarne l’uso).


4) Mistral — modalità JSON (validazione client-side)

from mistralai import Mistral
import json

client = Mistral()

resp = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role":"user","content":
        "Restituisci JSON con chiavi title, date (YYYY-MM-DD), location per: "
        "'PyData Sydney è il 2025-11-03 a 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 schema Pydantic/JSON localmente.

La json_object di Mistral impone forma JSON (non lo schema esatto) — valida client-side.


5) AWS Bedrock — schema dello strumento API Converse (modello indipendente)

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":
        "Estrai l'evento da: 'PyData Sydney è il 2025-11-03 a 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 dello strumento contro il tuo schema JSON e molti modelli ospitati (ad esempio, Claude) lo supportano tramite Converse.


Linee guida pratiche e validazione

  • Se desideri le garanzie più forti sul lato server: Output strutturati di OpenAI o schema di risposta di Gemini.
  • Se sei già su Claude/Bedrock: definisci un Strumento con uno schema JSON e forzane l’uso; leggi gli argomenti dello strumento come oggetto tipizzato.
  • Se utilizzi Mistral: attiva 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)