You know a token is one entry in a fixed dictionary. So how does a whole conversation — system, you, the model — become tokens? It's folded into one long formatted string, with reserved control tokens marking who is speaking.
Your chat app shows tidy bubbles. What it hands the model is plainer: a list of items, each tagged with a role and the content that was said.
Three roles.system sets the standing rules, user is you, assistant is the model. Every message is exactly one of these.
Order is the memory. The list is read top to bottom; the model always writes the last turn — an empty assistant slot waiting to be filled.
It's just data. No colours, no bubbles — an array of {role, content} objects, exactly what your app's API sends.
THE CONVERSATION ARRAY
↳ Tap a role to see what it's for — the model always answers as the final assistant turn.
system — the standing instructions: who the model is and the rules it follows for the whole chat. Usually set once, first.
NODE 02 / 03
The template folds the list into one string
A chat template is a small function. It walks the list and emits one flat string, wrapping every turn in reserved control tokens that mark where it starts, who is speaking, and where it ends.
Every turn gets delimiters — an opener, the role name, the content, then a closer — repeated for each message.
This exact shape is ChatML (<|im_start|> … <|im_end|>). Llama does the same job with different tokens — switch below.
The string ends with an empty assistant opener. That trailing delimiter is the model's cue to start writing its reply.
LIST → TEMPLATE → ONE STRING
↳ Edit the user line, switch templates, or reveal the IDs — the green pieces are the reserved control tokens.
ChatML — the format GPT-style models use. `<|im_start|>role` opens a turn and `<|im_end|>` closes it.
NODE 03 / 03
A control token is one entry, not a word
A token is one entry in the dictionary — one integer ID (the token-dictionary lesson). Control tokens are extra, reserved entries in that same dictionary.
As plain text it's a dozen pieces. If <|im_start|> were ordinary characters, the tokenizer would shatter it into many little tokens.
As a reserved token it's exactly one ID. The dictionary holds a single dedicated entry — one integer the model reads instantly.
That's what keeps structure safe. A user can type the characters <|im_start|>, but they stay ordinary text — never the real control token — so nobody can fake a system turn.
PLAIN TEXT vs RESERVED TOKEN
↳ Pick a control token — the left panel is what it would cost as plain text, the right is its single reserved ID.
<|im_start|> — opens a turn and is followed by the role name. One reserved id: 100264.
EXPLAIN IT BACK
Your app shows separate bubbles for system, user and assistant. What does the model actually receive?
NEXT · MULTI-TURN & FORMATTING
One exchange is now one string. Next, multi-turn & formatting: how longer chats, tool results, and the model's own reasoning all fold back into these same role blocks.