Claude Platform Docs
MessagesFerramentas

Tool runner (SDK)

Use o tool runner do SDK para lidar automaticamente com o loop agêntico, o encapsulamento de erros e a segurança de tipos.

O "tool runner" (executor de ferramentas) lida com o loop agêntico, o encapsulamento de erros e a segurança de tipos para que você não precise fazer isso. Quando você precisar de aprovação humana no loop (human-in-the-loop), logging personalizado ou execução condicional, use o loop manual em vez disso.

Em vez de lidar manualmente com chamadas de ferramentas, resultados de ferramentas e gerenciamento de conversa, o tool runner automaticamente:

  • Executa ferramentas quando Claude as chama
  • Lida com o ciclo de requisição/resposta
  • Gerencia o estado da conversa
  • Fornece segurança de tipos e validação

Uso básico

Defina ferramentas usando os helpers do SDK e, em seguida, use o tool runner para executá-las.

Dependendo da assinatura de ferramenta do SDK, uma ferramenta retorna seu resultado como uma string ou como blocos de conteúdo (blocos de texto, imagem ou documento), de modo que uma ferramenta pode retornar resultados multimodais. Uma string retornada se torna um único bloco de conteúdo de texto. Para retornar dados estruturados, como um objeto JSON ou um número, codifique-os primeiro como uma string.

Use o decorador @beta_tool para definir ferramentas com type hints e docstrings.

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)

O decorador @beta_tool inspeciona os argumentos da função e a docstring para derivar o JSON schema para você.

Iterando sobre o tool runner

O tool runner é um iterável que produz mensagens de Claude. Em cada iteração, o runner verifica se Claude solicitou um uso de ferramentas. Se sim, ele executa a ferramenta e envia o resultado de volta para Claude automaticamente, e então produz a próxima mensagem de Claude para continuar seu loop.

Você pode encerrar o loop em qualquer iteração com uma instrução break. O runner continua em loop até que Claude retorne uma mensagem sem uso de ferramentas, ou até atingir max_iterations, se você o definir.

Se você não precisa das mensagens intermediárias, pode obter a mensagem final diretamente:

Use runner.until_done() para obter a mensagem final.

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)

Uso avançado

Dentro do loop, você pode ler cada mensagem de resposta e modificar o estado do runner antes da próxima chamada à API. Cada iteração segue este ciclo de vida:

  1. O runner envia uma requisição para a Messages API com seu estado atual.
  2. O runner produz a mensagem de resposta para o corpo do seu loop.
  3. O corpo do seu loop é executado. Você pode ler a mensagem e, opcionalmente, modificar o estado do runner.
  4. Quando o corpo do seu loop retorna, o runner verifica se você modificou o histórico de mensagens dele.
    • Se você não modificou o histórico de mensagens: Se a mensagem contém chamadas de ferramentas, o runner anexa a mensagem do assistente e os resultados das ferramentas, e então continua. Se não houver chamadas de ferramentas, o loop termina.
    • Se você modificou o histórico de mensagens: O runner pula sua anexação automática e usa seu estado sem alterações. Consulte Assumindo o controle do histórico de mensagens.

Assumindo o controle do histórico de mensagens

Por padrão, o runner gerencia o estado da conversa para você: após cada turno com chamada de ferramenta, ele anexa a mensagem do assistente e quaisquer resultados de ferramentas ao seu próprio histórico de mensagens. Você assume o controle do histórico de mensagens quando deseja repetir um turno (descartar a resposta e reenviar), injetar uma mensagem de acompanhamento ou construir o resultado da ferramenta você mesmo.

Você assume o controle modificando as mensagens do runner de dentro do corpo do loop. O método exato depende do SDK. Consulte as abas por linguagem a seguir.

Quando você assume o controle em uma iteração, o runner não anexa a mensagem do assistente nem os resultados das ferramentas daquele turno. Você passa a ser responsável por manter a conversa válida: anexe você mesmo a mensagem do assistente e um resultado de ferramenta (se quiser que o turno conte), modifique o estado condicionalmente para que o loop ainda possa terminar quando não houver chamadas de ferramentas, e passe max_iterations para limitar o loop. Todos os sete SDKs suportam max_iterations.

Use generate_tool_call_response() para inspecionar ou calcular o resultado da ferramenta. Chamar append_messages() dentro do loop informa ao runner que você está gerenciando o histórico por conta própria, portanto inclua a mensagem do assistente e o resultado da ferramenta no que você anexar.

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() marca o estado como modificado, então o runner pula o
        # append automático nesta iteração. Faça o append da mensagem do assistente
        # e do tool result você mesmo, além de qualquer follow-up.
        runner.append_messages(
            message,
            tool_response,
            {"role": "user", "content": "Please be concise."},
        )
    # Quando não há chamada de ferramenta, deixe o estado intacto para o loop sair.

