Inicio / Artículos

Herramienta Claude_use End to End: El bucle agente en tres iteraciones

Herramienta Claude_use de extremo a extremo: una solicitud a la API de Mensajes a través del bucle agente, con reglas de definición de herramientas, elección de herramientas, llamadas paralelas y resultados de herramientas.

Editor: REACTAPP.TOP19 min de lectura4379 palabras

Fuentes: Part III | ‘tool_use’, End to End: The Agentic Loop in Three Iterations

Por qué el bucle lo debes escribir tú

Trabajar con tool_use directamente a través de la API de Mensajes (anthropic.Anthropic() seguido de client.messages.create(...)) pone el bucle en tus manos. Tu código declara las herramientas, examina la razón de detención, analiza las solicitudes de Claude, las ejecuta y devuelve los resultados obtenidos. Escribir ese bucle a mano es la base de todo sistema que utiliza herramientas, y es un tema estándar en el programa de estudios de Claude Certified Architect.

Primero, un recordatorio: la API de Mensajes no tiene estado. No se recuerda nada entre llamadas, por lo que cada solicitud contiene toda la conversación, y una acción que utiliza herramientas suele consistir en varias solicitudes en lugar de una, repetidas hasta que Claude tenga lo que necesita. El resto sigue con una sola solicitud a través de ese ciclo completo.

El ejemplo en ejecución es un pequeño asistente de programación de citas. Un usuario pregunta si debe llevar un paraguas a una reunión en Londres mañana y a qué hora debe salir de casa para llegar a tiempo. Para responder se necesitan tres datos separados: los detalles de la reunión, el pronóstico del tiempo y el tiempo de viaje. Observe cómo Claude divide la pregunta en tres iteraciones, y cuánto del trabajo recae en su código en lugar del modelo.

1. La estructura del bucle

Cada intercambio que utiliza herramientas sigue un ciclo de cuatro fases:

  1. Declarar las herramientas en la solicitud.
  2. Claude pide una o más de ellas, indicado por stop_reason: "tool_use".
  3. Su código ejecuta esas herramientas y recopila los resultados.
  4. Usted envía de vuelta los resultados y llama nuevamente a la API.

Las fases dos a cuatro se repiten, y cada paso cuenta como una iteración. El bucle finaliza cuando Claude deja de solicitar herramientas y devuelve una conclusión normal (end_turn) o otra razón de detención. Dado que la API no tiene estado, usted mismo debe construir la conversación y reenviarla cada vez. Nada avanza a menos que su código lo haga.

2. Declaración de las herramientas

Claude está limitado a las herramientas que usted especifique. Estas se encuentran en el parámetro de nivel superior tools de la solicitud, el cual contiene un array de definiciones. Cada vez que está presente tools, la API elabora un prompt del sistema oculto a partir de ellas antes del mensaje del usuario. Ese prompt oculto genera costos: cada nombre, descripción y esquema consume tokens de entrada en cada solicitud, incluso si Claude nunca utiliza una herramienta.

Cada herramienta del cliente necesita tres campos:

  • name es un identificador único que debe cumplir con la expresión ^[a-zA-Z0-9_-]{1,64}$
"tools": [
  {
    "name": "get_calendar_event",
    "description": "Look up a calendar event by a natural-language query. Returns the event's start time and location.",
    "strict": true,
    "input_schema": {
      "type": "object",
      "properties": {
        "query": { "type": "string", "description": "What to search for, e.g. 'meeting in London tomorrow'" }
      },
      "required": ["query"],
      "additionalProperties": false
    }
  },
  {
    "name": "get_weather",
    "description": "Get the forecast for a city on a given date. Returns condition and temperature in Celsius.",
    "strict": true,
    "input_schema": {
      "type": "object",
      "properties": {
        "city": { "type": "string", "description": "City name, e.g. 'London'" },
        "date": { "type": "string", "description": "ISO date, e.g. '2026-07-14'" }
      },
      "required": ["city", "date"],
      "additionalProperties": false
    }
  },
  {
    "name": "get_travel_time",
    "description": "Estimate door-to-door travel time between two places. Returns minutes.",
    "strict": true,
    "input_schema": {
      "type": "object",
      "properties": {
        "origin": { "type": "string", "description": "Starting location" },
        "destination": { "type": "string", "description": "Ending location" }
      },
      "required": ["origin", "destination"],
      "additionalProperties": false
    }
  }
]

