← back
00 · opening
Last updated August 4, 2026

From token predictor to agent.

A language model only ever does one thing: guess the next token. Here is how special tokens, a parser, and a small Python loop turn that guess into an agent that reads files and runs commands.

TOKENS IN the whole transcript MODEL next-token guess TOKENS OUT prose · or a tool call HARNESS runs the tool · appends result
The model never runs anything. It writes text. A loop around it does everything else, and the feedback is what looks like agency.
scroll
01 · the predictor

It does one thing. Guess the next token.

Strip away the chat window, the tools, the personality. What remains is a function. Each call is one forward pass: you feed it a sequence of tokens, it returns one score per vocabulary entry (the logits), and a sampler picks exactly one. Append it, run again. That is the entire machine: no memory outside the prompt, no hands, no goals.

one forward pass at a time
candidates for the next token
in essence
# the whole model, seen from outside
def generate(tokens):
    while True:
        logits = model(tokens)            # one forward pass
        tok = sample(softmax(logits))     # pick one token
        if tok == END_OF_TURN:            # stopping is a token too
            return tokens
        tokens.append(tok)                # append, repeat
the constraint: Everything an "agent" ever does must squeeze through this needle: one token at a time, chosen by probability. Even the decision to stop talking is just one more token.
02 · the transcript

Chat is a costume. The model sees one long string.

There is no "user" object and no "assistant" object inside the weights. Before every call, a chat template flattens the whole conversation into a single stream, and special tokens mark where each turn begins and ends. The model learned this shape in post-training, so when the transcript ends mid-assistant-turn, the most probable continuation is a helpful reply, and eventually the end-of-turn token, which tells the server to stop.

the same conversation, two ways
user
What's the weather in Tokyo?
assistant
It's 22°C and sunny in Tokyo right now.
in essence
# a chat template is string formatting, nothing more
def render(messages):
    out = ""
    for m in messages:
        out += f"<|im_start|>{m.role}\n{m.content}<|im_end|>\n"
    return out + "<|im_start|>assistant\n"  # cue: your turn
the trick: Roles, turns, even "stopping" are conventions written in tokens: learned in training, enforced by the server watching for the closing special token. The chat is a rendering choice.
03 · learning to act

A tool call is a sentence it learned to say.

Two ingredients turn a chat model into one that "uses tools". First, tool definitions travel as text: the JSON Schemas you pass to the API get rendered into the prompt alongside the system prompt, so the model can read what each tool does. Second, post-training drills a rigid output format: thousands of example conversations where the correct assistant turn is not prose but a fenced, machine-readable block naming a tool and its arguments. The lineage runs from ReAct prompting and Toolformer to today's models, where the format is baked into the weights.

watch the model emit a tool call, token by token

      
Press play. The model is mid-assistant-turn; the tool schemas are already in its context.
in essence · what the training data looks like
# one of thousands of supervised examples
prompt = render(system + tool_schemas + "user: weather in Tokyo?")
target = "<tool_call>{\"name\": \"get_weather\", " \
         "\"arguments\": {\"city\": \"Tokyo\"}}</tool_call>"
# training makes this exact shape the most probable reply
the punchline: The model executes nothing. "Calling a tool" means predicting the tokens of a tool call, with high confidence in the format because so many training examples looked exactly like this. It is writing down an intention.
04 · the api contract

The server catches it and hands you structure.

The inference server watches the token stream. When the model opens a tool-call block, the server lets it finish, then stops the turn, parses the JSON, and returns it as a structured object with a stop_reason of tool_use. You never see the raw special tokens. And because sampling picks one token at a time, the server can even enforce grammar: constrained decoding masks any token that would break the schema, which is what "strict" tool modes promise.

three dialects, one idea
open weights · raw stream

Special tokens

What actually comes out of a Qwen- or Hermes-style model. The tags are reserved tokens.

<tool_call>
{"name": "get_weather",
 "arguments":
   {"city": "Tokyo"}}
</tool_call>
anthropic · parsed block

