Saltar al contenido
astorlm
Idioma: Español
← Mapa

Nivel 15

Subagentes

Algunos encargos son pesados y autocontenidos. Delégalos a otro agente: empieza con un historial limpio, hace la búsqueda pesada y te devuelve solo lo que necesitas.
1/22 Pliegues del bandoneón:
  • user
  • assistant
  • tool_result
Una agencia de detectives. Arriba, el Oráculo y Astor en la central. Abajo, dos ventanas de campo vacías: todavía no hay nadie en un caso.

EventBus

El problema

Muchos pedidos esconden encargos adentro: buscar entre veinte publicaciones, leer una página larga, revisar cuarenta reseñas, recorrer una base de código para encontrar una función. El agente necesita la respuesta a cada encargo. No necesita la búsqueda pesada.

Ese es Hoarder, el agente que hace cada encargo él mismo. Cada resultado de búsqueda y cada página se suman a su historial, y el bucle reenvía todo eso en cada turno posterior. Para cuando vuelve a tu pregunta, la está leyendo debajo de una pila de publicaciones que solo necesitó un minuto. Los pedidos son pesados, el modelo se distrae, y un encargo más lo empuja más allá de la ventana.

La compactación (nivel 7) puede recortar la pila después. Mejor no armarla desde el principio.

La solución

Dale el encargo a un subagente. Un subagente es un agente completo, con su propio bucle, sus propias llamadas al modelo y sus propias herramientas, que el agente padre ve como una herramienta. Cuando el padre la llama, arranca un agente nuevo con el historial vacío y un mensaje: el informe que escribió el padre. Hace la búsqueda pesada, responde y se descarta. El padre solo recibe esa respuesta, como un resultado de herramienta común.

  • Hacerlo uno mismo

    Un agente, todas las herramientas. Ejecuta cada búsqueda y lee cada página él mismo.

    Cada resultado crudo queda en su historial, y cada turno posterior lo reenvía. Los encargos entierran la pregunta.

  • Subagente

    Delegar el encargo a otro agente, expuesto como herramienta. Empieza vacío, recibe un informe y sus propias herramientas, y responde en pocas líneas.

    El agente padre se mantiene chico y enfocado. El precio: más llamadas al modelo en total, y el subagente solo sabe lo que dice el informe.

  • Workflow

    Tu código llama a los agentes, en un orden que escribiste tú (nivel 1). Nadie decide delegar: lo decidiste tú, de antemano.

    Predecible y fácil de razonar. Solo funciona cuando conoces los pasos antes de que llegue el pedido.

Hay dos cosas que vienen gratis. Si el modelo pide dos subagentes en el mismo mensaje, el bucle los ejecuta en paralelo, como cualquier par de llamadas a herramientas. Y cada subagente puede tener un system prompt distinto, un conjunto más acotado de herramientas, incluso un modelo más chico: el explorador que lee reseñas no tiene por qué reservar nada.

Los agentes de código se apoyan en esto todo el tiempo: “explora el repo y dime dónde se maneja la autenticación” va a un subagente que hace grep en cincuenta archivos y vuelve con tres líneas.

El elenco

El mismo elenco de siempre, esta vez en una agencia de detectives.

La central el agente padre
El bucle del nivel 2: Astor, el Oráculo y el bandoneón. Sus únicas herramientas son los dos exploradores.
El telégrafo herramientas de subagentes
Donde corren milonga_scout y food_scout. Un informe baja por el cable como entrada de la herramienta; un telegrama sube como su resultado.
Una ventana de campo una ejecución de subagente
Un agente completo: un explorador, su propio Oráculo, sus propias herramientas (las dos tiendas) y su propio bandoneón. El contador de la ventana es su contexto. Cuando responde, desaparece.
El grosor de los pliegues tokens
En este nivel, un pliegue es tan grueso como pesado es su mensaje. Un telegrama de tres líneas es una lámina. Una página de reseñas es un bloque.

Mira las dos barras de arriba. Parent es lo que pesa de verdad el pedido del padre. All in 1 es lo que pesaría si el padre hubiera hecho los dos encargos él mismo, con cada página en su propio historial: termina por encima de la línea de compactación.

Las líneas subagent del registro de eventos son los eventos propios de los exploradores. El EventBus del padre nunca las ve: lo único que recibe es el inicio y el fin de cada herramienta de subagente.

El código

