A dive in five depths · LLM

How agents
really work

The arc's second lever. Somewhere around the point the industry started saying models had "gained the ability to act", something got quietly inverted — because the model gained no abilities at all. You lent it yours. It still does exactly one thing, the thing it did on the first page of the hub: it writes the next token. Everything an agent appears to do happens in code you wrote, on credentials you issued, because a parser you own read a piece of text and obeyed it. Stop wherever you like — each depth is complete on its own.

New to it? Levels 1–2 are plenty.
Building one? Level 3 is the level.
Shipping one? Level 4 is not optional.
begin the descent
LVL 1
▸ Surface · 0–10m

A loop with hands — and the hands are yours

The demystification comes first, because every question you will ever have about agents depends on it: the model does not execute anything. It writes a request. Something else decides whether to obey.

Start with what an agent is not. The model has no network access. It has no filesystem, no shell, no credentials, no permissions, no ability to reach anything at all. It is the machine the hub described — a next-token predictor in a loop — and the only thing it emits is tokens. That does not change when you "give it tools". Nothing is installed. Nothing is granted. The model is byte-identical before and after.

So what actually happens? The model writes a request. In an agreed format, in the middle of its output, it produces some text that says, in effect, I would like to call lookup_order with the id 4127. That text arrives back at your program like any other completion. And then your code — a parser, a switch statement, a function, ordinary software with your name in the git blame — reads it, decides whether to obey, executes the call, and pastes the result back into the context as more tokens.

The model finds out what happened the way it finds out everything: by reading. The result of the API call is not returned to it in any special sense. It is appended to the transcript, and on the next turn the whole transcript is replayed, and the model reads the result the same way it reads your system prompt and the user's question — as text sitting in a window. There is no other channel. There has never been another channel.

🙅
The picture almost everyone has

"The model has access to my tools"

A model reaching out into your systems and doing things — clicking, calling, writing. It is a vivid picture and it is wrong in a way that costs money, because under it every safety question becomes a model question: will it behave? can I trust it? is the next version safer? Those questions have no good answers, which is why teams holding this picture end up trying to solve architecture with adjectives in a system prompt.

What is actually running

"The model writes; my code acts"

A loop — propose → execute → observe → repeat — whose middle step is software you own, with an allow-list, an audit log, and a decision to make on every single turn. Under this picture the safety questions become architecture questions: what is this credential scoped to? which calls need approval? what can this run reach? Those questions have answers. That is the entire reason the second picture is worth the trouble of adopting.

"Function calling", named honestly. It is a format contract, not a capability switch. Dive four's post-training taught the model to emit tool calls in a parseable shape when a tool would plausibly help — the same machinery, the same scoreboards, that taught it to answer rather than continue. That is the model's half of the contract: a trained propensity to write a particular shape of text at a particular moment. Your half is a parser and an executor. Both halves are required. Neither is magic. A model with no executor attached will happily propose tool calls all day into the void, and nothing will happen, because nothing is listening.

And the loop ends by not calling anything. Every turn the model either proposes a call or answers. When it answers, your loop returns and the agent is done. That is the whole termination condition, and its bluntness is worth noticing: an agent is finished when a text predictor stops writing tool calls.

Working memory, restated. The context window is the agent's entire memory — there is no other place for anything to live. Every observation, every failed call, every plan it wrote to itself three turns ago exists only as text in the transcript, replayed from token zero on every single turn. An agent that "remembers" what it tried is an agent whose attempts are still in the window. An agent that has "forgotten" is an agent whose window was trimmed.

The takeaway for Level 1

An agent = the hub's loop + a format contract + your executor. The model only ever writes; the hands are yours — and so, therefore, is the responsibility.

One turn of that loop, under a microscope — including the two turns that go wrong ↓
LVL 2
▸ Sunlit zone · 10–50m

The loop, mechanically

One full turn, anatomised: where the toolbox lives, what "proposing" looks like on the wire, what your executor owes the model in return — and why tool design turns out to be API design for a reader made of statistics.