Defínalos con cuidado. La guía de Anthropic sobre herramientas eficaces se resume en unos pocos hábitos:

  • Sé específico con los nombres de las herramientas. get_current_weather le proporciona a Claude más información que weather.
  • Nombra y describe los parámetros de manera inequívoca. Prefiere user_id en lugar de user para que se proporcione el valor adecuado, y describe cada parámetro, ya que un campo bien nombrado pero con una descripción vaga sigue atrayendo entradas mal formadas.
  • Mantén el conjunto de herramientas reducido. Un conjunto demasiado grande ensucia el prompt del sistema generado y dificulta la selección del modelo.
  • Valida todo. Un esquema restringe la estructura, no la intención, así que verifica cada entrada que reciba tu código.

Hay dos adiciones en esas definiciones que merecen una atención detallada.

  1. strict: true es lo más importante. Se encuentra en el nivel más alto de cada herramienta, junto a name, description y input_schema, y activa el muestreo restringido por reglas gramaticales para los argumentos de la herramienta, de modo que el modelo no pueda generar un input que vulnere su esquema. Si no se activa, Claude suele emitir argumentos válidos pero puede desviarse, por ejemplo con "2" cuando se necesita el número 2, un valor fuera de un enum o un campo requerido faltante. Cada desviación se convierte en un fallo en tiempo de ejecución en el código que utiliza la llamada. Al activar strict: true, el input dentro de cualquier bloque tool_use cumplirá sin duda con input_schema, y se garantizará que el nombre sea uno de tus propios herramientas.
  2. Una regla del esquema viene con la garantía de que cada objeto debe establecer additionalProperties: false, de ahí su presencia en todas las herramientas mencionadas. El uso estricto de estas herramientas forma parte de la función de salidas estructuradas de Anthropic. En el momento en que se escribió esto, estaba disponible de forma general en modelos actuales como Opus 4.8, mientras que modelos anteriores como Sonnet 4.5 requerían el encabezado beta structured-outputs-2025-11-13. Los esquemas compilados se almacenan en caché durante un máximo de 24 horas, por lo que contar con un caché activo evita costos adicionales por compilación.

También puede adjuntar cache_control a las definiciones de herramientas para almacenarlas en caché entre solicitudes y evitar cargos por reprocesamiento, lo cual resulta beneficioso cuando el conjunto de herramientas es grande y estable.

Las definiciones describen únicamente la entrada

La definición de una herramienta abarca únicamente la entrada, es decir, lo que Claude le envía. Esto sorprende a quienes provienen de frameworks que declaran tipos de retorno. En términos sencillos:

  • Una definición incluye como partes obligatorias name, description y input_schema, mientras que strict, input_examples y cache_control son elementos opcionales. No existen output_schema ni error_schema.
  • Los valores de retorno se determinan en tiempo de ejecución por su código y luego se devuelven dentro del bloque tool_result descrito en la sección 6. El campo content puede contener una cadena o una lista de bloques, y la API no lo verifica contra nada, ya que no existe un esquema al que compararlo. Una precaución práctica: una herramienta podría devolver un objeto DTO, y el analizador estándar lo convierte en una cadena simple mediante su método toString. Aunque funciona utilizar esa forma simplificada, no es la opción ideal; en su lugar, convierta los DTO a JSON estructurado.
  • Los errores también son un asunto de tiempo de ejecución. Informe sobre una llamada fallida indicando is_error: true en tool_result y incluyendo un mensaje informativo en content; la definición no especifica ningún tipo de error declarativo. De forma predeterminada, el SDK transmite la excepción generada a Claude como resultado de error, y Claude procede a ajustarse en consecuencia.
  • La asimetría es intencionada. Las definiciones existen para mostrarle a Claude cómo invocar una herramienta. El resultado es simplemente contexto adicional que se vuelve a integrar en el bucle, por lo que, aparte del tool_use_id que lo relaciona con su solicitud, no se necesita ningún contrato.
  • ¿Desea un formato de salida garantizado? Revíselo usted mismo en el código antes de enviar cualquier cosa, o aplique salidas estructuradas (output_config.format) a la respuesta final que escribe Claude a partir de esos resultados. Las herramientas MCP incluyen su propio outputSchema, pero esa es una característica de la capa MCP y no de las definiciones nativas.

