Claude Platform Docs
MessagesHerramientas

Ejecutor de herramientas (SDK)

Usa el ejecutor de herramientas del SDK para manejar automáticamente el bucle agéntico, el encapsulamiento de errores y la seguridad de tipos.

El "tool runner" (ejecutor de herramientas) maneja el bucle agéntico, el encapsulamiento de errores y la seguridad de tipos para que tú no tengas que hacerlo. Cuando necesites aprobación humana en el bucle (human-in-the-loop), registro personalizado o ejecución condicional, usa el bucle manual en su lugar.

En lugar de manejar manualmente las llamadas a herramientas, los resultados de herramientas y la gestión de la conversación, el ejecutor de herramientas automáticamente:

  • Ejecuta las herramientas cuando Claude las llama
  • Maneja el ciclo de solicitud/respuesta
  • Gestiona el estado de la conversación
  • Proporciona seguridad de tipos y validación

Uso básico

Define las herramientas usando los helpers del SDK y luego usa el ejecutor de herramientas para ejecutarlas.

Dependiendo de la firma de herramienta del SDK, una herramienta devuelve su resultado como una cadena o como bloques de contenido (bloques de texto, imagen o documento), por lo que una herramienta puede devolver resultados multimodales. Una cadena devuelta se convierte en un único bloque de contenido de texto. Para devolver datos estructurados, como un objeto JSON o un número, codifícalos primero como una cadena.

Usa el decorador @beta_tool para definir herramientas con anotaciones de tipo y 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)

El decorador @beta_tool inspecciona los argumentos de la función y el docstring para derivar el esquema JSON por ti.

Iterar sobre el ejecutor de herramientas

El ejecutor de herramientas es un iterable que produce mensajes de Claude. En cada iteración, el ejecutor verifica si Claude solicitó un uso de herramientas. Si es así, ejecuta la herramienta y envía el resultado de vuelta a Claude automáticamente, y luego produce el siguiente mensaje de Claude para continuar tu bucle.

Puedes terminar el bucle en cualquier iteración con una sentencia break. El ejecutor itera hasta que Claude devuelve un mensaje sin uso de herramientas, o hasta que alcanza max_iterations si lo configuraste.

Si no necesitas los mensajes intermedios, puedes obtener el mensaje final directamente:

Usa runner.until_done() para obtener el mensaje 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 avanzado

Dentro del bucle, puedes leer cada mensaje de respuesta y modificar el estado del ejecutor antes de la siguiente llamada a la API. Cada iteración sigue este ciclo de vida:

  1. El ejecutor envía una solicitud a la Messages API con su estado actual.
  2. El ejecutor produce el mensaje de respuesta hacia el cuerpo de tu bucle.
  3. Se ejecuta el cuerpo de tu bucle. Puedes leer el mensaje y, opcionalmente, modificar el estado del ejecutor.
  4. Cuando el cuerpo de tu bucle retorna, el ejecutor verifica si modificaste su historial de mensajes.
    • Si no modificaste el historial de mensajes: Si el mensaje contiene llamadas a herramientas, el ejecutor agrega el mensaje del asistente y los resultados de las herramientas, y luego continúa. Si no hay llamadas a herramientas, el bucle termina.
    • Si modificaste el historial de mensajes: El ejecutor omite su agregado automático y usa tu estado sin cambios. Consulta Tomar el control del historial de mensajes.

Tomar el control del historial de mensajes

De forma predeterminada, el ejecutor gestiona el estado de la conversación por ti: después de cada turno con llamadas a herramientas, agrega el mensaje del asistente y cualquier resultado de herramienta a su propio historial de mensajes. Tomas el control del historial de mensajes cuando quieres reintentar un turno (descartar la respuesta y reenviar), inyectar un mensaje de seguimiento o construir el resultado de la herramienta tú mismo.

Tomas el control modificando los mensajes del ejecutor desde dentro del cuerpo del bucle. El método exacto depende del SDK. Consulta las pestañas por lenguaje que siguen.

Cuando tomas el control en una iteración, el ejecutor no agrega el mensaje del asistente ni los resultados de las herramientas de ese turno. Pasas a ser responsable de mantener la conversación válida: agrega tú mismo el mensaje del asistente y un resultado de herramienta (si quieres que el turno cuente), modifica el estado de forma condicional para que el bucle aún pueda terminar cuando no haya llamadas a herramientas, y pasa max_iterations para limitar el bucle. Los siete SDK admiten max_iterations.

Usa generate_tool_call_response() para inspeccionar o calcular el resultado de la herramienta. Llamar a append_messages() dentro del bucle le indica al ejecutor que estás gestionando el historial tú mismo, así que incluye el mensaje del asistente y el resultado de la herramienta en lo que agregues.

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 el estado como modificado, así que el runner omite su
        # append automático para esta iteración. Agrega tú mismo el mensaje del asistente y
        # el tool result, más cualquier seguimiento.
        runner.append_messages(
            message,
            tool_response,
            {"role": "user", "content": "Please be concise."},
        )
    # Cuando no hay llamada a herramienta, deja el estado intacto para que el bucle termine.

Para cambiar parámetros de la solicitud como max_tokens sin tomar el control del historial de mensajes, usa set_messages_params(). El ejecutor sigue agregando el mensaje del asistente y el resultado de la herramienta automáticamente.

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