The toolbox is text. Before anything runs, the tools have to be described to the model, and they are described the only way anything is described to a model: as tokens in the context. Names, one-line descriptions, and a schema for the arguments, written into the system prompt (or into a dedicated section of the request that your provider serialises into the same document — the packaging varies, the mechanism does not). The model does not have a list of your functions. It has read a list of your functions. Every turn. Again.

That single fact prices something people are surprised by later: your tool definitions are prefill, on every turn, forever. Twenty tools with generous descriptions is a standing tax on every request in the run.

The model proposes. It emits text in the agreed shape — a name and a JSON-ish argument object. At this instant nothing has happened. No API has been called, no row has been written, no money has moved. There is a string in a buffer. It is worth being pedantic about this, because the entire security model of the next two levels rests on the gap between proposed and executed, and that gap is the widest lever you have.

Your parser validates. The shape may be wrong: a missing field, a string where a number belongs, a date in the wrong format, a tool name that does not exist. This is routine, it happens to every agent in production, and the correct response is not to crash. It is to write a readable error back into the context and let the model try again — because the model can read, and reading is the only way it can be told anything.

Your executor runs it — your code, your credentials, your choice. This is the line the model cannot reach, and everything you will do about safety happens on it or before it. The call may be executed, refused, rate-limited, queued for a human, or silently rewritten to something narrower. All of those are your decision, made in a language with a type system.

The observation is appended, and the transcript grows. Whatever came back — a JSON blob, a row count, an error string — is turned into text and added to the document. The next turn replays all of it: system prompt, tool schemas, task, every proposal, every observation, from token zero. That is the hub's stateless replay and dive five's invoice, iterated — and it comes with one mercy worth knowing about now rather than discovering on a bill. An agent transcript only ever grows at the end. The front of it — system prompt, tool schemas, the task, the first ten turns — is byte-identical from one turn to the next, which is the exact condition prompt caching requires. Agents are the workload prefix caching was practically invented for.

// the whole agent. the model's power is on line 6.
// the entire agent. everything else is error handling and taste.

transcript = [system_prompt_and_tool_schemas, user_task]

while True:
    out = model(transcript)        // the model writes. that is the whole of it.

    if not is_tool_call(out):
        return out                  // it answered instead of proposing — loop ends

    call = parse(out)              // YOUR parser. a wrong shape is an error, not a crash
    if not allowed(call):          // YOUR policy. allow-list, scopes, approval gates
        result = "refused: needs approval"
    else:
        result = EXECUTE(call)      // YOUR code. the model cannot reach this line.

    transcript += [out, observation(result)]   // the result is just more tokens to read

// nothing executes inside the model. it only ever writes.

Read line 6 and line 15 together. The model's entire contribution to this program is producing the string on line 6. Every consequential thing — the parse, the policy check, the execution, what goes back into the transcript — happens in code you can step through in a debugger. An agent framework is a for-loop with opinions. That is not a criticism; it is where the leverage is.

Below, one authored task walks that loop end to end — including a rejected call, an injection, and a gate.

One task, one loop — propose, execute, observe step 0 / 8
the transcript · everything the model knows 0 tokens
An authored vignette — no real APIs, no real model. The order of operations is exact, and the two beats that matter are real design rules: errors written for the model to read, and an injection defused by privilege rather than by prompt.
✦ the action ledger
0
0/8

Two beats in there are worth doing slowly. At step 4, watch what the parser sends back: not a stack trace, a sentence the model can act on — and it acts on it, on the very next turn, without a human touching anything. At step 5, a tool result arrives with an instruction hidden in it, the model does exactly what the attacker asked, and nothing bad happens — because the tool the attacker wanted was behind an approval gate. That is not the model being clever. That is Level 4's whole argument, arriving early.

Tool design is API design for a reader made of statistics. The same discipline you would apply to a public SDK, with four twists that come from the consumer being a language model.

!

The classic trap: "tool calling is a model feature you switch on"

