Function calling: guida pratica con un esempio Python completo
Come funziona il function calling: un esempio Python completo, le differenze tra OpenAI, Anthropic e Gemini e quando bastano gli output strutturati.
Pubblicato il

Il function calling permette a un modello linguistico di grandi dimensioni (LLM) di chiedere al software di eseguire un'operazione. La separazione dei compiti si articola in 3 passaggi: il modello propone una chiamata strutturata, il codice la convalida e la esegue, poi il risultato viene restituito al modello. Collegare un database significa costruire questa interfaccia, non consegnare al modello la password di accesso.
Come funziona il function calling?
Il modello formula una richiesta. L'applicazione esegue l'operazione. Per chi deve collegare un LLM a un'API o a un database, questa distinzione è il punto da chiarire prima ancora di scrivere codice.
Pensiamo alla comanda di un ristorante. Il cameriere può ordinare un piatto, ma il foglietto non cucina nulla. La cucina controlla la richiesta, prepara ciò che può e comunica l'esito. Nell'applicazione, la chiamata a uno strumento è la comanda; la funzione è la cucina.
Al modello si fornisce uno schema dello strumento: una descrizione dell'operazione disponibile e dei campi di input che accetta. Il modello può rispondere con il nome della funzione e i suoi argomenti, cioè i valori da inserire in quei campi. L'applicazione controlla la richiesta, invoca la funzione e ne restituisce l'output, così il modello può rispondere o chiedere un'altra operazione. La guida al function calling di Google assegna esplicitamente l'esecuzione all'applicazione.
Uno strumento può leggere dati oltre che scriverli. Cercare un ordine, interrogare una raccolta di documenti e verificare una disponibilità sono operazioni di uno strumento anche quando non modificano nulla. Per la prima integrazione conviene partire da una ricerca circoscritta: permette di esaminare l'intero percorso senza introdurre una transazione da annullare in caso di errore.