Para alterar parâmetros de requisição como max_tokens sem assumir o controle do histórico de mensagens, use set_messages_params(). O runner ainda anexa a mensagem do assistente e o resultado da ferramenta automaticamente.

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

Gerenciamento automático de contexto

Para tarefas agênticas de longa duração, os tool runners de TypeScript e Ruby suportam compactação automática, que gera resumos quando o uso de tokens excede um limite, para que a conversa possa continuar além dos limites da "context window" (janela de contexto). Ambos os SDKs descontinuaram essa opção do lado do cliente em favor da compactação do lado do servidor, que funciona com o tool runner de todos os SDKs por meio do parâmetro de requisição context_management. O SDK Python (v1.0 e posteriores) e os tool runners de Go, Java, C# e PHP não incluem compactação do lado do cliente. O tool runner tem um helper compact_before_next_turn() para compactação sob demanda. Consulte Compactar em um loop. Use-o ou uma edição de compactação context_management em um runner, não ambos.

Depurando a execução de ferramentas

Quando uma ferramenta lança uma exceção, o tool runner a captura e retorna o erro para Claude como um resultado de ferramenta com is_error: true. O resultado da ferramenta carrega a mensagem da exceção (em Python, seu tipo e mensagem), não o stack trace completo.

O que o SDK registra em log é específico de cada linguagem. O SDK Python registra a exceção completa, incluindo seu stack trace, por meio do módulo padrão logging sempre que uma ferramenta lança uma exceção não tratada. Os SDKs Python, TypeScript e Java leem a variável de ambiente ANTHROPIC_LOG para ativar o logging do SDK, que inclui detalhes de requisição e resposta:

# Registrar no nível info
export ANTHROPIC_LOG=info

# Registrar no nível debug para uma saída mais detalhada
export ANTHROPIC_LOG=debug

Os SDKs Go, Ruby, C# e PHP não leem ANTHROPIC_LOG. Fora do Python, nenhum SDK registra em log uma ferramenta que falhou: para ver por que uma ferramenta falhou, capture e registre a exceção dentro da função da ferramenta antes de retorná-la ou relançá-la.

Interceptando erros de ferramentas

Por padrão, os erros de ferramentas são repassados para Claude, que pode então responder adequadamente. No entanto, você pode querer detectar erros e tratá-los de forma diferente, por exemplo, para interromper a execução antecipadamente ou implementar tratamento de erros personalizado.

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 é um dict: {"role": "user", "content": [...]}
        # Verifica se algum resultado de ferramenta tem erro
        for block in tool_response["content"]:
            if block.get("is_error"):
                # Opção 1: lançar uma exceção para interromper o loop
                raise RuntimeError(f"Tool failed: {json.dumps(block['content'])}")

                # Opção 2: registrar no log e continuar (deixar o Claude lidar com isso)
                # logger.error(f"Tool error: {json.dumps(block['content'])}")

    # Processa a mensagem normalmente
    print(message.content)

Modificando resultados de ferramentas

Você pode modificar os resultados de ferramentas antes que sejam enviados de volta para Claude. Isso é útil para adicionar metadados como cache_control para habilitar o "prompt caching" (cache de prompt) em resultados de ferramentas, ou para transformar a saída da ferramenta. Consulte cache de prompt.

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 é um dict: {"role": "user", "content": [...]}
        # Modifica o resultado da ferramenta para adicionar controle de cache
        for block in tool_response["content"]:
            if block["type"] == "tool_result":
                # Adiciona cache_control para armazenar em cache este resultado da ferramenta
                block["cache_control"] = {"type": "ephemeral"}

        # Anexa a resposta modificada (isso impede o anexo automático da original)
        runner.append_messages(message, tool_response)

    print(message.content)

Streaming

Habilite o streaming para processar a resposta de cada turno de forma incremental. Cada iteração produz um objeto de stream que você pode iterar para obter eventos.

Defina stream=True e use get_final_message() para obter a mensagem acumulada.

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,
)

# Ao usar streaming, o runner retorna 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())

Próximos passos

Imponha conformidade com JSON Schema nas entradas de ferramentas de Claude com amostragem restrita por gramática.

Faça o parsing de blocos tool_use, formate respostas tool_result e trate erros com is_error.

Habilite, formate e desabilite chamadas paralelas de ferramentas, com orientações sobre histórico de mensagens e solução de problemas.

Especifique schemas de ferramentas, escreva descrições eficazes e controle quando Claude chama suas ferramentas.

Was this page helpful?