It is a trained propensity plus your parser — a convention between two programs, one of which is a text predictor. Nothing executes inside the model, no capability is granted, and the flag in your API request does not open a door; it adds text to a prompt and promises to parse the reply.

The practical consequence: when a tool call misfires, the debugging question is almost never "is the model good enough". It is which half of the contract broke — was the tool described ambiguously, was the error unreadable, did the schema permit a shape your executor could not handle, or did the model genuinely choose wrong from a well-written menu? Three of those four are yours to fix, and they are the three that usually did it.

Loops that run once are demos. Loops that run twenty times meet arithmetic ↓
LVL 3
▸ Twilight zone · 50–200m

Planning, memory, and the odds

Two of these are less than they sound and one is more. Planning is text, memory is the transcript — and the reliability of a long chain is a multiplication nobody does before the demo.

Planning, honestly. "The agent makes a plan and then executes it" describes something real, and the something is a prompt. You ask the model to think first and act after; it writes a numbered list; the list goes into the transcript; every subsequent turn replays the list, which is how it stays on task. There is no planner module, no separate reasoning engine, no state machine holding the goal. There is a model reading its own notesthe hub's scratchpad, given a job.

This is worth saying plainly because it is often taught as new machinery, and the honest version is more useful: it works, it works for the mechanical reason that generated tokens are visible to every later token, and its failure modes are the failure modes of text. The plan can drift. The plan can be re-read after five thousand tokens of tool output and quietly outweighed. The plan can be summarised away in a context squeeze, at which point the agent is confidently pursuing a goal it can no longer see.

Memory, honestly. The transcript is the memory, and it outgrows the window — quickly, because tool results are verbose. Three options, and every agent you have used runs some blend of them:

Now the arithmetic, which is the level. Give an agent a task that takes twenty steps and a per-step success rate of 95% — a number most people would call excellent, and which is optimistic for anything involving a real API. The probability that all twenty steps go right is not 95%. It is 0.9520, which is about 36%.

Sit with that for a moment, because it is the single most load-bearing fact in agent engineering and it is almost never in the demo. Per-step success compounds. The chain is as strong as the product of its links, not the average of them, and a product of numbers below one falls off a cliff. The money line, which you can watch move in the widget below: at 95% per step, a task is a coin flip by step 14.

And this is why long autonomous runs fail the way they do — not dramatically, not with an error, but statistically. Nothing exploded. Each individual step looked fine. Somewhere around step nine a search returned a slightly wrong record, and every step after that was a competent, well-reasoned continuation of a wrong premise.

// per-step success compounds. the chain is a product.
// the least popular arithmetic in the field.

p_task = p_step ** n

//   per step     n=10     n=20     n=50
//   0.99          90%      82%      61%
//   0.95          60%      36%       8%
//   0.90          35%      12%       1%

p_checked = 1 - (1 - p) ** 2   // verify-and-retry: one extra step, squared failure

// 0.95 becomes 0.9975 per step — 95% over twenty steps, for 1.5x the step cost.
// no prompt you can write buys that. an extra step does.

The mitigations are derivable from that one equation, which is the best thing about it — you do not have to remember a list of best practices, you have to remember that it is an exponent with a base below one.

Drive the numbers yourself — the curve, the coin-flip step and the cost are all computed live from whatever you set:

presets — three runs you have seen
the compound curve — pn, accumulated one step at a time
The model, stated once. Steps are treated as independent and equally hard, and a run succeeds only if every step does — so p_task = p_step ** n. Verify & retry models a check that catches an independent failure and retries once: effective per-step reliability becomes 1 − (1 − p)², at 1.5× the cost of a plain step.
Real arithmetic on a stated model — real agents' steps are neither independent nor equally hard, so treat the shape as the lesson, not the third decimal.

The cost, stated once. Turn n replays the whole transcript, so a thirty-turn run pays the prefill bill thirty times over a context that grows at every step — and the growth is not gentle, because tool results are the bulkiest thing in the window. Two things soften it, and you have already met both: prefix caching, which fits agents unusually well because the front of the transcript never changes, and lean tool results, which is the same discipline stated as a cost control. Agents are the invoice's best customer, and the invoice is one of the reasons short loops beat long ones even when the odds do not decide it.