Function calling e tool calling: che differenza c'è?
Quando si parla di funzioni personalizzate, i due termini indicano spesso lo stesso meccanismo. OpenAI li usa entrambi; anche Anthropic precisa che il tool use viene chiamato function calling. Il termine più ampio «strumento» può comprendere sia funzionalità ospitate dal fornitore sia funzioni implementate da chi sviluppa. Conta soprattutto dove avviene l'esecuzione: se la funzione interroga il proprio database, è l'applicazione a doverla eseguire.
La documentazione dei tre fornitori, esaminata più avanti, rende concreta questa distinzione. I formati dei messaggi cambiano: per ogni fornitore, richiesta e risultato devono usare le rispettive strutture previste dalla stessa API.
Un esempio di function calling dall'inizio alla fine
Si espone una funzione per consultare lo stato di un ordine e si restituisce soltanto lo stato fornito dal database. L'esempio seguente usa un ordine volutamente inventato, ORD-1042, in un database SQLite locale. È un dato di prova a scopo didattico, non un record di un cliente né una risposta effettivamente osservata del modello.
Function calling con OpenAI: eseguire il ciclo completo
La documentazione sul function calling di OpenAI illustra il ciclo della Responses API: dichiarare la funzione, ricevere un function_call, eseguire il codice e restituire un function_call_output con lo stesso call_id.
Installare l'SDK Python con python -m pip install openai, impostare OPENAI_API_KEY nell'ambiente e salvare il codice in order_tool.py. L'esempio usa gpt-6-astra, il modello presente nell'esempio della Responses API nella documentazione attuale; questo non significa che sia l'unico modello adatto. Lo script invia richieste reali all'API, mentre il database rimane in memoria.
import json
import re
import sqlite3
from openai import OpenAI
client = OpenAI()
model = "gpt-6-astra"
# Demo identity. In a web app, obtain this from your authenticated session.
tenant_id = "tenant_demo"
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE orders (tenant_id TEXT, order_id TEXT, status TEXT)")
db.execute(
"INSERT INTO orders VALUES (?, ?, ?)",
(tenant_id, "ORD-1042", "shipped"),
)
# The tool schema: a name, a purpose, and an input contract.
tool = {
"type": "function",
"name": "get_order_status",
"description": (
"Look up the current status of an existing order by order ID. "
"Use for status questions, not refunds or address changes. "
"Returns the order ID and status, or an error."
),
"strict": True,
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID supplied by the user, such as ORD-1042.",
}
},
"required": ["order_id"],
"additionalProperties": False,
},
}
def execute(name, raw_arguments):
if name != "get_order_status":
return {"error": "unknown_tool"}
try:
args = json.loads(raw_arguments)
except (json.JSONDecodeError, TypeError):
return {"error": "invalid_arguments"}
if not isinstance(args, dict) or set(args) != {"order_id"}:
return {"error": "invalid_arguments"}
order_id = args["order_id"]
if not isinstance(order_id, str) or not re.fullmatch(r"ORD-[0-9]+", order_id):
return {"error": "invalid_order_id"}
row = db.execute(
"SELECT status FROM orders WHERE tenant_id = ? AND order_id = ?",
(tenant_id, order_id),
).fetchone()
if row is None:
return {"error": "order_not_found"}
return {"order_id": order_id, "status": row[0]}
history = [{"role": "user", "content": "What is the status of order ORD-1042?"}]
response = client.responses.create(
model=model,
input=history,
tools=[tool],
tool_choice={"type": "function", "name": "get_order_status"},
parallel_tool_calls=False,
)
# Keep every output item, including any reasoning items.
history.extend(response.output)
calls = [item for item in response.output if item.type == "function_call"]
if len(calls) != 1:
raise RuntimeError("Expected one tool call; inspect the response before continuing.")
call = calls[0]
result = execute(call.name, call.arguments)
history.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result),
})
answer = client.responses.create(
model=model,
input=history,
tools=[tool],
tool_choice="none",
instructions="Answer from the tool result. If it contains an error, report the failure.",
)
print(answer.output_text)
db.close()In caso di successo, questo esempio, volutamente circoscritto, esegue 2 richieste al modello e 1 ricerca nel database. La prima richiesta impone la ricerca prevista; la seconda disabilita ulteriori chiamate a strumenti. In questo modo il meccanismo è facile da seguire. Un assistente generico dovrebbe anche gestire richieste per cui nessuno strumento è adatto, mancano informazioni oppure serve un'altra ricerca.
Il tenant_id non compare nello schema dello strumento perché l'applicazione sa già chi ha effettuato l'accesso. La query al database usa questa identità attendibile insieme all'ID dell'ordine richiesto. Affidare l'identità a un argomento fornito dal modello eliminerebbe proprio la separazione che l'esempio vuole insegnare.
La chiamata del modello e il risultato da restituire
La richiesta e il risultato sono messaggi distinti, collegati da un identificativo della chiamata. Una chiamata tipica avrebbe questo aspetto. Identificativi e valori sono illustrativi: non si tratta di una risposta acquisita dall'API.
{
"type": "function_call",
"id": "fc_demo",
"call_id": "call_demo",
"name": "get_order_status",
"arguments": "{\"order_id\":\"ORD-1042\"}"
}Quando l'handler restituisce lo stato dell'ordine di prova, l'applicazione aggiunge questo risultato. Con una risposta reale bisogna copiare il valore effettivo di call.call_id, senza mai inventarne uno nuovo.
{
"type": "function_call_output",
"call_id": "call_demo",
"output": "{\"order_id\":\"ORD-1042\",\"status\":\"shipped\"}"
}Ora il modello dispone dei dati per rispondere, per esempio: «L'ordine ORD-1042 è stato spedito». Lo stato proviene dal database. Il contributo del modello consiste nel tradurre la richiesta in argomenti e i dati restituiti in una risposta.
Lo stesso meccanismo nella documentazione dei tre fornitori
Il modello di esecuzione è comune ai vari fornitori; il formato dei messaggi non lo è. La tabella seguente, verificata sulla documentazione online dei fornitori il 17 ottobre 2026, distingue gli esempi attuali senza trattare come intercambiabili campi dai nomi simili.
Il tool use di Anthropic
La documentazione sul tool use di Anthropic definisce gli input della funzione tramite input_schema. Per uno strumento lato client, il codice gestisce il blocco tool_use dell'assistente e invia un tool_result all'interno del successivo messaggio utente, mantenendo nella conversazione il messaggio dell'assistente.
La documentazione chiarisce la divisione dei compiti: gli strumenti lato client vengono eseguiti nell'applicazione, quelli lato server sull'infrastruttura di Anthropic. La presenza di uno strumento di ricerca ospitato dal fornitore non dimostra quindi che Claude possa eseguire direttamente una funzione sul proprio database privato. Quell'implementazione rimane a carico di chi sviluppa.
Il function calling di Gemini
Anche la documentazione sul function calling di Gemini di Google separa la richiesta del modello dall'esecuzione nell'applicazione. La guida attuale usa l'Interactions API, con passaggi function_call e function_result e il campo previous_interaction_id per proseguire l'interazione.
Occorre seguire l'esempio relativo all'API che si sta usando. Copiare una dichiarazione da un'API e un messaggio di risultato da un'altra non rende il codice portabile. Si mantiene l'operazione comune, per esempio get_order_status, adattando insieme la dichiarazione e la struttura del risultato.
Perché le chiamate agli strumenti fanno funzionare il ciclo di un agente
Un agente che usa strumenti sceglie un'operazione, ne riceve il risultato e decide come proseguire, ripetendo il ciclo. Il function calling collega quella decisione a un'operazione che il software può eseguire.
Un assistente per gli ordini potrebbe, per esempio, cercare una spedizione, esaminare il riferimento del corriere restituito e poi richiedere i dati di tracciamento prima di rispondere. La seconda operazione dipende dal risultato della prima. Questo ciclo, basato sui risultati delle operazioni, è più utile che chiedere al modello di inventare in anticipo tutti i passaggi.
Le indicazioni di Anthropic sull'architettura degli agenti distinguono i workflow prestabiliti dagli agenti che decidono autonomamente come usare gli strumenti. Descrivono inoltre i risultati restituiti dagli strumenti e le condizioni di arresto come parti del ciclo.
Definirlo «la base di ogni agente» è una scorciatoia utile solo fino a un certo punto. La documentazione non dimostra che ogni sistema chiamato agente usi una specifica API di function calling. Il concetto che resta valido è il ciclo tra modello e azione. Una singola ricerca può rimanere un workflow prestabilito: non ha bisogno di un pianificatore autonomo.
Tre problemi che emergono in produzione
Qualità dello schema, scelta dello strumento e convalida degli argomenti sono problemi distinti. Risolverne uno non dimostra che anche gli altri siano risolti.
Schemi ambigui: il contratto si presta a interpretazioni sbagliate
Un campo chiamato id non chiarisce al modello se serve il numero di un ordine, l'identificativo di un cliente o il riferimento di una spedizione. Una stringa senza vincoli chiamata action lascia spazio a valori che l'handler potrebbe non riconoscere. Prima di aggiungere altre istruzioni, conviene migliorare il contratto.
Usare nomi precisi per i parametri, descriverne il significato e ricorrere a un'enumerazione quando sono ammessi soltanto valori di un insieme prestabilito. Il primo strumento dovrebbe avere un ambito abbastanza ristretto da poter essere descritto in una frase semplice. Nell'esempio, order_id è esplicito e la descrizione esclude rimborsi e modifiche dell'indirizzo.
La modalità strict di OpenAI richiede additionalProperties: false sugli oggetti e la presenza di tutte le proprietà in required; per un valore che può mancare si può ammettere null. La guida documenta questi requisiti della modalità strict. Anche Google documenta restrizioni sugli schemi: uno schema accettato dal validatore locale non viene automaticamente accettato da tutte le API dei modelli.
Il limite: uno schema può vincolare la rappresentazione dei dati, ma un ID valido può comunque riferirsi all'ordine sbagliato. Questo controllo spetta all'applicazione.
Il modello sceglie lo strumento sbagliato: argomenti validi, operazione errata
Se un cliente chiede «A che punto è il mio rimborso?», serve una consultazione dello stato, non l'avvio di un nuovo rimborso. Se entrambi gli strumenti sono descritti con «gestisce i rimborsi», la scelta diventa ambigua.
Descrivere quando ciascuno strumento va usato e quando va evitato, rendendo disponibili soltanto le operazioni appropriate per il compito e per l'utente. Le indicazioni di Anthropic sulla definizione degli strumenti raccomandano espressamente di spiegare scopo, significato dei parametri, ambito di utilizzo e limiti.
Quando l'applicazione sa già quale operazione deve essere eseguita, si può usare un controllo supportato per la scelta dello strumento oppure chiamare direttamente l'operazione. L'esempio impone una ricerca nota per ragioni didattiche; da questo non deriva che «ogni messaggio utente deve attivare uno strumento». Un saluto o una domanda incompleta su un ordine possono richiedere invece una risposta nella conversazione.
Il limite: argomenti ben formati non dicono se il modello ha scelto l'azione corretta sul piano operativo. La scelta dello strumento va valutata su richieste rappresentative degli utenti, compresi i casi in cui non dovrebbe partire alcuna chiamata.
Argomenti non convalidati: il formato è corretto, l'accesso no
L'handler è un punto di ingresso dei dati. Occorre verificare che la funzione appartenga a una lista esplicita di funzioni consentite, analizzare gli argomenti, convalidarne struttura e significato e applicare i controlli di accesso usando l'identità autenticata dall'applicazione.
Nell'esempio degli ordini, un caso negativo utile da provare è un ordine appartenente a un altro tenant, cioè a un altro account cliente. La query non deve restituirlo, anche se il suo identificativo è una stringa perfettamente valida. Altre prove comprendono un ID ordine mancante, un campo inatteso e la richiesta di un'operazione che l'handler non implementa.
La panoramica di Anthropic osserva che, quando mancano informazioni, il modello può dedurre i valori degli argomenti. La presenza di un campo obbligatorio non dimostra che sia stato l'utente a fornirlo. Per un'azione con conseguenze rilevanti, confrontare l'operazione richiesta e i suoi input con ciò che l'utente ha effettivamente autorizzato.
Quando l'esecuzione fallisce, restituire un errore riconoscibile. Un timeout non dimostra che l'operazione non sia mai avvenuta: prima di ritentare una scrittura, usare un meccanismo di idempotenza a livello applicativo, capace di riconoscere la stessa operazione prevista ed evitare di ripeterne l'effetto. Sono raccomandazioni di implementazione, non garanzie offerte dall'API del modello.
Quando bastano gli output strutturati?
Gli output strutturati servono quando la risposta deve rispettare un formato usando informazioni già disponibili. Uno strumento serve quando occorre un'operazione esterna. Un classificatore di messaggi di assistenza che restituisce categoria e urgenza può produrre un output strutturato. Un assistente che ha bisogno dello stato attuale di un ordine deve invece ottenerlo tramite una ricerca.
La guida a Structured Outputs di OpenAI distingue proprio tra collegare funzionalità dell'applicazione e formattare la risposta del modello. Spiega inoltre che gli schemi di funzione in modalità strict si basano su Structured Outputs. Sono capacità complementari, non prodotti che si escludono a vicenda.

