Function Calling con LLM nel 2026: OpenAI, Anthropic e Google a Confronto

Confronto pratico tra function calling su OpenAI, Anthropic e Google nel 2026: strict mode, parallel calls, gestione errori, valutazioni e migrazione degli schemi.

Function Calling 2026: GPT, Claude, Gemini

Aggiornato: 30 giugno 2026

Il function calling con LLM e' il meccanismo che permette a un modello come Claude, GPT-5 o Gemini di richiedere l'esecuzione di funzioni esterne restituendo un oggetto JSON strutturato invece di testo libero. Nel 2026 e' diventato lo standard de facto per costruire agenti affidabili, ma le differenze tra vendor su strict mode, parallel calls e gestione degli errori possono trasformare un prototipo elegante in un sistema fragile in produzione. In questa guida confronto le tre principali implementazioni e racconto le best practice che applico nei miei deploy.

  • Anthropic, OpenAI e Google espongono function calling con API simili ma con differenze critiche su strict, schemi JSON e blocchi paralleli.
  • Lo schema-first design con additionalProperties: false e required espliciti elimina circa l'80% degli errori di parsing in produzione.
  • Il parallel tool calling e' attivo di default su tutti e tre i vendor nel 2026; va gestito ritornando tutti i risultati in un singolo messaggio utente.
  • Le valutazioni dedicate (tool selection accuracy, argument validity, end-to-end task success) sono indispensabili prima di ogni deploy.
  • Gli errori dei tool vanno restituiti con is_error: true (Anthropic) o come messaggio di ruolo tool con campo error (OpenAI), mai silenziati.
  • MCP (Model Context Protocol) sta unificando la distribuzione dei tool, ma non sostituisce la disciplina di schema design.

Cos'e' il function calling negli LLM

Il function calling, chiamato anche tool use da Anthropic, e' il protocollo con cui un LLM segnala l'intenzione di invocare una funzione esterna. Il modello non esegue codice: produce un blocco strutturato con il nome della funzione e gli argomenti, l'applicazione esegue la funzione, e il risultato viene rinviato al modello per la prosecuzione del ragionamento. Questo loop e' la spina dorsale di praticamente ogni agente di produzione che ho costruito negli ultimi due anni.

Tecnicamente si tratta di un'estensione della Messages API (o Chat Completions): il modello, vincolato da uno schema JSON che descrive i parametri attesi, restituisce un blocco tool_use con un campo input validato. La svolta del 2024-2025 e' stata l'introduzione dello strict mode, che garantisce a livello di server che l'output rispetti esattamente lo schema fornito. Prima di strict mode, validare l'output era responsabilita' del client; oggi e' una garanzia API.

La differenza tra un sistema che usa function calling come novelty e uno che lo usa come fondamento e' enorme. Nel primo caso vedi prototipi che impressionano in demo ma falliscono al primo edge case; nel secondo vedi agenti che gestiscono migliaia di task al giorno senza intervento umano. Il discrimine non e' il modello scelto, ma la disciplina nella definizione degli schemi e nella valutazione del comportamento. Per una panoramica piu' ampia su come orchestrare questi tool dentro un agente vero e proprio, ho scritto una guida pratica agli agenti IA nel 2026 che copre LangGraph, CrewAI e i design pattern fondamentali.

Confronto OpenAI, Anthropic e Google nel 2026

I tre vendor principali implementano function calling con filosofie diverse. La tabella qui sotto riassume le differenze che incontro piu' spesso durante l'integrazione. Le specifiche cambiano spesso, quindi verifica sempre la documentazione ufficiale Anthropic, la guida OpenAI e le specifiche Gemini prima di un rilascio.

CaratteristicaAnthropic (Claude 4.x)OpenAI (GPT-5)Google (Gemini 2)
Parametro definizione tooltools[].input_schematools[].function.parameterstools[].function_declarations
Strict modestrict: true sul toolstrict: true sulla functionresponse_schema con vincoli
Parallel tool callsAttivo di defaultAttivo di defaultAttivo di default
Disabilitazione paralleldisable_parallel_tool_use: trueparallel_tool_calls: falseConfigurazione tool config
Messaggio risultatoBlocco tool_result in ruolo userMessaggio con ruolo toolParte function_response
Identificatore correlazionetool_use_idtool_call_idNome funzione
Schema JSON supportatoJSON Schema completoSottoinsieme JSON SchemaOpenAPI 3 schema

La differenza piu' insidiosa e' la shape del messaggio di risultato. Anthropic incapsula i risultati dentro un messaggio utente con blocchi tool_result, mentre OpenAI introduce un ruolo dedicato tool. Uno strato di astrazione che non normalizza questa differenza diventa rapidamente un punto di rottura quando si cambia provider. Ho visto team perdere settimane a debuggare loop di tool che funzionavano su un provider e si bloccavano sull'altro per una correlazione di ID gestita male.

