Vai al contenuto principale

Primi passi con gli Structured Outputs di OpenAI

Scopri come iniziare con gli Structured Outputs di OpenAI, capire la nuova sintassi ed esplorare le applicazioni principali.
Aggiornato 29 set 2026  · 9 min leggi

Scopri con l'IA

ChatGPTClaudePerplexity

Ad agosto 2024, OpenAI ha annunciato una nuova potente funzionalità nella sua API — Structured Outputs. Con questa funzione, come suggerisce il nome, puoi fare in modo che gli LLM generino risposte solo nel formato che specifichi. Questa capacità semplifica notevolmente la creazione di applicazioni che richiedono una formattazione dei dati precisa. 

In questo tutorial imparerai come iniziare con gli Structured Outputs di OpenAI, comprendere la nuova sintassi ed esplorare le principali applicazioni.

Perché gli Structured Outputs sono importanti nelle applicazioni di AI

Le risposte deterministiche, ovvero risposte con una formattazione coerente, sono fondamentali per molti compiti come inserimento dati, recupero di informazioni, question answering, workflow multi-step e così via. Probabilmente avrai notato come gli LLM possano generare output in formati molto diversi, anche con lo stesso prompt.

Per esempio, considera questa semplice funzione classify_sentiment alimentata da GPT-4o:

# List of hotel reviews
reviews = [
   "The room was clean and the staff was friendly.",
   "The location was terrible and the service was slow.",
   "The food was amazing but the room was too small.",
]
# Classify sentiment for each review and print the results
for review in reviews:
   sentiment = classify_sentiment(review)
   print(f"Review: {review}\nSentiment: {sentiment}\n")

Output:

Review: The room was clean and the staff was friendly.
Sentiment: Positive
Review: The location was terrible and the service was slow.
Sentiment: Negative
Review: The food was amazing but the room was too small.
Sentiment: The sentiment of the review is neutral.

Anche se le prime due risposte sono nello stesso formato di una singola parola, l'ultima è un'intera frase. Se un'altra applicazione a valle dipendesse dall'output del codice precedente, andrebbe in errore perché si aspetterebbe una risposta di una sola parola.

Possiamo risolvere questo problema con un po' di prompt engineering, ma è un processo iterativo e dispendioso. Anche con un prompt perfetto, non possiamo essere sicuri al 100% che le risposte rispetteranno il nostro formato nelle richieste future. A meno di usare, appunto, gli Structured Outputs:

def classify_sentiment_with_structured_outputs(review):
   """Sentiment classifier with Structured Outputs"""
   ...
# Classify sentiment for each review with Structured Outputs
for review in reviews:
   sentiment = classify_sentiment_with_structured_outputs(review)
   print(f"Review: {review}\nSentiment: {sentiment}\n")

Output:

Review: The room was clean and the staff was friendly.
Sentiment: {"sentiment":"positive"}
Review: The location was terrible and the service was slow.
Sentiment: {"sentiment":"negative"}
Review: The food was amazing but the room was too small.
Sentiment: {"sentiment":"neutral"}

Con la nuova funzione, classify_sentiment_with_structured_outputs, le risposte sono tutte nello stesso formato.

Questa capacità di forzare i modelli linguistici in un formato rigido è significativa: ti fa risparmiare innumerevoli ore di prompt engineering o la necessità di altri tool open source.

Primi passi con gli Structured Outputs di OpenAI

In questa sezione analizzeremo gli structured outputs usando come esempio la funzione di analisi del sentiment.

Configurare l'ambiente

Prerequisiti

Prima di iniziare, assicurati di avere quanto segue:

  • Python 3.7 o successivo installato sul tuo sistema.
  • Una chiave API di OpenAI. Puoi ottenerla registrandoti sul sito di OpenAI.

Configurare l'API di OpenAI

1. Installa il pacchetto Python di OpenAI: apri il terminale ed esegui il seguente comando per installare o aggiornare il pacchetto Python di OpenAI all'ultima versione:

$ pip install -U openai

2. Imposta la tua chiave API: puoi impostare la chiave API come variabile d'ambiente o direttamente nel codice. Per impostarla come variabile d'ambiente, esegui:

$ export OPENAI_API_KEY='your-api-key'

3. Verifica l'installazione: crea un semplice script Python per verificare l'installazione:

from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
   model="gpt-4o-mini",
   messages=[
       {"role": "system", "content": "You are a helpful assistant."},
       {"role": "user", "content": "Say hello!"}
   ],
   max_tokens=5
)
>>> print(response.choices[0].message.content.strip())
Hello! How can I

Esegui lo script per assicurarti che tutto sia configurato correttamente. Dovresti vedere la risposta del modello stampata nel terminale.

Oltre al pacchetto OpenAI, ti servirà la libreria Pydantic per definire e validare gli schemi JSON per gli Structured Outputs. Installala con pip:

$ pip install pydantic

Con questi passaggi, il tuo ambiente è pronto per usare la funzione Structured Outputs di OpenAI.

Definire uno schema di output con Pydantic

Per usare gli Structured Outputs, devi definire la struttura dell'output atteso con i modelli Pydantic. Pydantic è una libreria di validazione dei dati e gestione delle impostazioni per Python, che permette di definire modelli di dati usando le annotazioni di tipo. Questi modelli possono poi essere usati per imporre la struttura degli output generati dai modelli di OpenAI.

Ecco un esempio di modello Pydantic per specificare il formato per il nostro classificatore di sentiment delle recensioni:

from pydantic import BaseModel
from typing import Literal
class SentimentResponse(BaseModel):
   sentiment: Literal["positive", "negative", "neutral"]

In questo esempio:

  • SentimentResponse è un modello Pydantic che definisce la struttura attesa dell'output.
  • Il modello ha un solo campo sentiment, che può assumere solo uno tra tre valori letterali: "positive", "negative" o "neutral".

Quando passiamo questo modello come parte delle nostre richieste all'API di OpenAI, gli output saranno solo una delle parole che abbiamo fornito.

Vediamo come.

Usare l'helper parse

Per imporre il nostro schema Pydantic nelle richieste a OpenAI, basta passarlo al parametro response_format della Chat Completions API. In pratica, ecco come appare:

response = client.beta.chat.completions.parse(
   model=MODEL,
   messages=[...],
   response_format=SentimentResponse
)

Come puoi notare, invece di usare client.chat.completions.create, usiamo il metodo client.beta.chat.completions.parse. .parse() è un nuovo metodo nella Chat Completions API pensato specificamente per gli Structured Outputs.

Ora mettiamo tutto insieme riscrivendo il classificatore di sentiment delle recensioni con gli Structured Outputs. Per prima cosa effettuiamo gli import necessari, definiamo il modello Pydantic, il system prompt e un template di prompt:

from openai import OpenAI
from pydantic import BaseModel
from typing import Literal
class SentimentResponse(BaseModel):
   sentiment: Literal["positive", "negative", "neutral"]
client = OpenAI()
MODEL = "gpt-4o-mini"
SYSTEM_PROMPT = "You are a sentiment classifier assistant."
PROMPT_TEMPLATE = """
   Classify the sentiment of the following hotel review as positive, negative, or neutral:\n\n{review}
"""

Poi scriviamo una nuova funzione che usa il metodo helper .parse():

# Function to classify sentiment using OpenAI's chat completions API with structured outputs
def classify_sentiment_with_structured_outputs(review):
   response = client.beta.chat.completions.parse(
       model=MODEL,
       messages=[
           {"role": "system", "content": SYSTEM_PROMPT},
           {"role": "user", "content": PROMPT_TEMPLATE.format(review=review)},
       ],
       response_format=SentimentResponse
   )
   return response.choices[0].message

La riga importante della funzione è response_format=SentimentResponse, che è ciò che abilita concretamente gli Structured Outputs.

Testiamola su una delle recensioni:

# List of hotel reviews
reviews = [
   "The room was clean and the staff was friendly.",
   "The location was terrible and the service was slow.",
   "The food was amazing but the room was too small.",
]
result = classify_sentiment_with_structured_outputs(reviews[0])
>>> print(result.content)
{"sentiment":"positive"}

Qui, result è un oggetto message:

>>> type(result)
openai.types.chat.parsed_chat_completion.ParsedChatCompletionMessage[SentimentResponse]

Oltre all'attributo .content, che recupera la risposta, ha un attributo .parsed che restituisce le informazioni analizzate come classe:

>>> result.parsed
SentimentResponse(sentiment='positive')

Come vedi, abbiamo un'istanza della classe SentimentResponse. Questo significa che possiamo accedere al sentiment come stringa invece che come dizionario usando l'attributo .sentiment:

>>> result.parsed.sentiment
'positive'

Nidificare modelli Pydantic per definire schemi complessi

In alcuni casi potresti dover definire strutture di output più complesse che coinvolgono dati annidati. Pydantic ti permette di nidificare i modelli l'uno nell'altro, consentendoti di creare schemi articolati che coprono vari casi d'uso. Ciò è particolarmente utile quando si lavora con dati gerarchici o quando devi imporre una struttura specifica per output complessi.

Consideriamo un esempio in cui dobbiamo estrarre informazioni dettagliate sull'utente, inclusi nome, contatti e un elenco di indirizzi. Ogni indirizzo dovrebbe includere campi per via, città, stato e CAP. Questo richiede più di un modello Pydantic per costruire lo schema corretto.

Passo 1: definire i modelli Pydantic

Per prima cosa, definiamo i modelli Pydantic per l'indirizzo e le informazioni utente:

from pydantic import BaseModel
from typing import List
# Define the Pydantic model for an address
class Address(BaseModel):
   street: str
   city: str
   state: str
   zip_code: str
# Define the Pydantic model for user information
class UserInfo(BaseModel):
   name: str
   email: str
   phone: str
   addresses: List[Address]

In questo esempio:

  • Address è un modello Pydantic che definisce la struttura di un indirizzo.
  • UserInfo è un modello Pydantic che include un elenco di oggetti Address, oltre a campi per nome, email e numero di telefono dell'utente.

Passo 2: usare i modelli Pydantic annidati nelle chiamate API

Successivamente, usiamo questi modelli Pydantic annidati per imporre la struttura dell'output in una chiamata all'API di OpenAI:

SYSTEM_PROMPT = "You are a user information extraction assistant."
PROMPT_TEMPLATE = """ Extract the user information from the following text:\n\n{text}"""
# Function to extract user information using OpenAI's chat completions API with structured outputs
def extract_user_info(text):
   response = client.beta.chat.completions.parse(
       model=MODEL,
       messages=[
           {"role": "system", "content": SYSTEM_PROMPT},
           {"role": "user", "content": PROMPT_TEMPLATE.format(text=text)},
       ],
       response_format=UserInfo
   )
   return response.choices[0].message
# Example text containing user information
text = """John DoeEmail: john.doe@example.comPhone: 123-456-7890Addresses:- 123 Main St, Springfield, IL, 62701- 456 Elm St, Shelbyville, IL, 62702"""
# Extract user information and print the results
user_info = extract_user_info(text)

Il testo di esempio è del tutto illeggibile e privo di spazi tra informazioni chiave. Vediamo se il modello riesce comunque. Useremo la libreria json per rendere più leggibile la risposta:

import json
data = json.loads(user_info.content)
pretty_response = json.dumps(data, indent=2)
print(pretty_response)
{
 "name": "John Doe",
 "email": "john.doe@example.com",
 "phone": "123-456-7890",
 "addresses": [
   {
     "street": "123 Main St",
     "city": "Springfield",
     "state": "IL",
     "zip_code": "62701"
   },
   {
     "street": "456 Elm St",
     "city": "Shelbyville",
     "state": "IL",
     "zip_code": "62702"
   }
 ]
}

Come puoi vedere, il modello ha correttamente catturato le informazioni di un singolo utente insieme ai due indirizzi separati sulla base dello schema fornito.

In breve, nidificando i modelli Pydantic puoi definire schemi complessi che gestiscono dati gerarchici e impongono strutture specifiche per output articolati.

Function calling con Structured Outputs

Una delle funzionalità più diffuse dei modelli linguistici più recenti è il function calling (chiamato anche tool calling). Questa capacità ti permette di collegare i modelli linguistici a funzioni definite dall'utente, dando di fatto ai modelli accesso al mondo esterno.

Alcuni esempi comuni sono:

  • Recupero di dati in tempo reale (ad es., meteo, prezzi azionari, risultati sportivi)
  • Esecuzione di calcoli o analisi dei dati
  • Query su database o API
  • Generazione di immagini o altri media
  • Traduzione di testi tra lingue
  • Controllo di dispositivi smart home o sistemi IoT
  • Esecuzione di logiche di business o workflow personalizzati