Supponiamo che il record dell'ordine sia già stato fornito e che all'interfaccia serva soltanto {order_id, status, explanation}. Basta una risposta strutturata. Se il record manca o va aggiornato, occorre prima eseguire la ricerca. Anche la risposta finale può poi essere strutturata.
Un oggetto JSON che dichiara l'invio di un'email rimane un semplice oggetto di output. Per inviare l'email serve un'operazione effettivamente eseguita, con un risultato verificabile.
Function calling e MCP: a cosa servono?
Il function calling gestisce la richiesta di un'operazione da parte del modello. Model Context Protocol (MCP) offre un'interfaccia condivisa per rendere disponibili strumenti e altri elementi di contesto alle applicazioni compatibili. Una funzione può essere collegata direttamente a un'applicazione prima di introdurre un server separato per gli strumenti.
MCP e tool calling: quando conviene condividere gli strumenti?
Se più applicazioni hanno bisogno della stessa ricerca sugli ordini, un server MCP può rendere lo strumento individuabile e invocabile. La documentazione sull'architettura di MCP descrive tools/list per individuare gli strumenti e tools/call per richiederne l'esecuzione. Il server ha comunque bisogno di un'implementazione e di controlli di accesso.
Per approfondire questo passaggio, si può leggere che cos'è MCP e come si collegano host, client e server. Una volta funzionante la ricerca diretta, la guida all'implementazione di un server MCP mostra come rendere riutilizzabili le funzionalità. Introdurre MCP non elimina la necessità di convalidare l'operazione richiesta.
Dove le promesse superano i fatti
Superare la convalida dello schema dimostra il rispetto di un vincolo di formato, non il completamento di un compito. L'operazione potrebbe essere inappropriata, il record inaccessibile o il servizio sottostante indisponibile. Una frase finale scorrevole non chiarisce nessuno di questi aspetti.
Nemmeno un elenco di strumenti basta, da solo, a creare un agente. Servono ancora regole per stabilire quali strumenti siano disponibili, come inserire i risultati nella conversazione, quando fermarsi e cosa fare dopo un errore. Un framework può fornire un'implementazione di questi meccanismi, ma la politica di esecuzione rimane una scelta progettuale.
Il primo traguardo utile è circoscritto: la richiesta raggiunge l'handler previsto, vengono restituiti soltanto dati accessibili all'utente e la risposta rispecchia il risultato. Prima di ampliare i poteri del sistema, bisogna rendere osservabile questo percorso.
Da quale operazione partire in produzione?
Iniziare con un'operazione di sola lettura che risponda a una domanda ricorrente degli utenti. L'esempio dello stato di un ordine è un buon modello perché il risultato atteso e i limiti di accesso sono facili da verificare.
Per chi sviluppa, ha senso procedere subito se la risposta dipende da dati che l'applicazione può recuperare in modo affidabile. Mantenere un workflow prestabilito quando l'operazione successiva è già nota. Prima di introdurre scritture scelte dal modello, servono permessi espliciti, gestione degli errori e un modo per evitare effetti ripetuti.
Per chi gestisce il sistema, conta verificare che cosa è stato eseguito e quale risultato è tornato. Registrare lo strumento scelto, l'esito della convalida, lo stato dell'esecuzione e la correlazione tra chiamata e risultato, tenendo gli argomenti sensibili fuori dai log ordinari. Impostare timeout e un numero massimo di cicli di chiamata agli strumenti adeguati al compito; il ciclo della conversazione deve avere un limite.
Per chi acquista, chiedere al team di implementazione di mostrare una ricerca a cui è stato negato l'accesso e un'operazione fallita, oltre a una risposta riuscita. Se il prodotto si limita a riassumere informazioni già fornite, il function calling può essere superfluo. Possono bastare un output strutturato o la normale generazione di testo.
Il primo passo concreto per lunedì: implementare una ricerca circoscritta, provare con un record accessibile, uno inaccessibile e una richiesta incompleta, quindi esaminare in ciascun caso il risultato restituito al modello. Ampliare l'elenco degli strumenti soltanto quando è chiaro perché ogni richiesta è stata accettata o rifiutata.
Domande frequenti
Che cos'è il function calling?
È il meccanismo con cui un modello richiede un'operazione indicando il nome di una funzione e fornendo gli argomenti. Per una funzione definita da chi sviluppa, l'applicazione la esegue e ne reinserisce il risultato nella conversazione.
Che differenza c'è tra tool calling e function calling negli LLM?
Per le funzioni personalizzate, i fornitori usano spesso i termini come sinonimi. Il tool use può comprendere anche funzionalità ospitate dal fornitore: bisogna quindi verificare dove avviene l'esecuzione, senza dedurlo dal nome.
Qual è un esempio di chiamata a una funzione?
Un modello richiede get_order_status con order_id impostato su ORD-1042. Il codice convalida la richiesta, verifica l'accesso, cerca l'ordine e ne restituisce lo stato. L'ordine e lo stato presenti in questo articolo sono dati inventati per l'esempio.
Come si usa il function calling con GPT-5?
Si segue il flusso documentato della Responses API con un modello compatibile disponibile nel proprio progetto: inviare le definizioni degli strumenti, gestire gli elementi di output delle chiamate a funzione e restituire i risultati con gli identificativi di chiamata corrispondenti. Nel proseguire la conversazione, mantenere gli elementi di ragionamento. L'esempio eseguibile riportato sopra usa il modello mostrato nella guida attuale, senza dare per scontato che uno specifico modello GPT-5 sia abilitato nell'account.
Per guide pratiche allo sviluppo con l'AI, iscriviti alla newsletter.
- Pubblicato
- Categoria
- Build
- Lingua