Un'altra trappola comune: il sottoinsieme di JSON Schema accettato da OpenAI in strict mode e' piu' restrittivo. oneOf, vincoli numerici (minimum, maximum) e pattern regex su stringhe sono spesso ignorati o producono errori. Anthropic accetta JSON Schema completo ma applica una validazione piu' rilassata se strict non e' impostato. Documenta i vincoli che il modello rispetta davvero, non quelli che lo schema dichiara.

Come definire schemi di tool affidabili

Lo schema e' il contratto tra il modello e la tua applicazione. Nei sistemi che ho messo in produzione, l'investimento maggiore non e' mai stato sul prompt: e' stato sulla definizione degli schemi. Ecco le regole che applico senza eccezioni.

Regola 1: additionalProperties false ovunque

Senza questa proprieta' il modello puo' aggiungere campi inventati. Anche con strict mode disattivato, i provider tendono a essere piu' conservativi quando lo vedono. Per Anthropic strict mode lo richiede esplicitamente.

{
  "name": "search_customer_orders",
  "description": "Cerca ordini per ID cliente con filtri opzionali su data e stato.",
  "strict": true,
  "input_schema": {
    "type": "object",
    "properties": {
      "customer_id": {
        "type": "string",
        "description": "ID univoco del cliente, formato CUS-XXXXXX."
      },
      "status": {
        "type": "string",
        "enum": ["pending", "shipped", "delivered", "cancelled"],
        "description": "Stato dell'ordine da filtrare."
      },
      "since_date": {
        "type": "string",
        "format": "date",
        "description": "Data minima ISO 8601 (es. 2026-01-15)."
      }
    },
    "required": ["customer_id", "status", "since_date"],
    "additionalProperties": false
  }
}

Regola 2: tutti i campi in required, usa null per gli opzionali

Strict mode di OpenAI rifiuta schemi con campi non presenti in required. La soluzione idiomatica e' dichiarare tutto come required e accettare null per i campi opzionali tramite "type": ["string", "null"]. Anthropic non lo richiede ma il pattern e' comunque sano: rende esplicito al modello quali campi puo' omettere senza ambiguita'.

Regola 3: descrizioni come prompt, non come commenti

Le descrizioni dei tool e dei parametri vengono iniettate nel contesto del modello. Una buona descrizione include: cosa fa il tool, quando usarlo, quando NON usarlo, e un esempio dell'output atteso. Le descrizioni dei parametri devono indicare formato, vincoli e cosa succede con valori mancanti. Una descrizione vaga produce tool call vaghe. Onestamente, investo nelle descrizioni piu' che nel system prompt principale, perche' arrivano al modello con maggior peso semantico.

Regola 4: enum invece di stringhe libere

Ogni volta che un parametro ha un dominio noto e finito, usa enum. Riduce le allucinazioni e rende le evals deterministiche. "language": "italiano" e "language": "Italiano" sono due valori diversi senza un enum, e il modello alternera' le forme in modo imprevedibile. Per gli ID, considera pattern regex via format quando supportati, altrimenti documenta il formato nella description e valida client-side.

Parallel tool calling: quando e come usarlo

Nel 2026 tutti e tre i vendor principali emettono di default piu' chiamate a tool in parallelo quando il modello giudica che siano indipendenti. Su Claude Opus 4.8 e' normale vedere 3-5 tool_use nello stesso messaggio assistente quando l'utente chiede "controlla meteo, traffico e calendario per domani mattina". Questo riduce drasticamente la latenza percepita, ma richiede una gestione corretta lato client.

Il pattern corretto di gestione e':

  1. Estrarre tutti i blocchi tool_use dalla risposta.
  2. Eseguire le funzioni in parallelo (asyncio.gather, Promise.all, ecc.).
  3. Restituire tutti i tool_result in un unico messaggio utente. Splittare i risultati in messaggi separati addestra implicitamente il modello a smettere di chiamare tool in parallelo nelle interazioni future.
import anthropic
import asyncio

client = anthropic.AsyncAnthropic()

async def execute_tool(tool_use):
    if tool_use.name == "get_weather":
        return await weather_api(tool_use.input["city"])
    elif tool_use.name == "get_traffic":
        return await traffic_api(tool_use.input["route"])
    elif tool_use.name == "get_calendar":
        return await calendar_api(tool_use.input["date"])

