Pular para o conteúdo
astorlm
Idioma: Português
← Mapa

Nível 15

Subagentes

Algumas tarefas são pesadas e independentes. Passe-as para outro agente: ele começa com um histórico limpo, faz o trabalho pesado de busca e devolve só o que você precisa.
1/22 Dobras do bandoneón:
  • user
  • assistant
  • tool_result
Uma agência de detetives. Em cima, o Oráculo e Astor na central. Embaixo, duas janelas de campo vazias: ainda não tem ninguém num caso.

EventBus

O problema

Muitas requisições escondem tarefas dentro delas: vasculhar vinte anúncios, ler uma página longa, passar por quarenta avaliações, percorrer uma base de código para achar uma função. O agente precisa da resposta de cada tarefa. Ele não precisa do trabalho pesado de busca.

Esse é o Hoarder, o agente que faz cada tarefa sozinho. Cada resultado de busca e cada página entram no histórico dele, e o loop reenvia tudo isso a cada turno seguinte. Quando ele volta à sua pergunta, já a está lendo debaixo de uma pilha de anúncios de que só precisou por um minuto. As requisições ficam pesadas, o modelo se distrai, e mais uma tarefa o empurra para além da janela.

A compactação (nível 7) pode aparar a pilha depois. Melhor nem montá-la.

A solução

Dê a tarefa a um subagente. Um subagente é um agente completo, com o próprio loop, as próprias chamadas ao modelo e as próprias ferramentas, que o agente pai enxerga como uma ferramenta. Quando o pai a chama, um agente novo começa com o histórico vazio e uma mensagem: o briefing que o pai escreveu. Ele faz o trabalho pesado, responde e é descartado. O pai só recebe essa resposta, como um resultado de ferramenta comum.

  • Fazer sozinho

    Um agente, todas as ferramentas. Ele roda cada busca e lê cada página por conta própria.

    Cada resultado bruto fica no histórico dele, e cada turno seguinte o reenvia. As tarefas soterram a pergunta.

  • Subagente

    Passar a tarefa para outro agente, exposto como ferramenta. Ele começa vazio, recebe um briefing e as próprias ferramentas, e responde em poucas linhas.

    O agente pai fica pequeno e focado. O preço: mais chamadas ao modelo no total, e o subagente só sabe o que o briefing diz.

  • Workflow

    O seu código chama os agentes, numa ordem que você escreveu (nível 1). Ninguém decide delegar: você decidiu, de antemão.

    Previsível e fácil de entender. Só funciona quando você conhece os passos antes de a requisição chegar.

Duas coisas vêm de brinde. Se o modelo pede dois subagentes na mesma mensagem, o loop os roda em paralelo, como quaisquer duas chamadas de ferramenta. E cada subagente pode ter um system prompt diferente, um conjunto mais restrito de ferramentas, até um modelo menor: o investigador que lê avaliações não tem por que reservar nada.

Agentes de código se apoiam nisso o tempo todo: “explore o repositório e me diga onde a autenticação é tratada” vai para um subagente que faz grep em cinquenta arquivos e volta com três linhas.

O elenco

O mesmo elenco de sempre, desta vez numa agência de detetives.

A central o agente pai
O loop do nível 2: Astor, o Oráculo e o bandoneón. As únicas ferramentas dele são os dois investigadores.
O telégrafo ferramentas de subagentes
Onde rodam milonga_scout e food_scout. Um briefing desce pelo fio como entrada da ferramenta; um telegrama sobe como resultado dela.
Uma janela de campo uma execução de subagente
Um agente completo: um investigador, o próprio Oráculo, as próprias ferramentas (as duas lojas) e o próprio bandoneón. O contador da janela é o contexto dele. Quando ele responde, some.
A espessura das dobras tokens
Neste nível, uma dobra é tão grossa quanto a mensagem é pesada. Um telegrama de três linhas é uma lasca. Uma página de avaliações é um bloco.

Observe as duas barras no topo. Parent é o quanto a requisição do pai pesa de verdade. All in 1 é o quanto pesaria se o pai tivesse feito as duas tarefas sozinho, com cada página no próprio histórico: ela termina acima da linha de compactação.

As linhas subagent no log de eventos são os eventos dos próprios investigadores. O EventBus do pai nunca as vê: tudo o que ele recebe é o início e o fim de cada ferramenta de subagente.

O código

Com astorlm: createSubagentTool embrulha um provedor, um system prompt e um conjunto de ferramentas numa única ferramenta que o pai pode chamar. Cada chamada inicia um novo agente filho, roda o briefing até o fim e devolve o texto final dele. Cancelar o pai cancela o filho.

Do zero: O loop do nível 2, recebendo as ferramentas como argumento. Um subagente é uma ferramenta cujo corpo chama esse loop de novo, com mensagens novas e menos ferramentas.

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.

O que observar

  • O briefing é tudo o que ele sabe. O subagente nunca viu a conversa. “Procure aquele de que falamos” não significa nada para ele. Diga ao pai, na descrição da ferramenta, para escrever um briefing completo: o objetivo, as restrições e como é uma boa resposta.
  • Peça um formato curto e fixo. A ideia toda é um resultado pequeno. Um system prompt como “responda em 3 linhas: nome, endereço, horários” impede o investigador de colar a pilha dele de volta no pai.
  • Economiza contexto, não dinheiro. O trabalho pesado continua acontecendo, nas requisições de outro agente. Muitas vezes custa mais no total. Use subagentes quando o foco do pai valer a pena, e dê a eles um modelo mais barato quando a tarefa permitir.
  • Divida só o que é independente. Dois investigadores podem rodar lado a lado porque nenhum precisa do outro. Se a segunda tarefa precisa da resposta da primeira, chame um depois do outro, ou mantenha tudo num só agente.
  • Restrinja as ferramentas, e limite a profundidade. Dê a cada subagente só as ferramentas de que a tarefa precisa, e pense duas vezes antes de dar a ele subagentes próprios. Cada nível multiplica as chamadas, e uma falha lá no fundo chega como uma única linha confusa.