Controlar si Claude llama a una herramienta

tool_choice decide si se pueden llamar herramientas en cada turno y cuáles.

  • Este campo se encuentra en el nivel superior de la solicitud, junto a tools y messages, y no pertenece a ninguna herramienta en particular.
  • Lo que restringe es la salida que Claude puede generar. El razonamiento no se ve afectado, ya que Claude lee todo el prompt sin importar nada más.
  • Su alcance es una única solicitud, nunca global. La API es sin estado, por lo que tool_choice solo controla la respuesta con la que se envía, que corresponde a una sola iteración. Para mantenerla entre iteraciones, hay que volver a enviarla cada vez. Es legítimo variarla en cada iteración: forzar el uso de una herramienta temprano, utilizar auto más tarde y none para obtener una respuesta de texto final.

El objeto se ve así:

{
  "type": "auto" | "any" | "tool" | "none",
  "name": "get_weather",              // required ONLY when type is "tool"
  "disable_parallel_tool_use": false  // optional; default false
}

type es obligatorio y puede tomar uno de cuatro valores. name solo es necesario cuando type es "tool". disable_parallel_tool_use es opcional y limita la cantidad de herramientas que se pueden utilizar en cada turno.

auto. Claude decide: cero, una o varias herramientas, o una respuesta de texto. Se permiten llamadas en paralelo. Se acepta una respuesta de texto plano, y Claude puede narrar antes de realizar las llamadas. Es el valor predeterminado siempre que se proporcionen tools. Al establecer disable_parallel_tool_use: true, se limita el uso a una sola herramienta, aunque sigue siendo posible usar cero. Es adecuado para asistentes de chat y bucles de agente donde se necesitan acciones diferentes en cada turno. Es el único modo que permite a Claude pensar en voz alta antes de actuar, y el único modo, aparte de none, que funciona con el pensamiento extendido.

any. Claude debe llamar a una herramienta, pero él elige cuál. Se realiza al menos una llamada, y como el uso paralelo está activado por defecto, pueden devolverse varios bloques tool_use, no necesariamente uno solo. Solo ocurre exactamente uno cuando se utiliza disable_parallel_tool_use: true. No se permite texto plano ni hay razonamiento previo, y esto nunca es el valor por defecto. Úselo cuando es obligatorio obtener un resultado a través de una herramienta, pero dicha herramienta puede variar, como ocurre con los enrutadores y los clasificadores.

tool. Claude debe llamar al único herramienta que usted nombre, por lo que name es obligatorio. Se realiza al menos una llamada a dicha herramienta (puede hacerla varias veces en paralelo), y exactamente una con disable_parallel_tool_use: true. Sin texto plano, sin razonamientos previos. Se adapta a la extracción estructurada de un solo intento, un paso inicial obligatorio, o al patrón JSON a través de herramienta, por ejemplo {"type": "tool", "name": "get_weather"}. Una solicitud que nombre una herramienta que no está en tools devuelve un error 400, y forzar el uso de una herramienta no lo limita a una sola llamada a menos que se desactive el uso en paralelo.