async def run_turn(messages, tools):
    response = await client.messages.create(
        model="claude-opus-4-8",
        max_tokens=4096,
        tools=tools,
        messages=messages,
    )
    tool_uses = [b for b in response.content if b.type == "tool_use"]
    if not tool_uses:
        return response

    # Esegui tutti i tool in parallelo
    results = await asyncio.gather(*[execute_tool(t) for t in tool_uses])

    # Aggiungi il messaggio assistente e tutti i risultati in UN solo messaggio user
    messages.append({"role": "assistant", "content": response.content})
    messages.append({
        "role": "user",
        "content": [
            {"type": "tool_result", "tool_use_id": t.id, "content": str(r)}
            for t, r in zip(tool_uses, results)
        ],
    })
    return await run_turn(messages, tools)

Disabilita il parallelismo solo se le tue funzioni hanno effetti collaterali ordinati o se stai facendo debug. In produzione, parallel calling riduce la latenza percepita del 40-60% in workflow tipici, e il modello sceglie quasi sempre correttamente quali chiamate siano indipendenti.

Gestione degli errori nei tool use

L'errore piu' frequente che vedo nelle review di codice agentico e' il silent failure: il tool fallisce, il client restituisce una stringa vuota o un errore generico, e il modello continua come se nulla fosse. Il modello non puo' recuperare se non sa che cosa e' andato storto. Ho una regola ferrea: ogni fallimento di tool e' un'opportunita' di steering, non un disastro.

Su Anthropic, segnala gli errori con is_error: true sul tool_result e includi un messaggio diagnostico utilizzabile:

{
  "type": "tool_result",
  "tool_use_id": "toolu_01ABC...",
  "content": "Errore: customer_id 'CUS-999999' non trovato. Verifica il formato (CUS- seguito da 6 cifre) o usa search_customer_by_email se hai solo l'email.",
  "is_error": true
}

Notare il pattern: che cosa e' fallito, perche', quale alternativa provare. Su Claude Sonnet 4.6 e successivi, questo formato aumenta il recovery rate (tool call corretta al tentativo successivo) dal 30% al 75% nei miei benchmark interni. Il modello riesce a leggere il messaggio di errore e adattare la chiamata successiva di conseguenza.

Su OpenAI, il pattern equivalente e' restituire il messaggio di errore come contenuto del messaggio con ruolo tool, includendo idealmente un campo error strutturato in JSON. Su Gemini, il function_response accetta un oggetto con campo error dedicato. Le filosofie sono leggermente diverse ma il principio e' lo stesso: parla al modello, non al tuo logger.

Valutare gli agenti basati su function calling

Ho un'opinione forte sulle evals: senza un set di valutazione dedicato, non stai costruendo un sistema, stai facendo demo. Per i sistemi function-calling misuro almeno quattro metriche distinte, e nessun deploy esce senza un report aggiornato su tutte e quattro.

1. Tool selection accuracy

Dato un input, il modello sceglie il tool giusto? Costruisco un dataset di 100-500 esempi con il tool corretto annotato. La metrica e' semplicemente l'accuratezza, ma stratificata: per categoria di tool, per ambiguita' dell'input, per presenza di parametri opzionali. Una tool selection accuracy globale del 95% puo' nascondere un 60% su una sotto-categoria critica. Stratificare e' la differenza tra una metrica che racconta una bugia rassicurante e una che ti dice dove devi intervenire.

2. Argument validity

Anche con il tool giusto, gli argomenti sono validi rispetto allo schema? Strict mode dovrebbe garantirlo a livello di tipo, ma non a livello semantico. since_date: "2026-13-45" e' sintatticamente una stringa valida ma semanticamente assurda. Valuto con un validatore custom che applica i vincoli semantici reali, non solo lo schema JSON.

3. End-to-end task success

L'utente ha ottenuto quello che voleva? Questa e' la metrica piu' importante e la piu' costosa da misurare. Spesso richiede LLM-as-a-judge o annotazione umana. La misuro su un sottoinsieme rappresentativo (100-200 task) prima di ogni release significativa.

4. Recovery rate

Quando un tool fallisce, il modello recupera? Inietto artificialmente errori nei tool e misuro se il modello riprova con argomenti corretti, sceglie un tool alternativo o chiede chiarimenti all'utente. Un sistema affidabile ha un recovery rate sopra il 70%. Questa metrica spesso correla con la qualita' dei messaggi di errore: migliorare l'una migliora l'altra.

Per implementare questo stack di valutazione, framework come Promptfoo, DeepEval e Langfuse sono diventati il riferimento. Ne parlo in dettaglio nella mia guida all'osservabilita' LLM, che copre anche il tracing distribuito necessario per debuggare loop di tool call complessi.

Migrazione e versionamento degli schemi

Gli schemi dei tool evolvono. Un campo che oggi e' opzionale diventa obbligatorio, un enum si arricchisce di nuovi valori, una firma cambia per supportare un caso d'uso nuovo. Senza versionamento esplicito, queste modifiche rompono i deploy in maniera silenziosa, perche' il modello continuera' a chiamare il tool con la firma vecchia finche' non viene re-instradato dal contesto.