The takeaway for Level 3

Plans are text, memory is the transcript, and reliability is a product of per-step odds. Engineer the odds — verify, checkpoint, shorten — before engineering the prompt.

Power that compounds meets text that lies. The stakes level ↓
LVL 4
▸ Midnight zone · 200–1000m

Hands meet stakes

The one-channel problem was embarrassing in a chatbot. In an agent it is dangerous, because untrusted text now steers a system that acts — and the defences are the ones you already use for untrusted code.

The escalation. The hub's one-channel problem: instructions and data arrive in the same token stream, and the role markers that appear to separate them are learned convention, not enforced mechanism. In a chatbot the worst outcome of that was a model saying something it should not have. Dive six's poisoned page gave the problem a delivery mechanism — untrusted text promoted into the prompt by a retriever doing its job perfectly.

An agent gives it hands. Every tool result is text of unknown provenance entering the context: a web page, a customer's support ticket, an inbound email, a code comment, a filename, a row in a database that someone typed into a form. Any of it can carry a sentence addressed to the model, and that sentence arrives with the same standing as your instructions, because it arrives through the same channel as your instructions. The model has no mechanism for telling them apart. It never did.

Name the dangerous combination, because it is the most useful safety heuristic in the field. Simon Willison called it the lethal trifecta (June 2025): access to private data, exposure to untrusted content, and a channel to the outside world. An agent holding all three at once is an exfiltration engine waiting for a paragraph to trigger it — read the secrets, read the attacker's instructions, send the secrets somewhere. Remove or gate any one of the three legs and that specific attack collapses. Which makes it a design checklist rather than a warning: for any agent you are about to ship, ask which of the three legs it stands on, and whether it needs all of them at the same time.

Note what the heuristic does not ask. It does not ask how good the model is, or how carefully you worded the system prompt. It asks what the run can reach. That is deliberate, and the rest of this level is why.

Architecture, not etiquette. Five levers, and none of them is a sentence you write to the model.

Least privilege. Scope the credentials to the task, per run, with the smallest surface that can still do the job — a token that can read one customer's orders, not a token that can read all of them; an hour of validity, not a year. The point is not that the model is untrustworthy. The point is that the worst instruction it can be talked into obeying should be small, and the only thing that makes it small is the credential. This is the lever with the best ratio of effort to protection, and it is the one most often skipped because the broad token was already in the environment.

Read and write are different trust classes. Treat them differently in your executor, because their failure costs differ by orders of magnitude: reading the wrong thing wastes tokens and time, writing the wrong thing makes history — a refund issued, an email sent, a row deleted, a message posted under your company's name. A great many agents can be made dramatically safer by the single change of making every write-class tool go through a different code path from every read-class tool, with a different policy attached.

Sandboxing. Effects should land somewhere they can be inspected before they escape: a container, a scratch directory, a staging account, a dry-run mode that produces a diff instead of a mutation. This is not agent-specific engineering. It is what you already do with code you did not write.

Human gates on irreversible actions. Approval is a step in the loop, not a hope — the executor refuses, queues the proposal, surfaces it, and waits. What makes this work is precisely the gap Level 2 was pedantic about: a proposal is text, and text is harmless until your code acts on it. The design question is not "will the model try something bad" but "which actions are allowed to happen without a person seeing them first", and that is a question with a checkable answer.

Audit logs. The transcript is your flight recorder — every proposal, every parse, every execution, every observation, timestamped. Keep it. You cannot debug a run you cannot replay, and when something does go wrong the transcript is the only artefact that can tell you whether the model chose badly, the tool returned nonsense, or a paragraph in a tool result gave it new instructions.

One honest sentence to close the list: none of this is special to LLMs. Least privilege, sandboxing, staged writes, approval workflows and audit trails are what the industry already does with untrusted code. The novelty is only that the untrusted thing is text, arriving through a channel you were treating as configuration.