none. Claude no puede utilizar ninguna herramienta en esta ronda aunque estén definidas, y stop_reason no puede ser tool_use. Se permite el texto plano, al igual que la exposición previa del razonamiento. Es el valor predeterminado cuando no se proporcionan tools. Úselo para forzar una ronda exclusivamente de texto, como en una síntesis final, manteniendo sin cambios el array tools. Desactiva las herramientas sin editar el array, aunque al cambiar a este modo se invalidan igualmente los bloques de mensaje en caché.

Hay varios detalles importantes que son fáciles de pasar por alto:

  • Los modos forzados omiten el preámbulo. En los modos any o tool, la API rellena automáticamente la parte correspondiente al asistente, por lo que el bloque tool_use llega sin contenido: sin explicaciones ni razonamiento, independientemente de lo que se solicite. Para que se muestre el razonamiento, se debe usar auto.
  • El pensamiento extendido requiere auto o none. No se puede combinar con el uso forzado de herramientas, y la API rechaza esa combinación.
  • Regla de conteo para disable_parallel_tool_use. auto significa como máximo uno, any y tool significan exactamente uno, mientras que el valor predeterminado false permite llamadas paralelas en los modelos Claude 4.
  • Costo de caché. Cambiar tool_choice entre solicitudes invalida los bloques de mensaje almacenados en caché. Las definiciones de herramientas y el prompt del sistema permanecen en caché, pero la conversación se vuelve a procesar.
  • Úselo junto con strict. tool_choice se encarga de que se ejecute una herramienta, y strict se asegura de que sus argumentos cumplan con el esquema. Juntos, permiten que cada paso devuelva un resultado estructurado y con tipo en cada turno.

En una solicitud:

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "any", "disable_parallel_tool_use": True},  # must call exactly one tool
    messages=messages,
)

De esto se derivan dos reglas de diseño: reserve el uso forzado de herramientas para aquellos casos en los que realmente sea necesario obtener un resultado de herramienta, y manténgase en auto si un texto narrativo constituye una respuesta aceptable. Además, cuando se fuerce el uso de una herramienta de clasificación, agregue una opción "other" a su enumeración para que Claude pueda rechazar una categoría en lugar de etiquetar incorrectamente la entrada que no puede clasificar.

3. Primera iteración: Claude solicita herramientas

La solicitud “one” reúne el array de herramientas, una tool_choice seleccionada por usted y la pregunta del usuario:

