LLM FundamentalsQwen2.5-0.5B-Instruct · transformers 5.17 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
18 small wins to finish your pathNext lesson

Chat templates: how messages become one text

You send a chat model messages with roles. It reads one text: a chat template writes the messages into it with special markers, and that text is tokenized.

Example
messages = [
    {"role": "system", "content": "You sort support tickets."},
    {"role": "user", "content": "My parcel has not arrived"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
print(text)

Each message becomes <|im_start|>, the role, the content and <|im_end|>. Those markers are single special tokens the model learned during training to read as message boundaries.

add_generation_prompt=True adds the opening of an assistant message at the end, with nothing after it. The model's next token is the start of its reply, and when it writes <|im_end|> the reply is over: the end token from lesson 7.

Leave the system message out and this template writes one in for you: You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Models differ here, which is one reason the same messages can behave differently on two models.

A helper for the rest of the course

Example
def reply(messages, max_new_tokens=40):
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    inputs = tokenizer(text, return_tensors="pt")
    output = model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=False)
    new_tokens = output[0][inputs.input_ids.shape[1]:]
    return tokenizer.decode(new_tokens, skip_special_tokens=True)

reply is lessons 4 and 9 in one function: template, tokenize, greedy generate, keep only the new tokens, decode. Greedy, so every answer in part 3 can be compared fairly.

Example
print(reply([{"role": "user", "content": "In one sentence, what is a parcel tracking number?"}]))

Why this matters with an API

Hosted APIs apply the template on their side, so you send the list and never see these markers. The model underneath still reads one long text. A system message has no special power beyond where the template puts it and what the model learned about that position.

Try it yourself
  • Print the template text with no system message.
  • Add a previous user question and assistant answer to messages and print the text.
  • Print len(tokenizer(text).input_ids): how many tokens did the markers add?

This is what real progress feels like.