When agents are the wrong lever. The reflex to reach for a loop deserves the same scrutiny dive six gave the reflex to reach for retrieval. If you can draw the flowchart, write the program. A deterministic workflow — this API, then that transform, then this write — wants code: cheaper by orders of magnitude, faster, testable, and it does not hallucinate. Wrapping it in a loop and asking a model to rediscover your flowchart on every run, at a token cost per step, is paying a premium for variance you did not want. Agents earn their cost exactly where the decision path cannot be enumerated in advance: where the next action genuinely depends on what the last one returned, in a space too large to pre-plan. That is a real and valuable category. It is smaller than the marketing suggests. (And problems of behaviour — the tone is wrong, the format will not hold — are not loop problems at all; they want the last dive of this arc.)

The big misconception: "a better system prompt will keep it safe"

The prompt is convention. The hub established the mechanism and it has not changed: role markers and instructions are learned, not enforced. "Never issue a refund without approval" is a sentence in a document that a statistical text predictor is reading, alongside every other sentence in that document — including the ones an attacker wrote.

Enforcement lives in what your executor will and will not do — in code, in credentials, in gates. Write the prompt carefully, because it does raise the bar and it is free. Then assume it will someday be overridden, and make sure that day is survivable. The attacker gets to write into the same channel you do.

The test for any agent safety measure is a single question: does it still hold if the model does exactly what the attacker asked? A prompt fails that test by construction. A scoped credential, an approval gate and a sandbox pass it without needing to know what the attacker wrote.

The frontier is loops that run longer, see screens, and talk to each other ↓
LVL 5
▸ The abyss · 1000m+

Frontiers

Five pods, each complete in itself. Two are about scaling the loop outward, one is about the plumbing that finally got standardised, one is about measuring the thing — and the last one hands the arc forward.

Pod 1 One good loop versus a committee

Multi-agent systems, honestly, which means separating the case that pays from the case that does not.

Where it genuinely pays: independent subtasks with isolated context. If a task splits into parts that do not need to know about each other — read these forty documents, check these twelve repositories — then running parallel workers is a real win, and the motivation is not cleverness. It is the working-memory limit. Each worker gets a clean window containing only its own subtask, instead of one window containing everyone's tool output. Context isolation is the architecture; the parallelism is a bonus.

Where it usually does not: role-play in a single context. A "team" of a Manager, an Engineer and a Critic debating inside one transcript is one model, reading one document, predicting several characters — and paying for the whole conversation every turn. Sometimes the structure genuinely helps, in the same way that asking for a critique helps. Often it is theatre, billed by the token, and a single well-prompted pass with one verification step would have been better and a fraction of the cost.

Two costs to price before splitting a loop. Coordination is a new failure surface — handoffs, merges, workers duplicating or contradicting each other, and the classic one, a worker that succeeded at the wrong subtask because the decomposition was slightly off. And Level 3's arithmetic now multiplies across workers: a run that needs five workers each to succeed has five products multiplied together, which is a longer chain wearing a wider shape.

Pod 2 Computer use — the same loop, a wilder channel

Point the loop at a screen instead of an API and nothing changes in kind. The observation is a screenshot, which becomes tokens, because everything becomes tokens. The action is a click or a keystroke, which is a tool call with coordinates in it. Propose → execute → observe, unchanged.

Everything changes in degree, and each difference is a term in an equation you already have. The observation is enormous and noisy — a screenshot costs far more context than a JSON row, so the window fills faster and the invoice grows faster. The action space is a screen rather than a menu of four typed functions, so the proposal is less constrained and more often subtly wrong. Feedback is ambiguous: an API returns an error, a UI returns a slightly different screen, and telling "the click missed" from "the page is still loading" from "a modal appeared" is itself a perception problem. And the tasks are long, which is the part that bites: per-step reliability is lower and the step count is higher, so Level 3's exponent gets worse at both ends at once.