messages = [{"role": "user",
             "content": "I have a meeting in London tomorrow
                         — should I bring an umbrella, and
                         when should I leave home to be on time?"}]

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    tools=tools,                    # the three strict definitions from Section 2
    tool_choice={"type": "auto"},   # let Claude decide whether, and what, to call
    messages=messages,
)

Claude analiza la pregunta y observa que dos de las tres necesidades que se plantean no dependen una de la otra: la consulta del calendario y la previsión pueden ejecutarse simultáneamente. Por lo tanto, solicita ambos herramientas en una sola respuesta. Las llamadas independientes que se realizan juntas se denominan uso paralelo de herramientas, y se manifiestan como varios bloques tool_use en una sola vuelta.

{
  "id": "msg_01...",
  "role": "assistant",
  "stop_reason": "tool_use",
  "content": [
    { "type": "text", "text": "Let me check your meeting details and the London forecast." },
    { "type": "tool_use", "id": "toolu_01Cal", "name": "get_calendar_event",
      "input": { "query": "meeting in London tomorrow" } },
    { "type": "tool_use", "id": "toolu_01Wx", "name": "get_weather",
      "input": { "city": "London", "date": "2026-07-14" } }
  ]
}

Tres cosas destacan en esa respuesta.

  1. stop_reason es tool_use, lo que significa que la turno no está completado y Claude está esperando por usted.
  2. El array content contiene una mezcla de elementos. Un bloque text con narración suele preceder a los bloques tool_use, por lo que las llamadas a herramientas no se encuentran en una posición fija.
  3. Cada bloque tool_use tiene un id (con prefijo toolu_), un name y un objeto input que ya cumple con su esquema.

Encuentre las llamadas iterando a través del contenido y filtrando por tipo, nunca por índice:

tool_calls = [block for block in response.content if block.type == "tool_use"]

Esto genera dos llamadas aquí. Cada id sirve como identificador para obtener el resultado de dicha llamada, y la asociación entre id y resultado es la forma en que Claude relaciona las respuestas con las preguntas cuando hay varias pendientes.

Nota para los modelos Claude 4: el razonamiento extendido puede intercalarse, por lo que el proceso de deducción puede ocurrir tanto entre las llamadas a herramientas como antes de ellas. Cuando el razonamiento está activado, se deben devolver los bloques de razonamiento junto con el resto de la respuesta del asistente; de lo contrario, la continuación olvidará ese razonamiento.

4. Ejecución de las herramientas

Ahora el trabajo pasa a tu código. Claude ha solicitado dos herramientas pero no ha ejecutado ninguna. Debes ejecutar todo lo que solicitó, recopilar todos los resultados y enviar una única respuesta posterior que los incluya a todos. Claude esperará hasta que cada llamada que realizó tenga un resultado correspondiente.

Usted decide si la ejecución será paralela o secuencial, ya que Claude no lo especifica. La respuesta indica qué llamar, no cómo programarlo. Las llamadas independientes, como las búsquedas en el calendario o el clima, pueden ejecutarse simultáneamente para reducir la latencia. Cuando una depende del resultado de otra, ejecútelas en orden. El modelo da indicios de dependencia al agrupar las tareas independientes en bloques paralelos y posponer las tareas dependientes para una etapa posterior, como ocurre en la segunda iteración. Los hilos, la ejecución asíncrona o una cola de tareas son opciones que usted elige en su bucle, no la API.

Trate la ejecución en producción como una E/S poco fiable que puede fallar:

  • Asigne un tiempo de espera a cada herramienta, para que una sola llamada lenta no bloquee todo el proceso.
  • Preferir operaciones idempotentes siempre que sea posible, ya que las reintentos y reproducciones son inevitables en un agente.
  • Controlar los fallos llamada por llamada. Cuando uno de los dos herramientas en paralelo genera un error, se debe seguir enviando un resultado para él (más información sobre is_error a continuación) en lugar de interrumpir la ejecución.
  • Medir el ciclo. La latencia por herramienta, el número de llamadas, el uso de tokens y las tasas de error permiten determinar si un agente está en buen estado o si su rendimiento está empeorando gradualmente.

5. Pasos antes y después de que se ejecute una herramienta

Responsabilizarse del ciclo implica gestionar dos intervalos: el que va desde “Claude solicitó una herramienta” hasta “la herramienta se ejecutó”, y el que va desde “la herramienta se ejecutó” hasta “el resultado se devolvió”. Estos intervalos merecen tener nombres, ya que la calidad de su diseño marca la mayor diferencia entre un agente seguro y uno arriesgado.

Un paso previo al uso de la herramienta se ejecuta antes de la ejecución real. Examina la solicitud y decide si la ejecuta sin cambios, la modifica o la rechaza. Aquí es donde tiene lugar la aplicación de las reglas, con Claude proponiendo opciones y tu código tomando la decisión final.

Un paso posterior al uso de la herramienta se ejecuta después de la ejecución y antes de que el resultado regrese a Claude. Procesa la salida bruta mediante la eliminación de información, el acortamiento, el reformateo o el registro en un log.

Dentro de un bucle del Client SDK, estos pasos son simplemente funciones que rodean la ejecución de la herramienta:

for block in tool_calls:
    if not pre_tool_use(block):                       # validate / guard
        results.append(error_result(block.id, "Blocked by policy."))
        continue
    output = execute(block.name, block.input)         # run the tool
    output = post_tool_use(block, output)             # redact / log / reshape
    results.append(tool_result(block.id, output))

La etapa previa al uso de la herramienta es donde se aplican las verdaderas medidas de control, y es responsabilidad del que la implementa, no del modelo. Patrones comunes en producción:

  • Verificar los argumentos según la propia política así como según el esquema.
  • Rechazar categóricamente las operaciones destructivas: rm -rf para herramientas de shell, DROP TABLE para herramientas de base de datos.
  • Solicitar la aprobación de una persona para cualquier acción de alto impacto, como escribir en el entorno de producción.
  • Eliminar los datos confidenciales de los argumentos antes de que lleguen a un servicio externo.
  • Registrar cada intento de llamada para que las auditorías y el cumplimiento normativo tengan un registro.

Posteriormente, la etapa posterior al uso de la herramienta se ocupa de lo que regresa, eliminando credenciales o datos personales antes de que los resultados ingresen al contexto, reduciendo cargas excesivas y registrando los resultados obtenidos.

6. Devolución de los resultados

Responda a una turno tool_use con un mensaje user que contenga una lista de bloques tool_result, uno por cada bloque tool_use en la respuesta, cada uno vinculado a su tool_use_id. Añada primero el turno del asistente a la lista de mensajes en curso, y luego los resultados como un nuevo turno del usuario. La iteración uno se ve así:

{
  "role": "user",
  "content": [
    {
        "type": "tool_result",
        "tool_use_id": "toolu_01Cal",
        "content": "{\"start\": \"2026-07-14T15:00\", \"location\": \"Canary Wharf, London\"}"
    },
    {
        "type": "tool_result",
        "tool_use_id": "toolu_01Wx",
        "content": "{\"condition\": \"rain\", \"temp_c\": 12}"
    }
  ]
}

Dos formas de contenido. El content de un resultado puede ser una cadena simple, el valor de retorno serializado como en el ejemplo, o una lista de bloques de contenido. Los bloques contienen más que texto, ya que se pueden mezclar text e image, lo que permite que una herramienta de gráficos o captura de pantalla proporcione a Claude una imagen real. Las cadenas sirven para datos escalares y JSON, mientras que las listas de bloques se utilizan para salida multimodal.

Diga claramente cuando algo falló. Marque una llamada fallida con is_error: true y un mensaje explicativo como “Error: no se encontró ningún evento de calendario que coincida con esa consulta”, nunca un valor vacío o simplemente “Falló”. De todos los hábitos relacionados con los resultados, este es el más importante. Un error claro permite a Claude intentarlo de nuevo con otros argumentos, elegir una herramienta diferente o informar al usuario sobre lo sucedido, mientras que un error oculto deja el bucle atascado. Un resultado extenso también puede incluir cache_control, lo que ahorra costos de reprocesamiento posteriormente.

El contrato de continuación

Tres reglas estrictas, cada una representando un punto común de fallo:

  • Un tool_result por tool_use. Si se omite uno, la solicitud fallará con un error 400, ya que los modelos esperan una respuesta uno a uno para cada llamada a herramienta que generan.
  • Volver a enviar el mismo array de tools. Cualquier solicitud que incluya bloques tool_use o tool_result debe definir las herramientas a las que se refieren, de lo contrario se rechazará.
  • Los resultados constituyen una fase independiente. Entregue los bloques tool_result como el mensaje del usuario, ubicados al principio de su contenido. Las instrucciones adicionales en esa fase no están permitidas. Cualquier nueva instrucción se incluirá en un mensaje de usuario separado una vez que finalice la fase de la herramienta.

Cada resultado es un dato de entrada no fiable

Un tool_result no es un dato neutral. Es texto que ingresa al contexto del modelo y determina la siguiente acción. Ya sea que provenga de la web abierta, de un documento proporcionado por el usuario o de cualquier sistema fuera de su control total, puede contener instrucciones insertadas con el fin de secuestrar al agente, como la orden de ignorar instrucciones anteriores y enviar la base de datos. El modelo podría cumplir con esas instrucciones. Eso es inyección de prompts, y en un bucle de agente representa un riesgo real en producción, no algo hipotético.

Las defensas son estructurales y se encuentran dentro del bucle que usted escribe:

  • Proporcione al agente los menores privilegios necesarios. Una herramienta que le falte es una acción de la que no se le puede engañar para que la ejecute.
  • Ejecute las herramientas riesgosas en un sandbox.
  • Sanitizar los resultados para convertirlos en acciones, por ejemplo, resolviendo las rutas de archivo con respecto a un directorio fijo a fin de evitar el recorrido no autorizado.
  • Dejar que el paso posterior al uso de la herramienta redacte, eliminando todo lo que parezca una instrucción y no datos.
  • Colocar todo aquello que tenga consecuencias reales detrás de una aprobación humana.

Un resultado de herramienta nunca debería poder autorizar algo que la propia llamada a la herramienta no pudiera hacer.

Considere los resultados como un contexto que también está curando. Cada token de resultado permanece en la ventana durante el resto de la conversación, por lo que busque una salida breve y bien estructurada: un JSON consistente o campos etiquetados son mejores que el texto prosaico que el modelo debe decodificar. Los bucles que generan muchos resultados pueden utilizar la función de eliminación de resultados de herramienta, una función beta de gestión de contexto que descarta los resultados obsoletos a medida que se acerca el límite. Úsela antes de que el bucle sea lo suficientemente grande como para agotar la ventana.

7. Segunda iteración: una llamada dependiente

Ya se han obtenido ambos resultados, por lo que la API se llama una vez más, pasando toda la lista de mensajes y las definiciones de las herramientas. La respuesta del calendario indica las 15:00 en Canary Wharf, y el pronóstico indica lluvia; sin embargo, aún no se puede determinar cuándo partir, ya que el tiempo de viaje depende de una ubicación que acaba de conocer. Esa dependencia es la razón por la cual la llamada no pudo realizarse en paralelo durante la primera iteración. Tuvo que realizarse en una fase posterior.

{
  "role": "assistant",
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_02Tt",
      "name": "get_travel_time",
      "input": {
        "origin": "home",
        "destination": "Canary Wharf, London"
      }
    }
  ]
}

