Nível 1
O que é um agente?
- user
- assistant
- tool_result
EventBus
O problema
Um chatbot, um script que chama um modelo duas vezes e um sistema que corrige bugs sozinho: todos são chamados de agentes. Isso dificulta saber o que você está construindo, e mais ainda escolher a ferramenta certa para o trabalho.
A animação é um pequeno jogo de aventura. Um aldeão pergunta: "Perdi a chave do baú da vila. Onde ela está?". O
jogo tem duas funções que podem ajudar: ask_villager pergunta a alguém o que viu, e
search_area procura num ponto do mapa. Repare em quem decide qual delas roda, e quando.
O que faz dele um agente
Um agente não é "um LLM com ferramentas" nem "um workflow esperto". É um sistema em que o modelo escolhe o fluxo de controle: qual ferramenta chamar, em que ordem e quando parar. O seu código nunca diz "pergunte ao pescador e depois procure no carvalho". O modelo lê cada resultado e decide o próximo passo.
O loop que torna isso possível é o próximo nível.
Na animação
- O aldeão seu app
- A pergunta sai de uma casa da vila, e a resposta volta para lá.
- Astor o loop
- O loop do agente, com o bandoneón de mensagens nas costas. Ele vai aonde os bilhetes do Oráculo mandam, e a nenhum outro lugar.
- O Oráculo LLM
- O modelo, numa caverna entre duas fogueiras. Ele nunca sai: só sabe o que está no bandoneón. Sem ferramentas, só consegue falar, como Petrus, o Inerte.
- O cais e a floresta ferramentas
-
ask_villageresearch_area: as suas próprias funções. Uma tela fica no escuro até que um caminho leve até ela. - As telas acesas fluxo de controle
- O padrão inteiro numa imagem, também no minimapa. Cada caminho só aparece quando o Oráculo pede aquela ferramenta, depois de ler o último resultado. Num workflow, o seu código teria desenhado todos eles antes de alguém perguntar. A montanha continua no escuro: o modelo nunca precisou dela.
- Rupias e corações tokens, maxTurns
- Cada visita ao Oráculo custa rupias, porque o bandoneón inteiro é lido de novo, e um coração do orçamento de turnos. Os números são ilustrativos.
O código
Com astorlm: Embrulhe as suas próprias funções com tool() e entregue-as ao agente.
O loop já vem embutido: o modelo escolhe quais ferramentas chamar, em que ordem e quando já tem o suficiente
para responder.
Do zero: As funções do seu jogo, entregues ao modelo como ferramentas. O loop em si é o do nível 2; a única novidade é quais ferramentas ele recebe.
import { OpenAIProvider, createLocalAgent, tool } from 'astorlm'
import { z } from 'zod'
// Any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, vLLM, a proxy…
const 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
})
// Your own game functions, wrapped as tools: a name, a description and an input schema.
const askVillager = tool({
name: 'ask_villager',
description: 'Ask someone in the village what they saw. Returns what they say.',
schema: z.object({ name: z.string().describe('Who to ask, e.g. "fisher" or "baker"') }),
execute: async ({ name }) => world.villager(name).say(),
})
const searchArea = tool({
name: 'search_area',
description: 'Search one spot on the map. Returns what is found there, if anything.',
schema: z.object({ area: z.string().describe('A named spot, e.g. "old oak" or "bridge"') }),
execute: async ({ area }) => world.search(area),
})
// The model decides which tools to call, in what order, and when to stop.
const agent = await createLocalAgent({
provider,
tools: [askVillager, searchArea],
maxTurns: 5, // a cap on the laps, in case it never settles
})
await agent.run('I lost the key to the village chest. Where is it?') // "In the crow's nest on the old oak"
// An agent that finds a villager's lost key. Plain TypeScript, no SDK.
// Your game. In a real one these read the world state and the characters' dialogue.
async function askVillager(name: string) {
const seen: Record<string, string> = {
fisher: 'A crow flew off with something shiny, toward the old oak in the woods.',
}
return seen[name] ?? `The ${name} saw nothing.`
}
async function searchArea(area: string) {
return area === 'old oak' ? "In the crow's nest: a small brass key." : `Nothing at the ${area}.`
}
// The same functions, as tools the model can ask for. Each one returns text.
const tools = {
ask_villager: ({ name }: { name: string }) => askVillager(name),
search_area: ({ area }: { area: string }) => searchArea(area),
}
// runAgent is the loop from level 2, with the tools passed in. Your code never says
// "ask the fisher, then search the oak": the model picks each step after reading the last result.
export async function agent(question: string): Promise<string> {
return runAgent(question, tools)
}
# An agent that finds a villager's lost key. Standard library only, no SDK.
# Your game. In a real one these read the world state and the characters' dialogue.
def ask_villager(name):
seen = {"fisher": "A crow flew off with something shiny, toward the old oak in the woods."}
return seen.get(name, f"The {name} saw nothing.")
def search_area(area):
return "In the crow's nest: a small brass key." if area == "old oak" else f"Nothing at the {area}."
# The same functions, as tools the model can ask for. Each one returns text.
TOOLS = {
"ask_villager": lambda args: ask_villager(args["name"]),
"search_area": lambda args: search_area(args["area"]),
}
# run_agent is the loop from level 2, with the tools passed in. Your code never says
# "ask the fisher, then search the oak": the model picks each step after reading the last result.
def agent(question):
return run_agent(question, TOOLS)
Quando usar um, e quanto custa
- Use quando você não consegue escrever os passos de antemão. A chave pode estar num ninho, debaixo de uma ponte ou já vendida na loja: em código, cada caso novo é mais um ramo. O agente resolve todos com o mesmo loop, desde que tenha as ferramentas.
-
Cada passo é uma chamada ao modelo. Duas ferramentas significaram três chamadas aqui. Mais
passos significam mais latência e mais tokens, então limite as voltas com
maxTurns. - A mesma pergunta pode seguir outro caminho. Registre o caminho que o modelo escolheu, para poder ver por que uma resposta saiu do jeito que saiu.
- As ferramentas são o limite. O modelo só pode fazer o que as suas ferramentas permitem. O que você entrega a ele é o que ele pode quebrar.