Con astorlm: createSubagentTool envuelve un proveedor, un system prompt y un conjunto de herramientas en una sola herramienta que el padre puede llamar. Cada llamada arranca un agente hijo nuevo, ejecuta el informe hasta el final y devuelve su texto final. Cancelar el padre cancela al hijo.

Desde cero: El bucle del nivel 2, que recibe sus herramientas como argumento. Un subagente es una herramienta cuyo cuerpo vuelve a llamar a ese bucle, con mensajes nuevos y menos herramientas.

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

// Any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, vLLM, a proxy…
const LLM = { baseURL: 'http://localhost:11434/v1', apiKey: 'YOUR_API_KEY' } // local servers usually ignore the key
const provider = new OpenAIProvider({ ...LLM, model: 'your-model' }) // e.g. 'llama3.1', 'gpt-4o-mini'

// The heavy tools: each one returns whole listings, pages or reviews.
const searchEvents = tool({
  name: 'search_events',
  description: 'Search tango events by neighborhood and date. Returns every match with its blurb.',
  schema: z.object({ neighborhood: z.string(), date: z.string() }),
  execute: async ({ neighborhood, date }) => eventsApi.search(neighborhood, date), // your code
})
const readPage = tool({
  name: 'read_page',
  description: 'Read a web page and return its text.',
  schema: z.object({ url: z.string() }),
  execute: async ({ url }) => fetchText(url), // your code
})
const searchPlaces = tool({
  name: 'search_places',
  description: 'Search restaurants near a street, with their opening hours.',
  schema: z.object({ near: z.string() }),
  execute: async ({ near }) => placesApi.search(near), // your code
})
const readReviews = tool({
  name: 'read_reviews',
  description: 'Read the latest reviews of one restaurant.',
  schema: z.object({ place: z.string() }),
  execute: async ({ place }) => placesApi.reviews(place), // your code
})

// Each subagent is a whole agent, handed to the parent as ONE tool.
// It gets its own system prompt, only the tools it needs, and a fresh history on every call.
const milongaScout = createSubagentTool({
  name: 'milonga_scout',
  description: 'Finds tango events. Give it a full brief: it knows nothing else about the conversation.',
  provider, // could be a smaller, cheaper model
  systemPrompt: 'You find milongas in Buenos Aires. Reply in 3 lines: name, address, times. No lists, no links.',
  tools: [searchEvents, readPage],
  maxTurns: 6,
})
const foodScout = createSubagentTool({
  name: 'food_scout',
  description: 'Finds places to eat. Give it a full brief: it knows nothing else about the conversation.',
  provider,
  systemPrompt: 'You find restaurants in Buenos Aires. Reply in 3 lines: name, address, why.',
  tools: [searchPlaces, readReviews],
  maxTurns: 6,
})

// The parent only sees two tools. It never gets the listings, pages or reviews: just each scout's final text.
const agent = await createLocalAgent({
  provider,
  systemPrompt: 'You plan evenings out. Send the scouts out with a clear brief each, then put their answers together.',
  tools: [milongaScout, foodScout],
  maxTurns: 6,
})

const answer = await agent.run('I’m staying in San Telmo. Find me a milonga for Saturday night, and somewhere to eat nearby before it.')
console.log(answer.content)
// Both scouts were asked for in one message, so the loop ran them in parallel.
// Cancelling the parent (abortSignal) cancels any scout still out.

Qué vigilar

  • El informe es todo lo que sabe. El subagente nunca vio la conversación. “Busca el que mencionamos” no significa nada para él. Pídele al padre, en la descripción de la herramienta, que escriba un informe completo: el objetivo, las restricciones y cómo se ve una buena respuesta.
  • Pide una forma corta y fija. Todo el punto es un resultado chico. Un system prompt como “responde en 3 líneas: nombre, dirección, horarios” evita que el explorador le pegue su pila de vuelta al padre.
  • Ahorra contexto, no dinero. La búsqueda pesada igual ocurre, en los pedidos de otro agente. A menudo cuesta más en total. Usa subagentes cuando el foco del padre lo vale, y dales un modelo más barato cuando el encargo lo permite.
  • Divide solo lo que es independiente. Dos exploradores pueden correr lado a lado porque ninguno necesita al otro. Si el segundo encargo necesita la respuesta del primero, llámalos uno después del otro, o mantenlo en un solo agente.
  • Acota sus herramientas, y limita la profundidad. Dale a cada subagente solo las herramientas que necesita su encargo, y piénsalo dos veces antes de darle subagentes propios. Cada nivel multiplica las llamadas, y una falla en lo profundo llega como una sola línea confusa.