Skip to content
astorlm
← Map

Level 1

What is an agent?

"Agent" gets used for almost anything with a language model in it. The useful definition is much narrower, and it comes down to one question: who decides the next step? In an agent, the model does.
1/16 Bandoneón folds:
  • user
  • assistant
  • tool_result
A villager lost the key to the village chest. Astor, the agent loop, takes the question to the Oracle, along with two tools: ask_villager and search_area. Most of the map is still dark.

EventBus

The problem

A chatbot, a script that calls a model twice, and a system that fixes bugs on its own all get called agents. That makes it hard to know what you're building, and harder to pick the right tool for the job.

The animation is a little adventure game. A villager asks: "I lost the key to the village chest. Where is it?". The game has two functions that can help: ask_villager asks someone what they saw, and search_area searches one spot on the map. Watch who decides which of them runs, and when.

What makes it an agent

An agent isn't "an LLM with tools" and it isn't "a smart workflow". It's a system where the model chooses the control flow: which tool to call, in what order, and when to stop. Your code never says "ask the fisher, then search the oak". The model reads each result and decides the next step.

The loop that makes that possible is the next level.

In the animation

The villager your app
The question starts at a house in the village, and the answer comes back there.
Astor the loop
The agent loop, with the bandoneón of messages on his back. He goes wherever the Oracle's notes send him, and nowhere else.
The Oracle LLM
The model, in a cave between two fires. It never leaves: it only knows what's in the bandoneón. Without tools it can only talk, like Petrus the Inert.
The dock and the woods tools
ask_villager and search_area: your own functions. A screen stays dark until a path leads to it.
The lit screens control flow
The whole pattern in one picture, also on the mini-map. Each path appears only when the Oracle asks for that tool, after reading the last result. In a workflow, your code would have drawn them all before anyone asked. The mountain stays dark: the model never needed it.
Rupees and hearts tokens, maxTurns
Every visit to the Oracle costs rupees, because the whole bandoneón is read again, and a heart from the turn budget. The numbers are illustrative.

The code

With astorlm: Wrap your own functions with tool() and hand them to the agent. The loop is built in: the model picks which tools to call, in what order, and when it has enough to answer.

From scratch: Your game's functions, handed to the model as tools. The loop itself is the one from level 2; the only new thing is which tools it gets.

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"

When to use one, and what it costs

  • Use one when you can't write the steps down in advance. The key could be in a nest, under a bridge, or already sold at the shop: in code, every new case is another branch. The agent handles them with the same loop, as long as it has the tools.
  • Every step is a model call. Two tools meant three calls here. More steps mean more latency and more tokens, so cap the laps with maxTurns.
  • The same question can take a different path. Log the path the model chose, so you can see why an answer came out the way it did.
  • Tools are the boundary. The model can only do what your tools allow. What you hand it is what it can break.