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 con intervención humana, 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 tool runner automáticamente:
El tool runner está en beta y está disponible en el SDK de Python, SDK de TypeScript, SDK de C#, SDK de Go, SDK de Java, SDK de PHP y SDK de Ruby.
Define herramientas usando los helpers del SDK, luego usa el tool runner 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.
Si estás usando el cliente asíncrono, reemplaza @beta_tool con @beta_async_tool y define la función con async def.
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-4-8",
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 tool runner es un iterable que produce mensajes de Claude. En cada iteración, el runner 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 instrucción break. El runner itera hasta que Claude devuelve un mensaje sin un uso de herramientas, o hasta que alcanza max_iterations si lo configuras.
Si no necesitas 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-4-8",
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 runner antes de la siguiente llamada a la API. Cada iteración sigue este ciclo de vida:
De forma predeterminada, el runner gestiona el estado de la conversación por ti: después de cada turno, 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 runner 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 runner 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 runner que estás gestionando el historial tú mismo, así que incluye el mensaje del asistente y el resultado de la herramienta en lo que agregas.
runner = client.beta.messages.tool_runner(
model="claude-opus-4-8",
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 solicitud como max_tokens sin tomar el control del historial de mensajes, usa set_messages_params(). El runner 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 tool runners de Python, TypeScript y Ruby admiten compactación automática, que genera resúmenes cuando el uso de tokens excede 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 tool runners de Go, Java, C# y PHP no incluyen compactación del lado del cliente.
Cuando una herramienta lanza una excepción, el tool runner 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 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 SDKs de Python, TypeScript y Java leen la variable de entorno ANTHROPIC_LOG para activar el registro del SDK, que incluye detalles de solicitud y respuesta:
# Registrar en nivel info
export ANTHROPIC_LOG=info
# Registrar en 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.
De forma predeterminada, 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 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-4-8",
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 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 runner continúe. Si agregas explícitamente el resultado modificado o lo mutas en el lugar depende del SDK. Consulta los comentarios de código en cada pestaña.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-4-8",
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)Agregar cache_control a los resultados de herramientas es particularmente útil cuando las herramientas devuelven grandes cantidades de datos (como resultados de búsqueda de documentos) que quieres almacenar en caché para llamadas posteriores a la API. Consulta Almacenamiento en caché de prompts para más detalles sobre estrategias de almacenamiento en caché.
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-4-8",
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())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 y formatea 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?