Cargando

El bucle del agente explicado

Lección 47 min
Cargando

El bucle del agente explicado

Has hecho llamadas a la API, pero una sola llamada solo devuelve una respuesta. Si quieres automatizar un flujo de trabajo, Claude necesita actuar, revisar el resultado, decidir qué sigue, y continuar. Ese patrón es a lo que la gente se refiere cuando habla de flujos de trabajo agénticos.

Qué es realmente un agente

Un agente es una versión autónoma de Claude, que ejecuta ambos lados del bucle de mensajería sin un humano en medio. Un agente recibe una tarea, elige una herramienta y ejecuta código en un bucle hasta que Claude decide que la tarea está terminada.

La forma más sencilla de implementar un bucle de agente se ve así:

  1. Envía un mensaje a Claude con herramientas disponibles.
  2. Claude responde ya sea con una respuesta final o con una solicitud para usar una herramienta que hayas definido.
  3. Tu código ejecuta esa herramienta.
  4. Envías el resultado de vuelta a Claude.
  5. Repite mientras la razón de detención (stop reason) sea tool_use. end_turn significa que Claude ha terminado, y cualquier otra razón de detención también finaliza el bucle.

Piénsalo como una conversación donde los turnos se alternan: el usuario inicia todo, el agente llama a una herramienta, la herramienta devuelve un resultado, y el agente continúa hasta que tiene una respuesta.

Un ejemplo mínimo que funciona

Para ver este bucle ejecutarse de principio a fin sin meter una base de datos o una interfaz de usuario, conectaremos una herramienta falsa llamada get_weather y le preguntaremos a Claude qué usar en Austin hoy. Claude no tiene forma de saber el clima por sí mismo, así que tiene que llamar a la herramienta, leer el resultado y luego darte una respuesta.

Aquí está el script completo:

python
import anthropic

client = anthropic.Anthropic()

# El arreglo tools le indica a Claude qué está disponible:
# un name, una description y un JSON schema para las entradas.
tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "The city to get weather for",
                }
            },
            "required": ["city"],
        },
    }
]

# run_tool es solo una búsqueda codificada directamente.
# En una app real, esto llamaría a tu base de datos, una API, lo que sea.
def run_tool(name, tool_input):
    if name == "get_weather":
        return f"Weather in {tool_input['city']}: 95F, sunny"
    raise ValueError(f"Unknown tool: {name}")

messages = [
    {"role": "user", "content": "What should I wear in Austin today?"}
]

# El bucle del agente. Cada iteración envía mensajes a Claude
# y actúa según el stop reason de la respuesta.
while True:
    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )

    if response.stop_reason == "end_turn":
        # Claude terminó. Imprime el texto final y sal del bucle.
        for block in response.content:
            if block.type == "text":
                print(block.text)
        break

    elif response.stop_reason == "tool_use":
        # Busca los bloques de uso de herramientas en la respuesta y ejecuta cada uno.
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                result = run_tool(block.name, block.input)
                tool_results.append(
                    {
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result,
                    }
                )

        # Agrega la respuesta del asistente y nuestros resultados de herramientas
        # de vuelta a messages, luego vuelve a iterar para que Claude pueda responder.
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": tool_results})

    else:
        # Cualquier otro stop reason, como max_tokens o refusal.
        # Nada en messages cambió, así que detente aquí. Volver a iterar
        # pagaría por la misma solicitud una y otra vez.
        print(f"Stopped early: {response.stop_reason}")
        break

Tres cosas a notar:

  • El arreglo de herramientas (tools array) le indica a Claude qué está disponible: un nombre, una descripción y un esquema JSON para las entradas.
  • run_tool es simplemente una búsqueda codificada de forma fija. En una aplicación real, esto consultaría tu base de datos, una API, lo que sea.
  • El bucle es el bucle del agente. Cada iteración envía los mensajes a Claude y actúa según la razón de detención de la respuesta. En end_turn, Claude ha terminado, así que imprime el texto final y detente. En tool_use, encuentra los bloques de uso de herramientas, ejecuta cada uno, agrega la respuesta del asistente y tus resultados de herramientas de vuelta a messages, y vuelve a iterar para que Claude pueda responder. En cualquier otro caso, imprime la razón de detención y detente.

No dejes fuera esa última rama. Sin ella, una respuesta que se corta en max_tokens no agrega nada a messages, así que el bucle envía la misma solicitud de nuevo y pagas por ello cada vez. La documentación enumera cada razón de detención y qué hacer al respecto.

Ejecutándolo

Cuando ejecutes el script, verás dos turnos:

  1. Turno uno: la razón de detención es tool_use. Claude solicita get_weather para Austin, y tu código devuelve la temperatura y las condiciones.
  2. Turno dos: la razón de detención es end_turn, y Claude te dice que uses algo ligero y transpirable.

Salida de terminal del bucle del agente: el turno 1 se detiene con tool_use y llama a get_weather para Austin, luego el turno 2 se detiene con end_turn y Claude imprime sus recomendaciones finales de vestimenta

Dos llamadas a la API, una ejecución de herramienta, una respuesta final. Eso es todo el bucle. Todo lo que construyas con la Claude API va a ser similar a esto.

El mismo bucle en producción

En un entorno real, este mismo bucle impulsa algo como un endpoint de revisión automática: un agente de cumplimiento que lee un informe estructural, busca los códigos de construcción relevantes mediante una herramienta, y escribe hallazgos de riesgo de vuelta en la base de datos uno por uno a medida que trabaja.

Un panel de revisión de cumplimiento que lista informes estructurales subidos, cada uno con un botón Ejecutar revisión automática que activa al agente

La forma del bucle es idéntica a la que acabas de ejecutar. Las diferencias son:

  • Herramientas reales en lugar de una búsqueda simulada del clima.
  • Los resultados se transmiten de vuelta a la interfaz de usuario como eventos enviados por el servidor.
  • Los hallazgos se persisten en una tabla de hallazgos de riesgo.

La traza de revisión de un agente de cumplimiento en ejecución: docenas de llamadas a herramientas buscando en la biblioteca de códigos de construcción y consultando secciones de código específicas mientras el bucle itera

Resumen

  • Un agente es Claude en un bucle: observar, decidir, actuar, repetir.
  • El bucle es simple: envía mensajes con herramientas, ejecuta cualquier herramienta que Claude solicite, devuelve el resultado, y detente cuando la razón de detención sea cualquier cosa menos tool_use.
  • Tú posees el bucle y las herramientas. Claude posee el razonamiento.
  • La misma forma de bucle escala desde una demo simulada del clima hasta un agente de cumplimiento en producción — solo cambian las herramientas y la plomería.
  • Cuando no quieras poseer el bucle, los agentes gestionados ejecutan este mismo bucle por ti en la infraestructura de Anthropic.