El "tool runner" (ejecutor de herramientas) maneja el bucle agéntico, el envoltorio de errores y la seguridad de tipos para que tú no tengas que hacerlo. Cuando necesites aprobación con humano en el bucle, 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:
Define herramientas usando los helpers del SDK, 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",
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.
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, 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 un 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",
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)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:
Por defecto, el ejecutor gestiona el estado de la conversación por ti: después de cada turno de llamada a herramienta, 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 a continuación.
Cuando tomas el control durante una iteración, el ejecutor no agrega el mensaje del asistente ni los resultados de herramientas de ese turno. Te vuelves responsable de mantener la conversación válida: agrega el mensaje del asistente y un resultado de herramienta tú mismo (si quieres que el turno cuente), modifica el estado condicionalmente para que el bucle aún pueda terminar cuando no haya llamadas a herramientas, y pasa max_iterations para acotar el bucle. Los siete SDKs 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",
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 aún agrega 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})Para tareas agénticas de larga duración, los ejecutores de herramientas de Python, TypeScript y Ruby admiten la 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 ventana de contexto. Los tres SDKs han marcado como obsoleta esta opción del lado del cliente en favor de la edición de contexto del lado del servidor, que está disponible en todos los SDKs. Los ejecutores de herramientas de Go, Java, C# y PHP no incluyen compactación del lado del cliente.
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 la herramienta lleva el mensaje de la excepción (en Python, su tipo y mensaje), no el stack trace completo.
Lo que el SDK registra es específico de cada 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 SDKs de Python, TypeScript y Java leen la variable de entorno ANTHROPIC_LOG para activar el registro del SDK, que incluye detalles de la solicitud y la respuesta:
# Registra a nivel info
export ANTHROPIC_LOG=info
# Registra a nivel debug para una salida más detallada
export ANTHROPIC_LOG=debugLos SDKs 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.
Por defecto, los errores de herramientas se devuelven a Claude, que luego puede responder apropiadamente. Sin embargo, es posible que quieras detectar errores y manejarlos de manera diferente, por ejemplo, para detener la ejecución anticipadamente o implementar un manejo de errores personalizado.
En los SDKs 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 errores antes de que se envíen a Claude. Los otros SDKs 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",
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": [...]}
# Verifica si algún resultado de herramienta tiene un error
for block in tool_response["content"]:
if block.get("is_error"):
# Opción 1: Lanzar una excepción para detener el bucle
raise RuntimeError(f"Tool failed: {json.dumps(block['content'])}")
# Opción 2: Registrar y continuar (dejar que Claude lo maneje)
# logger.error(f"Tool error: {json.dumps(block['content'])}")
# Procesa el mensaje normalmente
print(message.content)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 almacenamiento en caché de prompts en los resultados de herramientas, o para transformar la salida de la herramienta.
En los SDKs de Python y TypeScript, usa el método de respuesta de herramienta para obtener el resultado de la herramienta, luego modifícalo antes de que el ejecutor continúe. Si agregas explícitamente el resultado modificado o lo mutas 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",
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 herramienta
block["cache_control"] = {"type": "ephemeral"}
# Agrega la respuesta modificada (esto evita que se agregue automáticamente la original)
runner.append_messages(message, tool_response)
print(message.content)Habilita el streaming para procesar la respuesta de cada turno de forma incremental. Cada iteración produce un objeto de stream 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",
max_tokens=1024,
tools=[calculate_sum],
messages=[{"role": "user", "content": "What is 15 + 27?"}],
stream=True,
)
# Al hacer 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())Aplica el cumplimiento de JSON Schema en las entradas de herramientas de Claude con muestreo restringido por gramática.
Analiza bloques tool_use, formatea respuestas tool_result y maneja errores con is_error.
Habilita, formatea y deshabilita 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?