Programmatic Tool Calling
- Capire cosa succede davvero quando Claude chiama il tuo tool da dentro una sandbox — e perché il tuo tool gira comunque sulla tua macchina
- Abilitarlo correttamente con allowed_callers, e sapere perché non è un confine di sicurezza
- Conoscere i numeri reali: cosa fa risparmiare, su quali carichi di lavoro, e dove invece ti costa
- Evitare le cinque modalità di errore che producono 400 e TimeoutError in produzione
Il problema che risolve
Il tool use classico è una conversazione. Claude chiede una chiamata a un tool, tu rispondi, l'intero risultato finisce nella finestra di contesto, Claude lo legge e chiede la successiva. Venti lookup significano venti passaggi di inferenza e venti payload grezzi che restano nel contesto per sempre.
Gran parte di quel payload è spreco. Se vuoi sapere quali di venti dipendenti hanno sforato il budget spese, Claude non ha bisogno di ogni singola voce — gli servono quei pochi nomi. Ma nel tool use classico le voci devono passare attraverso il modello per essere filtrate da lui.
Il programmatic tool calling ribalta la cosa. Claude scrive uno script Python, lo script chiama i tuoi tool in un loop, filtra i risultati, e al modello torna solo ciò che lo script stampa. I dati grezzi non entrano mai nella finestra di contesto.
Cosa succede davvero
Ecco la parte che quasi ogni riassunto di questa funzionalità sbaglia: il tuo tool non gira dentro la sandbox. Il container di Anthropic non ha accesso al tuo database.
Quello che accade davvero è che il codice Python di Claude si mette in pausa a metà esecuzione, l'API restituisce la chiamata a te, e l'interprete riprende quando rispondi:
- Gira dentro il container di code execution. I tuoi tool appaiono a quel codice come funzioni Python async — una per tool, ciascuna prende un singolo dict di argomenti e restituisce una stringa.
- L'API restituisce un normale blocco tool_use per query_database, esattamente come nel tool use classico — solo che ora porta con sé un campo caller che punta all'esecuzione di codice che ha fatto la chiamata.
- Come sempre: esegui la query, rimandi indietro un blocco tool_result. L'ID del container è OBBLIGATORIO in questa richiesta di follow-up, non opzionale — l'API rifiuta la richiesta senza di esso, perché deve ritrovare l'interprete in pausa.
- Il tuo risultato diventa il valore di ritorno di quell'espressione await. Il loop continua. Claude non viene campionato nel frattempo — nessun passaggio di inferenza, nessun token.
- Quando lo script finisce, Claude riceve un code_execution_tool_result contenente stdout, stderr e un return_code. Tutto ciò che lo script ha recuperato ma non ha stampato semplicemente sparisce.
Poiché le funzioni sono async, Claude può fare fan-out con asyncio.gather e colpire dieci tool in parallelo — cosa che il tool use classico può solo approssimare con blocchi tool paralleli.
Che aspetto ha davvero il codice generato da Claude
import json
rows = json.loads(await query_database({"sql": "<sql>"}))
top = sorted(rows, key=lambda r: r["revenue"], reverse=True)[:5]
print(f"Top 5 customers: {top}")Nota il json.loads. La funzione del tool restituisce una stringa — il testo letterale del tool_result che rimandi indietro. Se la descrizione del tuo tool non dice "restituisce una lista di righe come oggetti JSON", Claude non ha modo di sapere che può deserializzare quella cosa, e tratterà i tuoi dati come un blob opaco. La frase sul formato di output nella descrizione del tool smette di essere documentazione e diventa codice portante. È la singola riga con più leva che scriverai adottando questa funzionalità.
Come attivarlo
Un campo sul tool che vuoi far chiamare dal codice, più il tool di code execution nella richiesta:
Abilitare la chiamata programmatica su un tool
{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": { "type": "object", "properties": { "sql": { "type": "string" } }, "required": ["sql"] },
"allowed_callers": ["code_execution_20260120"]
}allowed_callers assume tre forme:
| Valore | Significato |
|---|---|
["direct"] | Tool use classico. È il default quando il campo viene omesso. |
["code_execution_20260120"] | Claude viene guidato a chiamarlo solo da dentro il codice. |
["direct", "code_execution_20260120"] | Entrambi. La documentazione lo sconsiglia — scegline uno, così Claude riceve un segnale non ambiguo. |
Ogni blocco tool_use nella risposta porta ora un caller: o {"type": "direct"} oppure un caller di code execution il cui tool_id corrisponde al blocco server_tool_use che ha eseguito lo script. È così che attribuisci una chiamata allo script che l'ha fatta.
Non è un confine di sicurezza
La documentazione è insolitamente esplicita su questo, e vale la pena ripeterlo perché è facile assumere il contrario: allowed_callers controlla come il tool viene presentato a Claude. Non è un blocco rigido a livello di API. Claude è fortemente guidato a rispettarlo — ma il tuo client deve comunque essere pronto a ricevere un tool_use diretto per qualunque tool definisca, e non devi usare questo campo come meccanismo di autorizzazione. L'autorizzazione sta nel tuo handler del tool, dov'è sempre stata.
I numeri
Le cifre riportate da Anthropic stessa, così puoi giudicare se la complessità vale la pena:
- Su task di ricerca complessi, l'uso medio è sceso da 43.588 a 27.297 token — una riduzione del 37%.
- Sui benchmark GIA l'accuratezza è salita dal 46,5% al 51,2%; sul knowledge retrieval interno, dal 25,6% al 28,5%. Meno token e risposte migliori, perché il modello ragiona su conclusioni invece di annegare in payload grezzi.
- Sui benchmark di ricerca agentica (BrowseComp, DeepSearchQA), aggiungere la chiamata programmatica sopra ai tool di ricerca di base ha migliorato le prestazioni in media dell'11% usando il 24% di token di input in meno.
- Latenza: orchestrare più di 20 chiamate a tool in un unico blocco di codice elimina oltre 19 passaggi di inferenza.
La forma del vantaggio è il segnale rivelatore. Conviene quando hai 3+ chiamate dipendenti, un loop, un filtro o un fan-out. Non porta nulla — e ti costa un container — quando a Claude serve esattamente una chiamata e vuole comunque leggere l'intera risposta.
- Claude Haiku 4.5 accetta i tipi di tool più recenti ma NON supporta il programmatic tool calling né la persistenza dello stato REPL che ne dipende. Lì le versioni più nuove si comportano silenziosamente come code_execution_20250825. Se stai instradando su Haiku per risparmiare, non stai ottenendo questa funzionalità — e non riceverai nessun errore che te lo dica.
Quanto costa
Il programmatic tool calling viene fatturato come code execution, e la code execution si fattura a ora-container, non a chiamata:
- 1.550 ore gratuite al mese, per organizzazione.
- Oltre quella soglia, 0,05 $ all'ora, per container.
- Il tempo di esecuzione ha un minimo di 5 minuti — uno script da due secondi fattura comunque cinque minuti di container.
- Se alleghi file alla richiesta, il tempo di esecuzione viene fatturato anche se il tool non viene mai invocato, perché i file vengono comunque precaricati su un container.
- È gratuito quando la stessa richiesta usa anche web search o web fetch (
web_search_20260209/web_fetch_20260209o successivi).
Due conseguenze da interiorizzare. Primo, il minimo di 5 minuti significa che tanti container di breve durata è il pattern costoso; riusare un solo container lungo una sessione è quello economico. Secondo, questa funzionalità non è idonea alla Zero Data Retention — se la ZDR è un requisito contrattuale per te, questo è un muro, non una manopola da regolare.
I cinque modi in cui si rompe
- Quando ci sono chiamate a tool programmatiche pendenti, il tuo messaggio di risposta deve contenere SOLO blocchi tool_result. Non testo più risultati. Non risultati seguiti da una frase gentile. Solo blocchi tool_result.
- Una chiamata programmatica pendente va in timeout dopo circa quattro minuti e solleva un TimeoutError dentro il codice in esecuzione di Claude (lo stderr d'esempio nella documentazione riporta 'no response after 270s'). Claude lo vede nello stderr e di solito riprova. Metti un timeout sull'esecuzione del tuo tool, così fallisci in fretta invece di tenere il container appeso.
- Un input_schema con un $ref auto-referenziale non può essere abilitato alla chiamata programmatica — anche se lo stesso identico schema viene accettato per la chiamata diretta. Srotola la ricorsione a una profondità fissa e descrivi l'annidamento più profondo nella description più interna, oppure tieni quel tool solo diretto.
- Non puoi forzare la chiamata programmatica di un tool specifico. Nominare in tool_choice un tool i cui allowed_callers non includono 'direct' è un invalid_request_error. Non supportati nemmeno: strict: true (structured outputs) e disable_parallel_tool_use: true.
- I tool forniti da un connettore MCP non possono essere chiamati programmaticamente. Se vuoi una capacità basata su MCP dentro la sandbox, devi esporla tu stesso come normale custom tool.
Le stringhe di versione, decodificate
Tutte e tre le versioni di code execution sono generalmente disponibili e non richiedono nessun beta header:
| Versione | Cosa aggiunge |
|---|---|
code_execution_20250825 | La baseline. Bash + Python + operazioni su file. Supportata su ogni modello attuale. |
code_execution_20260120 | Aggiunge la persistenza dello stato REPL e il programmatic tool calling. È quella che ti serve. |
code_execution_20260521 | Runtime identico a 20260120. L'unica differenza è che la descrizione del tool informa Claude del limite di 90 secondi di wall-clock per cella Python, così può fare il budget delle celle lunghe. Una cella che sfora il limite restituisce un return_code diverso da zero con stato detection_timeout. |
Quest'ultima riga è un bel pezzo di design API da notare: un bump di versione il cui intero contenuto è un prompt migliore per il modello. Entrambe le stringhe sono intercambiabili dentro allowed_callers, e le risposte etichettano sempre il caller come code_execution_20260120 indipendentemente da quale hai dichiarato.
Il container stesso non ha accesso a internet — Claude non può fare pip install a runtime, quindi hai a disposizione il set di librerie preinstallate (pandas, numpy, scipy, scikit-learn, statsmodels e affini) e nulla di più. I container vengono messi in checkpoint dopo circa cinque minuti di inattività, sono ripristinabili per ID, e scadono 30 giorni dopo la creazione.
Quando usarlo
Usa il programmatic tool calling quando il modello viene impiegato come loop e filtro più che come ragionatore: lookup in batch su N entità, terminazione anticipata appena una condizione è soddisfatta, selezione condizionale del tool in base a un risultato intermedio, oppure schiacciare un dump di log da 200 KB nelle dieci righe che contano.
Usa invece il Tool Search Tool quando il tuo problema è che le definizioni si mangiano il contesto prima ancora che venga fatta una sola chiamata — marca i tool con defer_loading: true e Claude li carica su richiesta. I due sono complementari, non alternativi: la tool search trova il tool giusto, la chiamata programmatica lo esegue a basso costo. Se le tue definizioni di tool superano i 10K token circa, probabilmente ti servono entrambi.
E se stai affrontando la cosa dall'altro capo — un agente il cui contesto sta annegando nei risultati dei tool MCP — parti da Costo in token di MCP e Context Engineering, perché i token più economici restano quelli che non invii mai.
Check yourself
0/5Fonti e approfondimenti
- Programmatic tool calling — documentazione Claude Platform —
allowed_callers, il campocaller, il flusso pausa/ripresa, le restrizioni di formato e l'elenco dei vincoli. - Code execution tool — documentazione Claude Platform — versioni del tool, ciclo di vita e scadenza dei container, librerie preinstallate, e il prezzo di 1.550 ore gratuite / 0,05 $ l'ora.
- Introducing advanced tool use on the Claude Developer Platform — il dato 43.588 → 27.297 token, i guadagni di accuratezza su GIA e knowledge retrieval, e come il Tool Search Tool si combina con questa funzionalità.
- Improved web search with dynamic filtering — il risultato +11% / −24% di token di input sulla ricerca agentica, e come il filtro dinamico esegue la code execution al posto tuo.
- BrowseComp e DeepSearchQA — i benchmark di ricerca agentica dietro quei numeri.
- Correlati su AILmanac: Tool Use / Function Calling · MCP · Costo in token di MCP · Context Engineering · Token e prezzi