Honest status, as of mid-2026: it works, it is brittle, and it is improving quickly. The interesting thing about the category is not the capability but what it reveals — that the loop was never about APIs. It was about a channel of observations and a channel of actions, and a screen is just a very lossy version of both.

Pod 3 The protocol moment

Before protocols, tool integration was an N×M problem: every application had to be wired to every model harness, by hand, in a bespoke shape, and the integration you wrote for one was of no use to the next. That is the shape that always precedes a standard, in every layer of the stack that has ever had one.

A protocol turns it into N+M: applications expose their tools once, harnesses learn to speak one dialect once, and anything that speaks it can talk to anything else. MCP — the Model Context Protocol, published in 2024 — is the one that stuck. What it standardises is the plumbing of the format contract from Level 2: how a client discovers what tools a server offers, how it reads their schemas and descriptions, and how calls and results travel over a defined transport. It does not standardise the model's behaviour, the executor's policy, or anything else in this guide — the tool descriptions are still prompts, the results are still untrusted text, and the approval gates are still yours to build.

Status, date-stamped: as of mid-2026 the specification has moved to vendor-neutral governance under a foundation and is revised on a dated cadence, and the ecosystem around it is broad but still moving quickly. Treat the idea as settled and any particular integration as younger than it looks. The pod-level point survives whatever the version number is doing: a standard for discovery and schemas is plumbing, not capability, and the reason it matters is the one it always is — the N×M went away.

Pod 4 Evaluating an agent

Measure whole runs, not steps. Step accuracy is the metric that flatters: 95% per step reads like a healthy dashboard and Level 3 has already told you what it means over twenty of them. The unit that matters is end-to-end task success — did the run achieve the goal, on a fixed set of tasks, scored the way a user would score it. Where a single attempt is noisy, pass@k is the standard vocabulary: the fraction of tasks solved within k independent attempts, which also happens to price retrying honestly.

The demo-to-distribution gap. Agents demo brilliantly and regress silently, and the reason is structural rather than dishonest. A demo is one path through the space; production is the whole distribution, and the failures live in its tail — the malformed record, the empty result set, the API that returns 200 with an error in the body, the customer whose name breaks your parser. A run that fails on the tail looks exactly like a run that succeeded, right up until the last step, because every intermediate step was locally reasonable. Nothing throws.

Traces are the debugging substrate. You cannot fix a run you cannot replay. Keep every turn — the proposal, the parse, the executed call, the raw observation — and keep them per run, addressable, and diffable against a run that worked. This is the same asymmetry dive six found in retrieval: the expensive thing to grade is the final answer, and the cheap thing to inspect is everything that happened before it. Agent teams that debug quickly are, almost without exception, the ones who kept the transcripts.

Pod 5 How long can the loop run?

The frontier question of the category, and it is a race between two curves. On one side, compounding error — Level 3's exponent, which punishes length without mercy. On the other, the mitigations that fight it: verification, checkpointing, self-correction, and models with higher per-step reliability to begin with. Autonomy length is whatever those two produce when they meet.

The direction of travel is not in dispute: attempts to measure how long a task an agent can complete unaided keep producing longer answers year over year, and the trend has held for long enough to be worth planning around rather than dismissing. What that trend does not tell you is where it lands, and it is worth being precise about why extrapolating it is hard — the difficulty of a task is not proportional to its length, real steps are not independent, and a measured horizon on a benchmark suite is a claim about that suite.

The honest open question is narrower and more interesting than "how autonomous will they get": does verification scale as fast as ambition? Every mitigation in Level 3 assumes you can check a step more cheaply and more reliably than you can perform it. That assumption holds beautifully for a unit test and poorly for a judgement call, and the tasks people want agents to run for hours are disproportionately the second kind. Cheap, reliable verification is the bottleneck on autonomy — not context length, and not model quality.

Which leaves the arc one rung to climb. Everything in this guide engineers the loop around a model whose behaviour you took as given: better tools, better gates, better odds. Sometimes the thing that actually needs changing is the model itself — the last dive.