Il tool runner gestisce il ciclo agentico, il wrapping degli errori e la sicurezza dei tipi così non devi farlo tu. Quando hai bisogno di approvazione human-in-the-loop, logging personalizzato o esecuzione condizionale, usa invece il ciclo manuale.
Invece di gestire manualmente le chiamate agli strumenti, i risultati degli strumenti e la gestione della conversazione, il tool runner automaticamente:
Definisci gli strumenti usando gli helper dell'SDK, poi usa il tool runner per eseguirli.
A seconda della firma dello strumento dell'SDK, uno strumento restituisce il suo risultato come stringa o come blocchi di contenuto (blocchi di testo, immagine o documento), quindi uno strumento può restituire risultati multimodali. Una stringa restituita diventa un singolo blocco di contenuto di testo. Per restituire dati strutturati, come un oggetto JSON o un numero, codificali prima come stringa.
Usa il decoratore @beta_tool per definire strumenti con type hint e docstring.
import json
from anthropic import Anthropic, beta_tool
client = Anthropic()
@beta_tool
def get_weather(location: str, unit: str = "fahrenheit") -> str:
"""Get the current weather in a given location.
Args:
location: The city and state, e.g. San Francisco, CA
unit: Temperature unit, either 'celsius' or 'fahrenheit'
"""
return json.dumps({"temperature": "20°C", "condition": "Sunny"})
@beta_tool
def calculate_sum(a: int, b: int) -> str:
"""Add two numbers together.
Args:
a: First number
b: Second number
"""
return str(a + b)
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[get_weather, calculate_sum],
messages=[
{
"role": "user",
"content": "What's the weather like in Paris? Also, what's 15 + 27?",
}
],
)
for message in runner:
print(message)Il decoratore @beta_tool ispeziona gli argomenti della funzione e la docstring per derivare lo schema JSON per te.
Il tool runner è un iterabile che produce messaggi da Claude. A ogni iterazione, il runner verifica se Claude ha richiesto un uso degli strumenti. In tal caso, esegue lo strumento e invia automaticamente il risultato a Claude, poi produce il messaggio successivo da Claude per continuare il tuo ciclo.
Puoi terminare il ciclo a qualsiasi iterazione con un'istruzione break. Il runner cicla finché Claude non restituisce un messaggio senza un uso degli strumenti, o finché non raggiunge max_iterations se lo hai impostato.
Se non hai bisogno dei messaggi intermedi, puoi ottenere direttamente il messaggio finale:
Usa runner.until_done() per ottenere il messaggio finale.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[get_weather, calculate_sum],
messages=[
{
"role": "user",
"content": "What's the weather like in Paris? Also, what's 15 + 27?",
}
],
)
final_message = runner.until_done()
for block in final_message.content:
if block.type == "text":
print(block.text)All'interno del ciclo, puoi leggere ogni messaggio di risposta e modificare lo stato del runner prima della successiva chiamata API. Ogni iterazione segue questo ciclo di vita:
Per impostazione predefinita, il runner gestisce lo stato della conversazione per te: dopo ogni turno con chiamata agli strumenti, aggiunge il messaggio dell'assistente ed eventuali risultati degli strumenti alla propria cronologia dei messaggi. Prendi il controllo della cronologia dei messaggi quando vuoi ritentare un turno (scartare la risposta e reinviarla), iniettare un messaggio di follow-up o costruire tu stesso il risultato dello strumento.
Prendi il controllo modificando i messaggi del runner dall'interno del corpo del ciclo. Il metodo esatto dipende dall'SDK. Consulta le schede per linguaggio che seguono.
Quando prendi il controllo per un'iterazione, il runner non aggiunge il messaggio dell'assistente o i risultati degli strumenti di quel turno. Diventi responsabile di mantenere valida la conversazione: aggiungi tu stesso il messaggio dell'assistente e un risultato dello strumento (se vuoi che il turno conti), modifica lo stato in modo condizionale così che il ciclo possa comunque terminare quando non ci sono chiamate agli strumenti, e passa max_iterations per limitare il ciclo. Tutti e sette gli SDK supportano max_iterations.
Usa generate_tool_call_response() per ispezionare o calcolare il risultato dello strumento. Chiamare append_messages() all'interno del ciclo indica al runner che stai gestendo tu stesso la cronologia, quindi includi il messaggio dell'assistente e il risultato dello strumento in ciò che aggiungi.
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
max_iterations=10,
tools=[get_weather],
messages=[{"role": "user", "content": "What's the weather in San Francisco?"}],
)
for message in runner:
tool_response = runner.generate_tool_call_response()
if tool_response is not None:
# append_messages() segnala lo stato come modificato, quindi il runner salta
# l'append automatico per questa iterazione. Aggiungi tu stesso il messaggio
# dell'assistente e il tool result, più eventuali follow-up.
runner.append_messages(
message,
tool_response,
{"role": "user", "content": "Please be concise."},
)
# Se non c'è una chiamata a strumento, lascia lo stato invariato così il ciclo termina.Per modificare i parametri della richiesta come max_tokens senza prendere il controllo della cronologia dei messaggi, usa set_messages_params(). Il runner aggiunge comunque automaticamente il messaggio dell'assistente e il risultato dello strumento.
for message in runner:
runner.set_messages_params(lambda params: {**params, "max_tokens": 2048})Per attività agentiche di lunga durata, i tool runner Python, TypeScript e Ruby supportano la compattazione automatica, che genera riepiloghi quando l'utilizzo dei token supera una soglia così che la conversazione possa continuare oltre i limiti della finestra di contesto. Tutti e tre gli SDK hanno deprecato questa opzione lato client in favore della modifica del contesto lato server, che è disponibile in ogni SDK. I tool runner Go, Java, C# e PHP non includono la compattazione lato client.
Quando uno strumento genera un'eccezione, il tool runner la cattura e restituisce l'errore a Claude come risultato dello strumento con is_error: true. Il risultato dello strumento contiene il messaggio dell'eccezione (in Python, il suo tipo e messaggio), non lo stack trace completo.
Ciò che l'SDK registra nei log è specifico del linguaggio. L'SDK Python registra l'eccezione completa, incluso il suo stack trace, tramite il modulo standard logging ogni volta che uno strumento solleva un'eccezione non gestita. Gli SDK Python, TypeScript e Java leggono la variabile d'ambiente ANTHROPIC_LOG per attivare il logging dell'SDK, che include i dettagli di richiesta e risposta:
# Registra a livello info
export ANTHROPIC_LOG=info
# Registra a livello debug per un output più dettagliato
export ANTHROPIC_LOG=debugGli SDK Go, Ruby, C# e PHP non leggono ANTHROPIC_LOG. Al di fuori di Python, nessun SDK registra uno strumento fallito: per vedere perché uno strumento è fallito, cattura e registra l'eccezione all'interno della funzione dello strumento prima di restituirla o rilanciarla.
Per impostazione predefinita, gli errori degli strumenti vengono restituiti a Claude, che può quindi rispondere in modo appropriato. Tuttavia, potresti voler rilevare gli errori e gestirli diversamente, ad esempio per interrompere l'esecuzione in anticipo o implementare una gestione degli errori personalizzata.
Negli SDK Python e TypeScript, usa il metodo di risposta dello strumento (generate_tool_call_response() in Python, generateToolResponse() in TypeScript) per intercettare i risultati degli strumenti e verificare la presenza di errori prima che vengano inviati a Claude. Gli altri SDK non espongono quell'hook. Le loro schede descrivono l'alternativa più vicina:
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[my_tool],
messages=[{"role": "user", "content": "Run my_tool with the query 'hello'."}],
)
for message in runner:
tool_response = runner.generate_tool_call_response()
if tool_response is not None:
# tool_response è un dict: {"role": "user", "content": [...]}
# Verifica se un risultato di uno strumento contiene un errore
for block in tool_response["content"]:
if block.get("is_error"):
# Opzione 1: solleva un'eccezione per interrompere il ciclo
raise RuntimeError(f"Tool failed: {json.dumps(block['content'])}")
# Opzione 2: registra nel log e continua (lascia che Claude lo gestisca)
# logger.error(f"Tool error: {json.dumps(block['content'])}")
# Elabora il messaggio normalmente
print(message.content)Puoi modificare i risultati degli strumenti prima che vengano rinviati a Claude. Questo è utile per aggiungere metadati come cache_control per abilitare la cache dei prompt sui risultati degli strumenti, o per trasformare l'output dello strumento.
Negli SDK Python e TypeScript, usa il metodo di risposta dello strumento per ottenere il risultato dello strumento, poi modificalo prima che il runner proceda. Se devi aggiungere esplicitamente il risultato modificato o mutarlo sul posto dipende dall'SDK. Consulta i commenti nel codice in ogni scheda.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[search_documents],
messages=[
{
"role": "user",
"content": "Search for information about the climate of San Francisco",
}
],
)
for message in runner:
tool_response = runner.generate_tool_call_response()
if tool_response is not None:
# tool_response è un dict: {"role": "user", "content": [...]}
# Modifica il risultato dello strumento per aggiungere il controllo della cache
for block in tool_response["content"]:
if block["type"] == "tool_result":
# Aggiungi cache_control per mettere in cache questo risultato dello strumento
block["cache_control"] = {"type": "ephemeral"}
# Aggiungi in coda la risposta modificata (questo impedisce l'aggiunta automatica dell'originale)
runner.append_messages(message, tool_response)
print(message.content)Abilita lo streaming per elaborare la risposta di ogni turno in modo incrementale. Ogni iterazione produce un oggetto stream su cui puoi iterare per gli eventi.
Imposta stream=True e usa get_final_message() per ottenere il messaggio accumulato.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[calculate_sum],
messages=[{"role": "user", "content": "What is 15 + 27?"}],
stream=True,
)
# Durante lo streaming, il runner restituisce BetaMessageStream
for message_stream in runner:
for event in message_stream:
print("event:", event)
print("message:", message_stream.get_final_message())
print(runner.until_done())Imponi la conformità allo JSON Schema sugli input degli strumenti di Claude con il campionamento vincolato dalla grammatica.
Analizza i blocchi tool_use, formatta le risposte tool_result e gestisci gli errori con is_error.
Abilita, formatta e disabilita le chiamate parallele agli strumenti, con indicazioni sulla cronologia dei messaggi e risoluzione dei problemi.
Specifica gli schemi degli strumenti, scrivi descrizioni efficaci e controlla quando Claude chiama i tuoi strumenti.
Was this page helpful?