tool_use block

The same intention, parsed server-side into a content block. stop_reason: "tool_use".

{"type": "tool_use",
 "id": "toolu_01A...",
 "name": "get_weather",
 "input":
   {"city": "Tokyo"}}
openai · parsed array

tool_calls

Same again, with arguments left as a JSON string and the stop reason renamed: finish_reason: "tool_calls".

{"id": "call_x9q",
 "type": "function",
 "function": {
   "name": "get_weather",
   "arguments":
     "{\"city\":\"Tokyo\"}"}}
the contract: Every provider speaks a different dialect, but the deal is identical: the model emits formatted text, the server parses it into JSON, and your code gets something it can switch on instead of fishing answers out of free-form prose.
05 · the loop

The agent is a while loop.

Here is the part that surprises people: the core of every coding agent is embarrassingly small. A harness sends the transcript plus tool schemas, checks stop_reason, actually runs the requested function, appends the result as a new message, and calls the API again. The model reads its own tool result as fresh tokens and predicts the next move. Step through one task below.

one turn of an agent · read a file, then answer
01 SEND transcript 02 PREDICT tokens 03 BRANCH stop_reason 04 EXECUTE for real 05 APPEND result · loop while stop_reason == "tool_use" · END_TURN
01 / send
The whole transcript goes up. Every time.
Press play, or step through one full task.
in essence · the whole agent
TOOLS = {"read_file": read_file, "run_bash": run_bash}

def agent(task):
    messages = [{"role": "user", "content": task}]
    while True:
        r = api.messages.create(model="...",
                                tools=SCHEMAS, messages=messages)
        messages.append({"role": "assistant", "content": r.content})
        if r.stop_reason != "tool_use":
            return r                      # done: plain text answer
        results = []
        for call in r.content:            # may be several in one turn
            if call.type == "tool_use":
                out = TOOLS[call.name](**call.input)   # the real world
                results.append({"type": "tool_result",
                                "tool_use_id": call.id,
                                "content": out})
        messages.append({"role": "user", "content": results})
the surprise: Nothing persists between calls. The API is stateless, so the "agent" is reconstructed from the transcript on every single iteration. Memory, progress, personality: all of it lives in the messages array your loop keeps appending to.
06 · the composition

Weak parts. Strong whole.

Each piece is helpless alone. The predictor has judgment but no hands. The tools have hands but no judgment. The loop has neither, it just moves tokens. Composed, they close a feedback cycle: the model perceives the world through tool results rendered as tokens, decides by next-token prediction, and acts through the harness. That cycle, run enough times, is what we call agency.

same weights, different body
from the weights

What the model brings

judgment
Which tool, which arguments, when to stop: all next-token predictions shaped by training.
the format
Emitting parseable tool calls is a learned reflex, drilled in post-training.
recovery
An error message comes back as tokens too. The probable continuation after a failure is a retry or a new plan.
from the harness

What the loop brings

hands
The actual read_file, bash, browser. The model only ever names them.
time
The while loop grants as many turns as the task needs. One call is a chat; many calls are an agent.
guardrails
Permissions, sandboxes, context compaction, sub-agents. This is where harnesses differ: same model, very different agent.

This is why the harness matters so much. Claude Code, a CI bot, and a chat app can share identical weights and behave like different species, because the harness decides which tools exist, what the system prompt promises, and how the context window is managed. I took two production harnesses apart in the agent harness explainer; this page is the physics underneath that one.

the frame: An agent is a token predictor, a parser, and a for-loop. The intelligence lives in the weights. The agency lives in the loop.
07 · fin

Tokens all the way down.

A guess becomes a sentence. A sentence, in a learned format, becomes an intention. A parser turns the intention into structure, a loop turns the structure into action, and the result comes back as more tokens for the next guess. Nowhere in that chain does anything but text ever cross the model's boundary.

From here: the agent harness shows what production loops add on top, api, mcp & the two acps covers how tools get discovered and shared, and pre-training vs post-training covers where the reflexes in section 03 come from.