Claude Platform Docs
MessagesStrumenti

Tool runner (SDK)

Usa il tool runner dell'SDK per gestire automaticamente il ciclo agentico, il wrapping degli errori e la sicurezza dei tipi.

Il tool runner gestisce il ciclo agentico, il wrapping degli errori e la sicurezza dei tipi al posto tuo. 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:

  • Esegue gli strumenti quando Claude li chiama
  • Gestisce il ciclo richiesta/risposta
  • Gestisce lo stato della conversazione
  • Fornisce sicurezza dei tipi e validazione

Utilizzo di base

Definisci gli strumenti usando gli helper dell'SDK, quindi usa il tool runner per eseguirli.

A seconda della firma dello strumento nell'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-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 al posto tuo.

Iterare sul tool runner

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, quindi produce il messaggio successivo di Claude per continuare il tuo ciclo.

Puoi terminare il ciclo a qualsiasi iterazione con un'istruzione break. Il runner continua a iterare finché Claude non restituisce un messaggio senza uso degli strumenti, oppure 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-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)

Utilizzo avanzato

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:

  1. Il runner invia una richiesta alla Messages API con il suo stato corrente.
  2. Il runner produce il messaggio di risposta al corpo del tuo ciclo.
  3. Il corpo del tuo ciclo viene eseguito. Puoi leggere il messaggio e facoltativamente modificare lo stato del runner.
  4. Quando il corpo del tuo ciclo termina, il runner verifica se hai modificato la sua cronologia dei messaggi.
    • Se non hai modificato la cronologia dei messaggi: Se il messaggio contiene chiamate agli strumenti, il runner aggiunge il messaggio dell'assistente e i risultati degli strumenti, quindi continua. Se non ci sono chiamate agli strumenti, il ciclo termina.
    • Se hai modificato la cronologia dei messaggi: Il runner salta la sua aggiunta automatica e usa il tuo stato senza modifiche. Consulta Prendere il controllo della cronologia dei messaggi.

Prendere il controllo della cronologia dei messaggi

Per impostazione predefinita, il runner gestisce lo stato della conversazione al posto tuo: dopo ogni turno con chiamata agli strumenti, aggiunge il messaggio dell'assistente e gli 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 reinviare), 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 assumi il controllo per un'iterazione, il runner non aggiunge il messaggio dell'assistente né 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 venga conteggiato), modifica lo stato in modo condizionale affinché 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-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 cambiare parametri della richiesta come max_tokens senza prendere il controllo della cronologia dei messaggi, usa set_messages_params(). Il runner continua ad aggiungere automaticamente il messaggio dell'assistente e il risultato dello strumento.

for message in runner:
    runner.set_messages_params(lambda params: {**params, "max_tokens": 2048})

Gestione automatica del contesto

Per le attività agentiche di lunga durata, i tool runner TypeScript e Ruby supportano la compaction (compattazione) automatica, che genera riepiloghi quando l'utilizzo dei token supera una soglia, in modo che la conversazione possa proseguire oltre i limiti della "context window" (finestra di contesto). Entrambi gli SDK hanno deprecato questa opzione lato client a favore della compattazione lato server, che funziona con il tool runner di ogni SDK tramite il parametro di richiesta context_management. L'SDK Python (v1.0 e successive) e i tool runner Go, Java, C# e PHP non includono la compattazione lato client. I tool runner Python, TypeScript, C#, Go, Java, PHP e Ruby dispongono di un helper compact_before_next_turn() per la compattazione su richiesta. Consulta Compatta in un ciclo. Usa questo helper oppure una modifica di compattazione context_management su un runner, non entrambi.

Debug dell'esecuzione degli strumenti

Quando uno strumento lancia un'eccezione, il tool runner la intercetta 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 dipende dal linguaggio. Il Python SDK 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:

# Log a livello info
export ANTHROPIC_LOG=info

# Log a livello debug per un output più dettagliato
export ANTHROPIC_LOG=debug

Gli SDK Go, Ruby, C# e PHP non leggono ANTHROPIC_LOG. Al di fuori di Python, nessun SDK registra nei log uno strumento fallito: per vedere perché uno strumento è fallito, intercetta e registra l'eccezione all'interno della funzione dello strumento prima di restituirla o rilanciarla.

Intercettare gli errori degli strumenti

Per impostazione predefinita, gli errori degli strumenti vengono passati 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 questo hook. Le loro schede descrivono l'alternativa più vicina:

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-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 sia Claude a gestirlo)
                # logger.error(f"Tool error: {json.dumps(block['content'])}")

    # Elabora il messaggio normalmente
    print(message.content)

Modificare i risultati degli strumenti

Puoi modificare i risultati degli strumenti prima che vengano inviati 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, quindi modificalo prima che il runner proceda. Se 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-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 memorizzare nella cache questo risultato dello strumento
                block["cache_control"] = {"type": "ephemeral"}

        # Accoda la risposta modificata (evita l'accodamento automatico dell'originale)
        runner.append_messages(message, tool_response)

    print(message.content)

Streaming

Abilita lo streaming per elaborare la risposta di ogni turno in modo incrementale. Ogni iterazione produce un oggetto stream su cui puoi iterare per ottenere 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-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())

Prossimi passi

Imponi la conformità a JSON Schema sugli input degli strumenti di Claude con il campionamento vincolato da 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?