Cap 1: Agentic Loops y el Ciclo tool_use
Agentic Loops y el Ciclo tool_use
Un agentic loop es el patrón fundamental para construir agentes con Claude. El modelo no solo genera texto: inspecciona resultados, decide qué herramientas usar y repite hasta completar la tarea.
El lifecycle del agentic loop
El flujo básico tiene 4 fases que se repiten:
1. Enviar request a la API (messages + tools disponibles)
2. Inspeccionar stop_reason de la respuesta
3. Si stop_reason === "tool_use" → ejecutar tools → agregar results → volver a 1
4. Si stop_reason === "end_turn" → el agente terminó, entregar respuesta
La clave es que el modelo decide cuándo ha terminado. No es el desarrollador quien programa condiciones de salida — es Claude quien evalúa si la tarea está completa.
stop_reason: la señal de control
| stop_reason | Significado | Acción |
|---|---|---|
"tool_use" | Claude quiere ejecutar una o más tools | Ejecutar, agregar results, continuar loop |
"end_turn" | Claude considera la tarea completa | Salir del loop, entregar respuesta al usuario |
"max_tokens" | Se alcanzó el límite de tokens | Manejar como caso especial (truncamiento) |
El stop_reason es la única señal confiable para controlar el flujo. Intentar detectar la terminación por otros medios es un anti-pattern.
Tool results en el historial
Cuando Claude solicita una herramienta, la respuesta incluye un tool_use content block con:
id: identificador único del llamadoname: nombre de la herramientainput: parámetros que Claude eligió
El desarrollador ejecuta la herramienta y devuelve el resultado como un mensaje con role "user" que contiene un tool_result content block:
{
role: "user",
content: [{
type: "tool_result",
tool_use_id: "toolu_abc123",
content: "El archivo tiene 42 líneas"
}]
}
Este resultado se agrega al array messages completo y se envía de vuelta a la API. Claude ve todo el historial — incluyendo sus llamadas previas y los resultados — para decidir el siguiente paso.
Ejemplo: agentic loop básico en TypeScript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
function agenticLoop(tools: Tool[], messages: Message[]) {
let response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 4096,
tools,
messages,
});
while (response.stop_reason === "tool_use") {
const toolBlocks = response.content.filter(
(b) => b.type === "tool_use"
);
const results = await Promise.all(
toolBlocks.map((block) => executeTool(block.name, block.input))
);
messages.push({ role: "assistant", content: response.content });
messages.push({
role: "user",
content: toolBlocks.map((block, i) => ({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(results[i]),
})),
});
response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 4096,
tools,
messages,
});
}
return response;
}
Puntos clave del ejemplo:
- El loop solo verifica
stop_reason === "tool_use" - Múltiples tool calls se ejecutan en paralelo con
Promise.all - Cada iteración envía el historial completo de mensajes
- No hay límite artificial de iteraciones ni parsing de texto
Model-driven decision-making
En un sistema agentic bien diseñado, Claude decide:
- Qué herramientas usar y en qué orden
- Cuándo necesita más información
- Cuándo la tarea está completa
- Cómo manejar errores de herramientas
Esto contrasta con los pre-configured decision trees donde el desarrollador programa la secuencia de pasos. El modelo es más flexible: puede adaptarse a situaciones inesperadas, reintentar de formas diferentes y razonar sobre el estado actual.
Anti-patterns del agentic loop
1. Parsear lenguaje natural para terminar
// MAL: frágil y no confiable
if (response.content[0].text.includes("tarea completada")) {
break;
}
El texto del modelo puede variar. Usar stop_reason es determinístico.
2. Caps arbitrarios de iteraciones
// MAL: corta el razonamiento prematuramente
const MAX_ITERATIONS = 3;
for (let i = 0; i < MAX_ITERATIONS; i++) { ... }
Si Claude necesita 5 iteraciones para completar una tarea, cortarlo en 3 produce resultados incompletos. Si necesitas un safety net, ponlo alto (ej: 50) y loguéalo como anomalía.
3. Buscar texto del asistente como indicador
// MAL: el modelo puede generar texto Y tool calls juntos
if (response.content.some(b => b.type === "text")) {
return response; // podría haber tool_use blocks también
}
Una respuesta puede contener tanto text blocks como tool_use blocks simultáneamente. Solo stop_reason indica la intención real.
4. Ignorar tool_use blocks múltiples
Claude puede solicitar varias herramientas en un solo turno. Ejecutar solo la primera y descartar las demás produce información incompleta para el siguiente razonamiento.
Resumen
| Concepto | Detalle |
|---|---|
| Control de flujo | stop_reason es la única señal confiable |
| Historial | Se envía completo en cada iteración |
| Decisiones | El modelo decide, no el desarrollador |
| Tool results | Se agregan como mensajes del usuario con tool_result |
| Terminación | "end_turn" — no parsing de texto |
Anterior: Índice | Siguiente: Patrones Coordinator-Subagent