Ejecutas la herramienta siguiendo el mismo proceso de verificación previa al uso, ejecución y procesamiento posterior al uso, y devuelves un único resultado:

{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_02Tt", "content": "{\"minutes\": 45}" }
  ]
}

La estructura del bucle coincide con la primera iteración. Solo difieren la cantidad de llamadas y su dependencia. Ese es el patrón a asimilar: el bucle no cambia de forma de una iteración a otra y se repite hasta que Claude deja de hacer preguntas.

8. Iteración tres: síntesis y end_turn

La tercera llamada a la API encuentra a Claude con todos los datos que necesita. No se solicitan más herramientas, por lo que escribe la respuesta. end_turn reemplaza la razón de parada anterior y el bucle finaliza.

{
  "role": "assistant",
  "stop_reason": "end_turn",
  "content": [
    {
      "type": "text",
      "text": "Yes, bring an umbrella — rain is forecast in London tomorrow, around 12°C. Your meeting is at 3:00 PM in Canary Wharf, roughly 45 minutes away, so leave home by about 2:00 PM to arrive with a buffer."
    }
  ]
}

En total son tres iteraciones: dos llamadas paralelas, una llamada dependiente y una síntesis. El modelo eligió qué obtener y cuándo, y tu código decidió cómo se ejecutaba cada llamada, si estaba permitida y cómo se devolvían los resultados. El contexto crece en cada turno porque se vuelve a enviar toda la historia, pero tres turnos caben fácilmente dentro del límite.

