Saltar al contenido
astorlm
Idioma: Español
← Mapa

Nivel 2

El bucle del agente

Por sí solo, un modelo no puede hacer nada: solo genera texto. El bucle del agente es lo que lo convierte en un agente. Le pasa el historial al modelo, ejecuta las herramientas que el modelo pide, le devuelve los resultados, y repite hasta que el modelo dice "listo".
1/17 Pliegues del bandoneón:
  • user
  • assistant
  • tool_result
La mamá pájaro, tu app, llama a agent.run(). Astor (el bucle) toca la pregunta en su bandoneón (messages[]) y se la lleva al Oráculo (el modelo).

EventBus

El problema

Una mamá pájaro le pregunta a un modelo "¿puedes despejar el fuerte de los cerdos?". El modelo no puede lanzar nada. Lo mejor que puede hacer es responder con un pedido de herramienta: { name: "launch_red", input: { angle: 40 } }.

Si tu código hace una sola llamada al modelo, la conversación termina ahí. Te quedas con un pedido que nadie ejecutó y sin respuesta. Si ejecutas la herramienta a mano, chocas con el mismo problema en la siguiente ronda, porque el modelo puede necesitar otra herramienta, y después otra más.

La solución

Un bucle con una única regla de salida:

  1. Envía al modelo todo el historial más la lista de herramientas disponibles.
  2. Si la respuesta termina con stopReason: "end_turn", devuélvela. Esa es la única salida normal.
  3. Si termina con "tool_use", ejecuta cada herramienta pedida, agrega los resultados al historial como bloques tool_result y vuelve al paso 1.

El modelo decide qué hacer; el bucle es el que lo hace. Esa división es la base de todos los demás patrones: todo lo demás (steering, compactación de contexto, subagentes) se engancha en algún punto de este bucle.

El elenco

El bucle contado como una pequeña aventura. Una vez que conoces al elenco, ya no queda nada por descifrar.

Astor el bucle
Un pequeño tanguero, y el único que se mueve. Le lleva la pregunta al Oráculo, corre a la resortera en cada tiro y le trae la respuesta a la mamá pájaro.
El Oráculo Provider
El modelo. Nunca toca la resortera: solo escucha el bandoneón y devuelve una nota. Naranja si necesita una herramienta, dorada si terminó.
El bandoneón messages[]
El historial, un pliegue de color por mensaje. El fuelle crece en cada vuelta, y el Oráculo escucha cada pliegue cada vez. Eso son las notas que suben flotando hacia el Oráculo.
El banco ToolRegistry
Un pájaro por herramienta: launch_red, launch_bomb y un tercero que hoy nadie necesita. Astor lanza el que nombra la nota y muestra el resultado: verde si funcionó.
La mamá pájaro agent.run()
Tu código. Hace la pregunta y espera.
Estelas, puntaje y pájaros historial, tokens, maxTurns
Cada tiro deja su estela en el cielo, igual que el historial guarda cada resultado. El puntaje son los tokens, y salta más en cada vuelta porque se vuelve a enviar todo el historial. Cada vuelta cuesta un pájaro de la fila de la barra superior, el presupuesto de turnos. Los números son ilustrativos.

El panel EventBus muestra los eventos que emite el bucle real en cada paso de la animación.

El código

Con astorlm: El mismo bucle vive en src/agent/loop.ts, con streaming, reintentos, hooks, ejecución de herramientas en paralelo y cancelación. Desde afuera se ve así.

Desde cero: Unas 40 líneas contra cualquier endpoint compatible con OpenAI, sin SDK: fetch pelado en TypeScript, la biblioteca estándar en Python. Los tres pasos de arriba están marcados en los comentarios. Completa el bloque LLM del principio con tu propio endpoint, modelo y clave.

import { OpenAIProvider, createLocalAgent, tool } from 'astorlm'
import { z } from 'zod'

// Your game's functions, wrapped as tools: one per bird.
const angle = z.number().min(10).max(80).describe('Launch angle in degrees')

const launchRed = tool({
  name: 'launch_red',
  description: 'Fling the red bird. Good against wood. Returns what fell and how many pigs are left.',
  schema: z.object({ angle }),
  execute: async ({ angle }) => level.fling('red', angle), // your code
})

const launchBomb = tool({
  name: 'launch_bomb',
  description: 'Fling the bomb bird. It explodes on impact: the one to use against stone.',
  schema: z.object({ angle }),
  execute: async ({ angle }) => level.fling('bomb', angle), // your code
})

const agent = await createLocalAgent({
  // Any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, vLLM, a proxy…
  provider: new OpenAIProvider({
    baseURL: 'http://localhost:11434/v1', // e.g. Ollama's default address
    model: 'your-model', // e.g. 'llama3.1', 'gpt-4o-mini'
    apiKey: 'YOUR_API_KEY', // local servers usually ignore it
  }),
  tools: [launchRed, launchBomb],
  maxTurns: 10, // the birds in line: a cap on the laps
})

agent.on('tool-start', (tool) => console.log('→', tool.name, tool.input))

const answer = await agent.run('The pigs took our eggs! Can you clear their fort?')

Fíjate que un error de herramienta no detiene el bucle: vuelve al modelo como texto, para que pueda corregirse en la siguiente ronda.

Cuándo usarlo y qué vigilar

Siempre que el modelo necesite actuar (leer, buscar, ejecutar algo) antes de poder responder. Si solo necesitas transformar texto, alcanza con una sola llamada, y es más barato.

  • El costo crece en cada vuelta. Mira el bandoneón y el contador de tokens: el Oráculo escucha cada pliegue en cada turno, así que cada vuelta reenvía todo lo anterior. Con suficientes turnos, el contexto se llena.
  • Define siempre maxTurns. Un modelo confundido puede seguir pidiendo herramientas para siempre. Sin un límite, el bucle también corre para siempre.