Applico tre regole:

  1. Schemi come codice, con versionamento semantico. Ogni tool ha una versione (v1, v2). Modifiche backward-compatible (nuovi campi opzionali, nuovi enum) bump della minor. Breaking change bump della major e nuova registrazione del tool come entita' separata.
  2. Test di regressione sugli schemi. Per ogni schema mantengo un set di "tool call canoniche", cioe' input dell'utente per cui il modello dovrebbe produrre un tool call specifico. Le rieseguo ad ogni rilascio, prima di promuovere la build.
  3. Migrazione graduale, non flag-day. Quando aggiungo un parametro required, lo introduco prima come opzionale con un valore di default sensato, monitoro l'utilizzo reale per 2-4 settimane, poi promuovo a required. Cosi' eviti il classico bug del deploy del venerdi' pomeriggio.

Vale la pena ricordare che modificare uno schema invalida la prompt cache di Anthropic e OpenAI. Se hai un sistema sotto carico con un alto tasso di cache hit, modifiche frequenti agli schemi diventano costose. Pianifica le release, raggruppa le modifiche e considera il timing rispetto ai picchi di traffico. Per i lettori che condividono tool tra piu' agenti tramite Model Context Protocol, la stessa disciplina vale doppio: ogni breaking change si propaga a tutti i client collegati.

MCP e il futuro della distribuzione dei tool

Il Model Context Protocol (MCP), introdotto da Anthropic alla fine del 2024 e adottato dai principali vendor nel corso del 2025, sta cambiando il modo in cui i tool vengono distribuiti e consumati. Invece di definire tool inline nell'applicazione client, MCP permette di esporli da server remoti che il modello puo' collegare on-demand. Questo abilita ecosistemi di tool riusabili e marketplace di capability dedicate.

Questo non sostituisce le best practice di schema design, anzi le rende piu' importanti, perche' lo schema diventa un contratto pubblico tra il server MCP e qualsiasi client. Per chi vuole approfondire il deploy in produzione, la guida MCP in produzione 2026 copre Streamable HTTP, OAuth 2.1 e i gateway che sto vedendo emergere come standard.

La mia previsione: nel 2027 vedremo function calling diventare quasi invisibile a livello applicativo, sostituito da agenti che scoprono e legano tool MCP dinamicamente. Ma la disciplina di schema-first design e le evals dedicate resteranno la differenza tra un agente che funziona in demo e uno che funziona in produzione. Le mode passano; i contratti ben definiti restano.

Domande frequenti

Qual e' la differenza tra function calling e tool use?

Sono lo stesso concetto. OpenAI ha coniato il termine "function calling" nel 2023; Anthropic preferisce "tool use" perche' include anche tool nativi come bash e text editor che non sono propriamente "funzioni" definite dall'utente. Tecnicamente l'interfaccia e' equivalente: schema JSON in input, blocco strutturato in output.

Quando devo usare strict mode nel function calling?

Sempre, in produzione. L'unico motivo per disattivarlo e' se il tuo schema usa costrutti JSON Schema non supportati dal provider in strict mode (su OpenAI: oneOf, alcuni vincoli numerici, pattern regex). Anche in quel caso, e' meglio semplificare lo schema e attivare strict che lasciarlo off.

Posso mischiare tool sincroni e asincroni nello stesso turno?

Si'. Il modello non ha visibilita' su come eseguirai i tool. Puoi gestire alcuni tool con chiamate sincrone (lookup veloci in cache) e altri con job asincroni che ritornano un ID di tracking, da rinviare al modello solo quando completati. Il pattern comune e' bloccare l'esecuzione finche' tutti i tool del turno non sono completati, poi rinviare i risultati insieme.

Come gestisco i timeout nei tool che chiamano API esterne?

Imposta un timeout client-side e, se scade, restituisci un tool_result con is_error: true e un messaggio del tipo "Timeout dopo 30s. Riprova o usa cached_search per risultati approssimati". Mai bloccare il modello in attesa indefinita: il loop di un agente che si pianta su un tool e' il problema numero uno di affidabilita' che vedo nelle revisioni.

Quanti tool posso definire nello stesso request?

I limiti tecnici sono alti (Anthropic supporta fino a 128 tool per request, OpenAI similmente), ma il problema pratico e' la qualita' della selezione. Oltre i 20-30 tool osservo un calo significativo di tool selection accuracy. La soluzione e' il tool search dinamico (server-side su Anthropic, o pattern client-side basati su embedding) che carica solo i tool rilevanti per la query.

Daichi Watanabe
Sull'Autore Daichi Watanabe

LLM integration specialist with a strong opinion about function calling and an even stronger one about evaluations.