9. Todo el bucle en código

Todo lo anterior se resume en un único bucle en el Client SDK. Este verifica stop_reason, dirige cada herramienta solicitada a través de los pasos previos y posteriores a su uso, responde a cada llamada con un resultado y continúa hasta que Claude finaliza.

import anthropic
client = anthropic.Anthropic()

messages = [{"role": "user", "content": user_request}]
MAX_ITERATIONS = 10
for _ in range(MAX_ITERATIONS):
    response = client.messages.create(
        model="claude-opus-4-8", max_tokens=1024, tools=tools, messages=messages,
    )
    messages.append({"role": "assistant", "content": response.content})
    if response.stop_reason != "tool_use":
        break   # end_turn (or another reason): done
    results = []
    for block in (b for b in response.content if b.type == "tool_use"):
        if not pre_tool_use(block):   # your guard - may block the call
            results.append({"type": "tool_result", "tool_use_id": block.id,
                            "content": "Blocked by policy.", "is_error": True})
            continue
        try:
            output = execute(block.name, block.input)   # run it (concurrently if independent)
            output = post_tool_use(block, output)   # redact / log / reshape
            results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
        except Exception as e:
            results.append({"type": "tool_result", "tool_use_id": block.id,
                            "content": f"Error: {e}", "is_error": True})
    messages.append({"role": "user", "content": results})   # one result per tool_use
