Everyone is building agents these days, and half the posts make it sound like a single clever prompt is the whole game. It is not. After building agents in production for a while — and shipping an open-source framework so the lessons are public — here is what actually matters.
What An Agent Actually Is
Strip the hype and an agent is a loop with four parts:
- Perceive — read the current state (a message, an event, a tool result)
- Decide — ask the model what to do next
- Act — call a tool, which changes the state
- Repeat — until the goal is met or the loop must stop
That loop is small. Everything interesting lives in the details around it: what tools exist, how the model learns to use them, and how you stop the thing from spiraling.
Tool Calling Is The Real API
The single most important design decision is your tools. The model is only as smart as the surface area you give it.
Good tools are:
- Narrow. One tool does one thing. A
search_documentstool beats ado_everythingtool every time. - Schema-first. The tool's input schema is the contract the model talks to. A messy schema produces messy calls.
- Idempotent. Running a tool twice should not double-book an order or spam a webhook.
const tools = {
search_documents: {
description: 'Search the indexed docs for a query',
input: { query: 'string' },
run: async ({ query }) => searchIndex(query),
},
get_weather: {
description: 'Current weather for a city',
input: { city: 'string' },
run: async ({ city }) => weatherApi(city),
},
}
Notice what is missing: send_email with a freeform content field that accepts "whatever you want to say." That is how an agent accidentally tells a customer the wrong thing. Constrain the outputs that touch the real world.
Memory Is A Bounded Artifact
Beginners treat memory as "the whole transcript." That breaks for two reasons: cost, and confusion. After a few turns, the transcript is a swamp.
The pattern that works:
- Short-term memory — the current conversation, windowed.
- Working memory — a small, structured note the agent writes to itself about the goal.
- Long-term memory — retrieved snippets, not the full history.
The key idea is that memory is managed, not accumulated. The agent decides what matters, writes it down, and discards the noise. An agent that summarizes its state every few turns behaves dramatically better than one holding three hours of logs.
Knowing When To Stop
The loop's hardest skill is termination. Models left to run will happily call tools until they hit a token limit.
Three cheap guards that catch most runaway loops:
- Max iterations. A hard cap on tool calls. If you cannot finish in N steps, you do not have the right tools.
- Goal check. After each step, ask: did the goal change state measurably? If the last three steps produced no change, stop.
- Confidence threshold. Require the model to state how confident it is before acting on anything destructive.
Workflow, Not Chat
The biggest shift in my own thinking: agents work best inside a workflow, not as an open chat. You define the stages — gather, decide, act, verify — and the agent runs within the current stage instead of free-floating.
This is why the framework I shipped leans on orchestration. A pipeline of narrow, well-tooled stages is boring, and boring is what you want in production. The magic loop makes a great demo; the constrained pipeline makes a reliable product.
The Checklist Before You Ship
- Can the user see what the agent is doing, step by step?
- Is there a way to stop it manually?
- Can you replay a bad run to debug it?
- Are the dangerous tools gated behind confirmation?
Answer those four and you have an agent. Miss them and you have a demo that occasionally emails your customers.
Build the loop first. Then spend three times longer on the tools and the off-switch. That ratio has never let me down.
