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.
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.
# 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
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.
# 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
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.
# 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 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.
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>
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"}}
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\"}"}}
switch on instead of fishing answers out of free-form prose.
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.
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})
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.
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.
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.
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.