Uso degli strumenti / Function calling
L'uso degli strumenti consente a Claude di chiamare funzioni che tu definisci — una ricerca, una calcolatrice, il tuo database, qualsiasi API — e di usarne i risultati. È il fondamento di ogni agente.
La maggior parte dei tutorial sull'uso degli strumenti si ferma dopo il loop in quattro passi. Il loop è la parte facile. Ciò che manda davvero in crisi le integrazioni in produzione è più specifico e meno discusso: le forme di messaggio che restituiscono 400, il campo d'errore che permette a Claude di recuperare invece di scusarsi, e il fatto che forzare una chiamata a uno strumento non funziona più sui modelli Claude più recenti. Questa pagina copre tutto questo.
- Come funziona il loop agentico in quattro passi, dalle definizioni degli strumenti alla risposta finale
- Come scrivere una definizione di strumento — e descrizioni che indirizzano davvero quando Claude lo chiama
- Le tre regole sulla forma dei messaggi che restituiscono un 400, e il testo d'errore che ti dice quale hai violato
- Perché tool_choice "any" e "tool" ora falliscono sui modelli più recenti, e i due sostituti
- Come garantire input degli strumenti conformi allo schema con strict: true
- Perché i risultati degli strumenti sono il punto in assoluto più probabile in cui una prompt injection raggiunge il tuo agente
Il loop
L'uso degli strumenti è una conversazione, non una singola chiamata. Offri a Claude un menu di strumenti; Claude ne sceglie uno e si ferma; tu lo esegui e riferisci; Claude integra il risultato nella sua risposta — ripetendo il ciclo se necessario.
- Includi una lista di definizioni di strumenti — ciascuna con un nome, una descrizione e un input in JSON Schema.
- Se Claude decide di usarne uno, la risposta torna con stop_reason "tool_use" e uno o più blocchi tool_use, ciascuno con un id, un name e un oggetto input.
- Esegui tu stesso lo strumento e rinvii l'output come tool_result che fa riferimento all'id del tool_use.
- Claude continua, eventualmente chiamando altri strumenti, finché non risponde con uno stop_reason normale.
Nota il plurale al passo 2: Claude può restituire più blocchi tool_use in una singola risposta. Smista in base al campo type di ciascun blocco invece di dare per scontato content[0] — una risposta di solito apre con un blocco text, e sui modelli più recenti può aprire invece con blocchi thinking.
Definire uno strumento
Una definizione di strumento è un nome, una descrizione in linguaggio naturale e un JSON Schema per l'input.
| Campo | Obbligatorio | Note |
|---|---|---|
name | sì | Deve corrispondere alla regex ^[a-zA-Z0-9_-]{1,128}$ |
description | sì | Cosa fa, quando usarlo, quando non usarlo. Punta ad almeno 3–4 frasi |
input_schema | sì | Un oggetto JSON Schema che descrive i parametri |
input_examples | no | Un array di oggetti input di esempio, ciascuno valido rispetto allo schema |
strict | no | Imposta true per forzare input conformi allo schema — vedi sotto |
cache_control | no | Mette in cache la definizione tra i turni (vedi Prompt Caching) |
Strumento get_weather + prima chiamata (Python)
tools = [{
"name": "get_weather",
"description": (
"Get the current weather for a city. Use this when the user asks about "
"present conditions or temperature in a named place. Returns temperature "
"in the requested unit plus a one-word sky condition. It does NOT return "
"forecasts — use get_forecast for anything in the future."
),
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City and region, e.g. 'Rome, IT'"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
"additionalProperties": False,
},
}]
msg = client.messages.create(
model="claude-opus-5-5", max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "What's the weather in Rome?"}],
)
if msg.stop_reason == "tool_use":
for block in msg.content: # iterate — never index content[0]
if block.type == "tool_use":
result = run_tool(block.name, block.input)
# send back a tool_result carrying block.idLe descrizioni sono prompt
Il campo description è di gran lunga la leva più potente sulla qualità dell'uso degli strumenti — lo legge il modello, non i tuoi colleghi. Le indicazioni di Anthropic sono inequivocabili su cosa distingue una buona descrizione:
- Dì quando non usarlo. La maggior parte delle selezioni sbagliate è uno strumento chiamato per un compito che appartiene a uno strumento adiacente.
- Consolida invece di moltiplicare. Invece di
create_pr,review_pr,merge_pr, spedisci un solo strumento con un parametroaction. Strumenti meno numerosi e più ampi riducono l'ambiguità nella selezione. - Usa un namespace per servizio.
github_list_prseslack_send_messagebattonolist_prsesend_messagequando la tua superficie di strumenti cresce. - Restituisci risultati ad alto segnale. Identificatori stabili e semantici (slug, UUID) e solo i campi che servono a Claude per scegliere il passo successivo. Un output di strumento gonfio brucia contesto e seppellisce il segnale.
- Per input delicati, aggiungi
input_examples. Gli esempi validati rispetto allo schema costano circa 20–50 token ciascuno per oggetti semplici e 100–200 per quelli complessi e annidati, e un esempio non valido restituisce un 400.
Gestire la risposta: le tre regole che restituiscono 400
È qui che il codice funzionante di solito si rompe. Un tool_result è un normale messaggio user, ma l'API impone tre regole di posizionamento.
- Nessun messaggio può stare tra il messaggio tool_use dell'assistant e il tuo messaggio tool_result. Se violi questa regola, l'API segnala: tool_use ids were found without tool_result blocks immediately after.
- Qualsiasi testo che aggiungi deve venire DOPO tutti i tool_result. Un blocco text prima di un tool_result è un 400.
- Quando lo stesso turno dell'assistant ha chiamato anche uno strumento lato server che non ha ancora un risultato, aggiungere del testo in coda chiude il turno in anticipo e la richiesta fallisce con un 400 che nomina lo strumento lato server irrisolto.
// ❌ 400 — text before tool_result
{"role": "user", "content": [
{"type": "text", "text": "Here are the results:"},
{"type": "tool_result", "tool_use_id": "toolu_01", "content": "15 degrees"}
]}
// ✅ correct — results first, text after (client tools only)
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_01", "content": "15 degrees"},
{"type": "text", "text": "What should I do next?"}
]}
Il content di un tool_result può essere una stringa semplice, oppure una lista di blocchi text, image, document o search_result. Può anche essere omesso del tutto per uno strumento che non restituisce nulla.
Restituisci i fallimenti come risultati, non come eccezioni
Quando uno strumento solleva un'eccezione, non interrompere il loop — restituisci il fallimento con is_error:
{"role": "user", "content": [{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "Rate limit exceeded. Retry after 60 seconds.",
"is_error": true
}]}
Scrivi il messaggio per un lettore che deve decidere cosa fare dopo. "failed" non dice niente a Claude; "Rate limit exceeded. Retry after 60 seconds." gli dice di aspettare invece di riprovare subito. Quando una chiamata a uno strumento non è valida o manca un parametro, Claude di norma riprova due o tre volte con delle correzioni prima di arrendersi e scusarsi con l'utente — quindi un errore istruttivo è spesso la differenza tra il recupero e un vicolo cieco.
Garantire input conformi allo schema: strict: true
Impostare strict: true su una definizione di strumento vincola il sampling dei token del modello a un output conforme allo schema (grammar-constrained sampling). L'input di ogni blocco tool_use corrisponde allora in modo dimostrabile al tuo input_schema, e il name è sempre uno strumento reale — nessun "2" dove ti serviva 2, nessun campo obbligatorio mancante, nessuno strato di validazione-e-riprova.
Definizione di strumento strict
{
"name": "search_flights",
"description": "Search bookable flights for a route and date.",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"destination": {"type": "string"},
"departure_date": {"type": "string", "format": "date"},
"passengers": {"type": "integer", "enum": [1, 2, 3, 4, 5, 6]}
},
"required": ["destination", "departure_date"],
"additionalProperties": false
}
}Tre avvertenze che vale la pena conoscere prima di attivarlo ovunque:
- Lo schema deve restare all'interno del sottoinsieme di JSON Schema supportato — lo stesso che usa l'Output strutturato.
- Le voci toolset di computer use e browser use rifiutano
strict: true; una richiesta che lo imposta su una delle due viene rifiutata. - Gli schemi compilati vengono messi in cache separatamente dal contenuto dei messaggi per un massimo di 24 ore. Le indicazioni di Anthropic sono esplicite: non inserire informazioni sanitarie protette negli schemi — né nei nomi delle proprietà, né nei valori
enum, né nei valoriconst, né nelle regexpattern. I PHI vanno nel contenuto dei messaggi, che gode delle tutele HIPAA che gli schemi in cache non hanno.
tool_choice — e perché forzare uno strumento ora fallisce
tool_choice controlla se Claude può, deve o non può chiamare uno strumento. Accetta quattro valori:
| Valore | Significato |
|---|---|
auto | Claude decide. Il default ogni volta che tools è presente |
any | Claude deve chiamare uno dei tuoi strumenti, ma sceglie quale |
tool | Claude deve chiamare lo strumento indicato |
none | Claude non può chiamare nessuno strumento. Il default quando non è presente alcun tools |
Ecco la parte che a ogni altra guida sull'uso degli strumenti attualmente manca. any e tool non sono supportati universalmente, e sui modelli più recenti restituiscono un 400.
| Modello o impostazione | Limitazione | Usa invece |
|---|---|---|
Extended thinking manuale (thinking di tipo enabled) | any e tool danno errore | auto o none. Il thinking adattivo da solo non blocca l'uso forzato degli strumenti |
| Claude Opus 5.5, Sonnet 5.5, Fable 5.1, Mythos 5.1 | any e tool restituiscono un 400, indipendentemente dalle impostazioni di thinking | auto più strict: true, oppure Output strutturato. none funziona ancora |
L'errore è esplicito:
tool_choice: type "tool" and "any" are not supported for this model.
La stessa validazione si applica all'endpoint di conteggio dei token, quindi una richiesta che passa il conteggio dei token non ti riserverà sorprese più tardi.
Cosa fare invece
Separare le due motivazioni rende la migrazione ovvia, perché l'uso forzato degli strumenti svolgeva due compiti distinti:
- «Mi serve JSON conforme allo schema.» Non è mai stato davvero un problema di forzare una chiamata. Tieni
tool_choicesuautoe impostastrict: true, oppure sposta lo schema sull'Output strutturato se vuoi una risposta invece di una chiamata. - «Mi serve che chiami lo strumento, non che mi parli addosso.» Dillo nel prompt. Dichiarare quando lo strumento si applica — «Usa
get_weatherper qualsiasi domanda sulle condizioni attuali» — indirizza ancora quale strumento sceglieauto.
Rinunciare all'uso forzato degli strumenti ha un bonus: any e tool funzionavano precompilando il messaggio dell'assistant, cosa che sopprimeva qualsiasi preambolo in linguaggio naturale prima dei blocchi tool_use. Con auto, Claude può spiegare cosa sta per fare — che di solito è ciò che vuoi in un agente rivolto all'utente.
Cambiare tool_choice a metà conversazione invalida i blocchi di messaggio in cache. Le definizioni degli strumenti e i system prompt restano in cache, ma il contenuto dei messaggi deve essere rielaborato. Se stavi commutando tool_choice a ogni turno, ti stava costando silenziosamente — vedi Prompt Caching.
Strumenti lato client vs. strumenti lato server
Non tutti gli strumenti hanno bisogno di un handler nel tuo codice.
- Strumenti lato client — i tuoi. Definisci lo schema, esegui, restituisci un
tool_result. Il loop visto sopra. - Strumenti lato client con schema Anthropic — bash, text editor, memory. Lo schema è predefinito, ma a eseguirli è comunque la tua app.
- Strumenti lato server — web search, web fetch, code execution, tool search. Girano sull'infrastruttura di Anthropic senza codice di handler da parte tua, e gli errori sono gestiti in modo trasparente: per uno strumento lato server non restituisci mai
is_error. - Toolset lato client — computer use e browser use sono una singola voce senza
nameche dichiara un insieme fisso di strumenti membri. I loro blocchitool_useportano un campo aggiuntivotoolset_name("computer"o"browser"), e ilnameè il membro che viene chiamato, comescreenshotonavigate. Smista in base a entrambi i campi, e rimanda indietrotoolset_namenel risultato — il risultato di un membro che lo omette viene rifiutato.
Errori comuni
- Indicizzare
content[0]invece di iterare e smistare in base atype. Le risposte spesso iniziano con un bloccotext, e sui modelli più recenti con blocchithinking. - Gestire un solo
tool_useper risposta. Claude può chiamare più strumenti in un turno; un loop che risponde al primo e scarta gli altri lascia degli idtool_usesenza risultati, che è un 400 alla richiesta successiva. - Mettere i tuoi commenti prima dei risultati. I blocchi
tool_resultvengono per primi, sempre. - Inghiottire le eccezioni degli strumenti. Un'eccezione nel tuo handler chiude il loop; un risultato con
is_errorpermette a Claude di adattarsi. - Ricorrere a
tool_choice: "any"per abitudine. Sui modelli flagship attuali è un 400 — e comunquestrict: trueera già la risposta migliore. - Trattare l'output degli strumenti come affidabile. Vedi sotto.
I risultati degli strumenti portano abitualmente contenuti che non controlli: pagine web, email in arrivo, upload degli utenti, API di terze parti. Un attaccante che può influenzarne una parte qualsiasi può inserire istruzioni dirette al tuo modello — prompt injection indiretta. Mantieni i contenuti non affidabili dentro i blocchi tool_result, mai innestati nel prompt system o in un semplice blocco text dell'utente, e leggi Prompt Injection e Mettere in sicurezza agenti e strumenti. Uno strumento che compie azioni reali eredita anche un modello di sicurezza: privilegio minimo, e un umano nel loop per tutto ciò che è irreversibile.
Mettiti alla prova
0/4- L'uso degli strumenti è un loop: invii le definizioni degli strumenti, Claude restituisce uno o più blocchi tool_use e si ferma, tu esegui e restituisci dei tool_result, Claude continua. Itera sui blocchi di content in base al type — non indicizzare mai content[0].
- Tre regole di posizionamento causano la maggior parte dei 400: i risultati devono seguire immediatamente il loro tool_use, i blocchi tool_result vengono per primi nell'array content, e una chiamata a uno strumento lato server irrisolta impone soli risultati.
- L'uso forzato degli strumenti è scomparso sui modelli più recenti: tool_choice "any" e "tool" restituiscono un 400 su Opus 5.5, Sonnet 5.5, Fable 5.1 e Mythos 5.1. Usa auto con strict: true, oppure l'output strutturato, e indirizza la selezione degli strumenti dal prompt.
- strict: true garantisce input conformi allo schema tramite grammar-constrained sampling — ma tieni i PHI fuori dagli schemi e non impostarlo sulle voci toolset computer/browser.
- Restituisci i fallimenti come tool_result con is_error e un messaggio istruttivo, così Claude può adattarsi invece di scusarsi.
- I risultati degli strumenti sono la via più probabile perché una prompt injection indiretta raggiunga il tuo agente — tienici dentro i contenuti non affidabili, applica il privilegio minimo e subordina le azioni irreversibili a un umano.
Avanti
- Programmatic Tool Calling — lascia che Claude chiami questi stessi strumenti da Python dentro una sandbox, riducendo i round-trip e tenendo i risultati intermedi fuori dal contesto
- Output strutturato — l'altra metà del sostituto dell'uso forzato degli strumenti, quando vuoi una risposta JSON invece di una chiamata
- Costruire agenti sull'API — avvolgi il loop in un agente reale
- Claude Opus 5.5: la guida sul campo — la storia completa della migrazione dietro la rimozione dell'uso forzato degli strumenti
- MCP e collegamento agli strumenti — ottieni gli strumenti da un server invece di definirli tu stesso