Non entreremo nei dettagli di come funziona il function calling qui, ma puoi leggere il nostro tutorial su OpenAI Function Calling.

È importante sapere che, con gli Structured Outputs, usare il function calling con i modelli OpenAI diventa molto più semplice. In passato, le funzioni che passavi ai modelli OpenAI richiedevano la scrittura di schemi JSON complessi, definendo ogni parametro con i type hint. Ecco un esempio:

{
   "type": "function",
   "function": {
       "name": "get_current_weather",
       "description": "Get the current weather",
       "parameters": {
           "type": "object",
           "properties": {
               "location": {
                   "type": "string",
                   "description": "The city and state, e.g. San Francisco, CA",
               },
               "format": {
                   "type": "string",
                   "enum": ["celsius", "fahrenheit"],
                   "description": "The temperature unit to use. Infer this from the users location.",
               },
           },
           "required": ["location", "format"],
       },
   }
}

Anche se la funzione get_current_weather ha due parametri, il suo schema JSON diventa enorme e soggetto a errori se scritto a mano.

Questo si risolve con gli Structured Outputs usando nuovamente i modelli Pydantic:

from pydantic import BaseModel
from typing import Literal
def get_weather(location: str, unit: str, condition: str):
   # Implementation details...
   pass
class WeatherData(BaseModel):
   location: str
   unit: Literal["celsius", "fahrenheit"]
   condition: Literal["sunny", "cloudy", "rainy", "snowy"]

Prima scrivi la funzione e la sua logica. Poi la definisci di nuovo con un modello Pydantic specificando i parametri di input attesi.

Quindi, per convertire il modello Pydantic in uno schema JSON compatibile, chiami pydantic_function_tool:

>>> import openai
>>> openai.pydantic_function_tool(WeatherData)
{'type': 'function',
'function': {'name': 'WeatherData',
 'strict': True,
 'parameters': {'properties': {'location': {'title': 'Location',
    'type': 'string'},
   'unit': {'enum': ['celsius', 'fahrenheit'],
    'title': 'Unit',
    'type': 'string'},
   'condition': {'enum': ['sunny', 'cloudy', 'rainy', 'snowy'],
    'title': 'Condition',
    'type': 'string'}},
  'required': ['location', 'unit', 'condition'],
  'title': 'WeatherData',
  'type': 'object',
  'additionalProperties': False}}}

Ecco come usare questo tool come parte di una richiesta:

import openai
client = OpenAI()
tools = [openai.pydantic_function_tool(WeatherData)]
messages = [
   {
       "role": "system",
       "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user.",
   },
   {
       "role": "user",
       "content": "What is the weather in Tokyo?",
   }
]
response = client.chat.completions.create(
   model=MODEL, messages=messages, tools=tools
)
tool_call = response.choices[0].message.tool_calls[0]
>>> tool_call
ChatCompletionMessageToolCall(id='call_QnZZ0DmNN2cxw3bN433JQNIC', function=Function(arguments='{"location":"Tokyo","unit":"celsius","condition":"sunny"}', name='WeatherData'), type='function')

Passiamo il modello Pydantic in un formato JSON compatibile al parametro tools della Chat Completions API. Poi, in base alla query, il modello decide se chiamare o meno lo strumento.

Dato che la nostra query nell'esempio sopra è “What is the weather in Tokyo?”, vediamo una chiamata nei tool_calls dell'oggetto message restituito.

Ricorda, il modello non chiama la funzione get_weather, ma genera gli argomenti per essa sulla base dello schema Pydantic che abbiamo fornito:

arguments = json.loads(tool_call.function.arguments)
>>> arguments
{'location': 'Tokyo', 'unit': 'celsius', 'condition': 'sunny'}

Sta a noi chiamare la funzione con gli argomenti forniti:

some_result = get_weather(**arguments)

Se vuoi che il modello generi gli argomenti per la funzione e la chiami allo stesso tempo, ti serve un agente AI. 

Abbiamo un tutorial su LangChain Agents dedicato, se ti interessa.

Best practice per usare gli Structured Outputs di OpenAI

