From token predictor to agent.
A language model does only one thing: it guesses the next token. This page shows how special tokens, a parser, and a small Python loop turn that guess into an agent that reads files and runs commands.
All the model does is guess the next token.
Take away the chat window, the tools, and the personality, and what is left is a function. Each call is one forward pass. You give it a sequence of tokens, and it returns one score for every entry in its vocabulary (the logits). Then a sampler picks one token. You add that token to the end of the sequence and call the function again. There is nothing else. The model has no memory outside the prompt, no way to act on anything, and 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
You see a chat. The model sees one long string.
There is no "user" object and no "assistant" object inside the weights. Before every call, a chat template joins the whole conversation into one long string, and special tokens mark where each turn begins and ends. The model learned this layout in post-training. So when the string ends at the start of an assistant turn, the most probable continuation is a helpful reply, followed by 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 text the model learned to write.
Two things turn a chat model into one that "uses tools". First, the tool definitions are sent as text. The JSON Schemas you pass to the API are written into the prompt next to the system prompt, so the model can read what each tool does. Second, post-training teaches a strict output format. The model sees thousands of example conversations where the correct assistant turn is a machine-readable block, wrapped in special tags, that names a tool and its arguments. The idea goes back to ReAct prompting and Toolformer. In today's models, the format is trained 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 turns the tool call into structured data.
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. Because sampling picks one token at a
time, the server can also force the output to fit the schema. With
constrained decoding, it blocks
any token that would break the schema. That is what "strict" tool modes promise.
Special tokens
The raw output 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 tool call, parsed by the server into a content block, with stop_reason: "tool_use".
{"type": "tool_use",
"id": "toolu_01A...",
"name": "get_weather",
"input":
{"city": "Tokyo"}}
tool_calls
The same again, but the arguments stay a JSON string and the stop reason has a different name: finish_reason: "tool_calls".
{"id": "call_x9q",
"type": "function",
"function": {
"name": "get_weather",
"arguments":
"{\"city\":\"Tokyo\"}"}}
switch on, instead of having to dig answers out of free-form prose.
The agent is a while loop.
The core of every coding agent is much smaller than most people expect.
A harness sends
the transcript and the tool schemas, checks stop_reason, runs the requested function,
adds the result to the transcript as a new message, and calls the API again. The model reads the tool result as
new tokens and predicts what to do next. 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})
Three limited parts add up to an agent.
None of the pieces can do much alone. The predictor has judgment but no way to act. The tools can act but have no judgment. The loop has neither. It only passes tokens back and forth. Put together, they form a feedback cycle. The model perceives the world through tool results, which arrive as tokens. It decides by predicting the next token. It acts through the harness. Run that cycle enough times and you get what we call agency.
What the model brings
- judgment
- Which tool to call, with which arguments, and when to stop. Each of these is a next-token prediction shaped by training.
- the format
- Writing tool calls that a parser can read is a habit the model learned in post-training.
- recovery
- An error message comes back as tokens too. After a failure, the most probable continuation is a retry or a new plan.
What the loop brings
- hands
- The real
read_file,bash, and browser. The model only ever names them. - time
- The while loop keeps going for as many turns as the task needs. One call is a chat. Many calls in a loop are an agent.
- guardrails
- Permissions, sandboxes, context compaction, sub-agents. This is where harnesses differ, so the same model can make a very different agent.
This is why the harness matters so much. Claude Code, a CI bot, and a chat app can run the same weights and still behave very differently, because the harness decides which tools exist, what the system prompt says, and how the context window is managed. I took two production harnesses apart in the agent harness explainer. This page covers the mechanics underneath that one.
The model only ever reads and writes text.
The model guesses tokens one at a time, and they add up to a sentence. When the sentence is in the learned tool-call format, it states an intention. A parser turns the intention into structured data, the loop carries it out, and the result comes back as more tokens for the next guess. At no point does anything except text go into or come out of the model.
Where to go next: the agent harness shows what production loops add on top, api, mcp & the two acps covers how tools are discovered and shared, and pre-training vs post-training covers how the model learns the tool-call format from section 03.