print(response.content[-1].text)

El bucle explica algunos puntos clave. Usted mismo gestiona los messages ya que la API no mantiene ningún estado. Cualquier stop_reason distinto a tool_use hace que finalice el proceso. Un límite estricto de iteraciones evita que el agente se descontrole, por lo que nunca debe ejecutarse un bucle sin límites en entornos de producción. Los fallos de todo tipo, ya sean llamadas bloqueadas o excepciones, se devuelven como resultados is_error en lugar de causar la interrupción del turno. El código completo también aborda el parámetro max_tokens (lo que implica un turno truncado; en ese caso, hay que aumentar el límite o continuar) y pause_turn (un turno con herramientas del servidor que debe enviarse nuevamente). Cualquiera de estos casos puede ocurrir dentro del mismo bucle.

Lista de verificación para recordar

  • Las herramientas se declaran en el array tools con los campos name, description y input_schema, y cada solicitud paga tokens de entrada según esas definiciones.
  • tool_choice determina si se llama a una herramienta: auto, any, tool o none. El modo de razonamiento extendido solo funciona con auto y none, y strict: true obliga a que los argumentos coincidan exactamente con el esquema.
  • Un stop_reason de "tool_use" le pasa el turno a usted. Detecte las llamadas filtrando content en busca de type == "tool_use" y nunca por posición.
  • El uso paralelo de herramientas implica varios bloques tool_use en una misma respuesta. El orden de ejecución, ya sea paralelo o secuencial, lo decide usted y no el modelo. Las llamadas dependientes llegan en turnos posteriores.
  • Ejecuta todas las herramientas solicitadas y responde una sola vez: un único turno del usuario que contenga un tool_result para cada tool_use, relacionados mediante tool_use_id, manteniendo el array tools sin cambios. Si falta un resultado, se genera un código 400.
  • Tú eres responsable de los intervalos entre las ejecuciones. Valida y protege antes de ejecutar, y procesa y redacta después. Estos son pasos que tú debes escribir, no características que configurar.
  • tool_result.content puede ser una cadena o una lista de bloques. En caso de error, establece is_error: true con un mensaje útil, y mantén los resultados breves y estructurados.
  • Trata los resultados de las herramientas como contexto no fiable. El principio de menor privilegio, los entornos aislados, la sanitización y la aprobación humana en las acciones de mayor impacto son la solución contra la inyección de comandos.
  • El bucle se ejecuta hasta end_turn, mantiene su propio estado y requiere un límite estricto en el número de iteraciones.

Leer más

  • Notas prácticas: Dentro de ARD: Cómo funciona realmente la especificación de descubrimiento de recursos agente — Guía paso a paso de las Notas prácticas: Dentro de ARD: Cómo funciona realmente la especificación de descubrimiento de recursos agente: contratos, verificaciones y espacios para código adicional para los equipos que implementan este patrón.