Quando usi gli Structured Outputs, ci sono diverse best practice e raccomandazioni da tenere a mente. In questa sezione ne elenchiamo alcune.

  1. Usa modelli Pydantic per definire gli schemi di output: offrono un modo pulito e type-safe per definire le strutture attese.
  2. Mantieni gli schemi semplici e specifici per ottenere risultati più accurati.
  3. Usa tipi di dato appropriati (str, int, float, bool, List, Dict) per rappresentare accuratamente i tuoi dati.
  4. Usa i tipi Literal per gli enum per definire valori consentiti specifici per i campi.
  5. Gestisci i rifiuti del modello. Quando usi il nuovo metodo .parse(), gli oggetti message hanno un nuovo attributo .refusal per indicare un rifiuto:
text = """John DoeEmail: john.doe@example.comPhone: 123-456-7890Addresses:- 123 Main St, Springfield, IL, 62701- 456 Elm St, Shelbyville, IL, 62702"""
user_info = extract_user_info(text)
if user_info.refusal:
   print(user_info.refusal)
else:
   print(user_info.content)

Output:

{"name":"John Doe","email":"john.doe@example.com","phone":"123-456-7890","addresses":[{"street":"123 Main St","city":"Springfield","state":"IL","zip_code":"62701"},{"street":"456 Elm St","city":"Shelbyville","state":"IL","zip_code":"62702"}]}

6. Fornisci descrizioni chiare e concise per ogni campo nei tuoi modelli Pydantic per migliorare la precisione dell'output del modello:

from pydantic import BaseModel, Field
class Person(BaseModel):
   name: str = Field(..., description="The person's full name")
   age: int = Field(..., description="The person's age in years")
   occupation: str = Field(..., description="The person's current job or profession")

Queste pratiche ti aiuteranno a sfruttare al meglio gli Structured Outputs nelle tue applicazioni.

Conclusione

In questo tutorial abbiamo visto come iniziare con una nuova funzionalità dell'API di OpenAI: gli Structured Outputs. Abbiamo visto come questa funzione costringe i modelli linguistici a produrre output nel formato che specifichiamo. Abbiamo imparato a usarla in combinazione con il function calling ed esplorato alcune best practice per sfruttarla al meglio.

Ecco alcune risorse correlate per approfondire:

Structured Outputs - Domande frequenti

Come funzionano i modelli Pydantic con gli Structured Outputs?

I modelli Pydantic vengono usati per definire lo schema della struttura di output desiderata, che viene poi passato all'API di OpenAI per imporre il formato della risposta.

Gli Structured Outputs possono essere usati con il function calling?

Sì, gli Structured Outputs possono essere usati con il function calling per semplificare la definizione dei parametri della funzione e degli output attesi.

Quali sono i vantaggi dell'uso degli Structured Outputs?

I vantaggi includono formati di risposta coerenti, minore necessità di post-processing, maggiore affidabilità nelle applicazioni di AI e integrazione più semplice con i sistemi esistenti.

Ci sono limitazioni nell'uso degli Structured Outputs?

Pur essendo potenti, gli Structured Outputs possono limitare la flessibilità dell'AI nelle risposte e richiedono un'attenta progettazione degli schemi per bilanciare struttura e livello di dettaglio desiderato negli output.


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

Sono un creator di contenuti sulla data science con oltre 2 anni di esperienza e uno dei più seguiti su Medium. Mi piace scrivere articoli dettagliati su AI e ML con un pizzico di sarcasmo, perché bisogna pur fare qualcosa per renderli un po’ meno noiosi. Ho pubblicato oltre 130 articoli e un corso su DataCamp, con un altro in arrivo. I miei contenuti sono stati visti da oltre 5 milioni di persone, 20.000 delle quali sono diventate follower sia su Medium che su LinkedIn. 

Argomenti
Intelligenza artificiale
OpenAI

I migliori corsi OpenAI

Corso

Lavorare con l'API di OpenAI

3 ore
178.4K
Inizia a sviluppare applicazioni AI con l’API OpenAI. Scopri le funzionalità alla base di applicazioni AI popolari come ChatGPT.
Vedi i dettagliRight Arrow
Inizia Il Corso
Mostra altroRight Arrow
Correlato

blog

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

blog

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

blog

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

Mostra AltroMostra Altro