Gestión automática del contexto

Para tareas agénticas de larga duración, los ejecutores de herramientas de TypeScript y Ruby admiten la "compaction" (compactación) automática, que genera resúmenes cuando el uso de tokens supera un umbral para que la conversación pueda continuar más allá de los límites de la "context window" (ventana de contexto). Ambos SDK han dejado obsoleta esta opción del lado del cliente en favor de la compactación del lado del servidor, que funciona con el ejecutor de herramientas de cualquier SDK mediante el parámetro de solicitud context_management. El SDK de Python (v1.0 y posteriores) y los ejecutores de herramientas de Go, Java, C# y PHP no incluyen compactación del lado del cliente. Los ejecutores de herramientas de Python, TypeScript, C#, Go, Java, PHP y Ruby tienen un helper compact_before_next_turn() para la compactación bajo demanda. Consulta Compactar en un bucle. Usa este helper o una edición de compactación de context_management en un ejecutor, no ambos.

Depurar la ejecución de herramientas

Cuando una herramienta lanza una excepción, el ejecutor de herramientas la captura y devuelve el error a Claude como un resultado de herramienta con is_error: true. El resultado de herramienta contiene el mensaje de la excepción (en Python, su tipo y mensaje), no el stack trace completo.

Lo que el SDK registra depende del lenguaje. El SDK de Python registra la excepción completa, incluido su stack trace, a través del módulo estándar logging cada vez que una herramienta lanza una excepción no manejada. Los SDK de Python, TypeScript y Java leen la variable de entorno ANTHROPIC_LOG para activar el registro del SDK, que incluye detalles de solicitudes y respuestas:

# Registrar en nivel info
export ANTHROPIC_LOG=info

# Registrar en nivel debug para una salida más detallada
export ANTHROPIC_LOG=debug

Los SDK de Go, Ruby, C# y PHP no leen ANTHROPIC_LOG. Fuera de Python, ningún SDK registra una herramienta fallida: para ver por qué falló una herramienta, captura y registra la excepción dentro de la función de la herramienta antes de retornar o relanzarla.

Interceptar errores de herramientas

De forma predeterminada, los errores de herramientas se devuelven a Claude, que puede entonces responder de manera apropiada. Sin embargo, es posible que quieras detectar errores y manejarlos de forma diferente, por ejemplo, para detener la ejecución anticipadamente o implementar un manejo de errores personalizado.

En los SDK de Python y TypeScript, usa el método de respuesta de herramienta (generate_tool_call_response() en Python, generateToolResponse() en TypeScript) para interceptar los resultados de herramientas y verificar si hay errores antes de que se envíen a Claude. Los demás SDK no exponen ese hook. Sus pestañas describen la alternativa más cercana:

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 es un dict: {"role": "user", "content": [...]}
        # Comprueba si algún resultado de herramienta tiene un error
        for block in tool_response["content"]:
            if block.get("is_error"):
                # Opción 1: Lanza una excepción para detener el bucle
                raise RuntimeError(f"Tool failed: {json.dumps(block['content'])}")

                # Opción 2: Registra y continúa (deja que Claude lo maneje)
                # logger.error(f"Tool error: {json.dumps(block['content'])}")

    # Procesa el mensaje normalmente
    print(message.content)

Modificar resultados de herramientas

Puedes modificar los resultados de herramientas antes de que se envíen de vuelta a Claude. Esto es útil para agregar metadatos como cache_control para habilitar el "prompt caching" (almacenamiento en caché de prompts) en los resultados de herramientas, o para transformar la salida de la herramienta. Consulta almacenamiento en caché de prompts.

En los SDK de Python y TypeScript, usa el método de respuesta de herramienta para obtener el resultado de la herramienta y luego modifícalo antes de que el ejecutor continúe. Si debes agregar explícitamente el resultado modificado o mutarlo en el lugar depende del SDK. Consulta los comentarios del código en cada pestaña.

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 es un dict: {"role": "user", "content": [...]}
        # Modifica el resultado de la herramienta para agregar control de caché
        for block in tool_response["content"]:
            if block["type"] == "tool_result":
                # Agrega cache_control para almacenar en caché este resultado de la herramienta
                block["cache_control"] = {"type": "ephemeral"}

        # Añade la respuesta modificada (esto evita que se añada automáticamente la original)
        runner.append_messages(message, tool_response)

    print(message.content)

Streaming

Habilita el streaming para procesar la respuesta de cada turno de forma incremental. Cada iteración produce un objeto stream sobre el que puedes iterar para obtener eventos.

Establece stream=True y usa get_final_message() para obtener el mensaje acumulado.

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

# Al usar streaming, el runner devuelve 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 pasos

Impón el cumplimiento de JSON Schema en las entradas de herramientas de Claude con muestreo restringido por gramática.

Analiza bloques tool_use, da formato a respuestas tool_result y maneja errores con is_error.

Habilita, da formato y deshabilita las llamadas a herramientas en paralelo, con orientación sobre el historial de mensajes y solución de problemas.

Especifica esquemas de herramientas, escribe descripciones efectivas y controla cuándo Claude llama a tus herramientas.

Was this page helpful?