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

Context windows: how much a model can read

A model reads a fixed number of tokens at once, its context window. The system prompt, examples, conversation and the answer all have to fit inside it.

Example
print(model.config.max_position_embeddings)

This model's configuration allows 32,768 token positions. Hosted models range from tens of thousands to over a million. The larger the window, the more you can send, and the more you pay for each call that uses it.

A conversation that grows

The conversation starts with structured, the system prompt from lesson 10.

Example
conversation = [{"role": "system", "content": structured}]
for turn in range(40):
    conversation.append({"role": "user", "content": f"Ticket {turn}: my parcel has not arrived yet"})
    conversation.append({"role": "assistant", "content": '{"category": "shipping", "priority": 3}'})
Example
def count_tokens(messages):
    encoded = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_dict=True)
    return len(encoded["input_ids"])

A support chat with 40 earlier turns. count_tokens applies the chat template and counts the tokens. tokenize=True with return_dict=True gives a dictionary whose input_ids are the token ids; without return_dict, calling len on the result would count the dictionary's two keys instead.

Example
print(count_tokens(conversation[:1]), "tokens: the system prompt")
print(count_tokens(conversation), "tokens: the whole conversation")

Every call re-sends the whole conversation, because the model remembers nothing between calls. A chat that runs long grows the cost of every new message, and eventually stops fitting.

Keeping the newest turns

Example
def trim(messages, budget):
    system, rest = messages[:1], messages[1:]
    while rest and count_tokens(system + rest) > budget:
        rest = rest[2:]
    return system + rest
Example
short = trim(conversation, budget=500)
print(len(conversation), "messages before,", len(short), "after")
print(count_tokens(short), "tokens")
print(short[1]["content"])

trim keeps the system message and drops the oldest user and assistant pair, two messages at a time, until the rest fits the budget. The oldest turn left is now a recent one. Leave room in the budget for the answer, since it shares the window.

Dropping old turns loses what was in them. Other approaches summarise old turns, or store facts about a user separately and add only the relevant ones, which is what memory frameworks like Mem0 do.

Try it yourself
  • Trim to a budget of 200 and print what is left.
  • Change rest[2:] to rest[1:] and look at the role of the first message kept. Why does it matter?
  • Count the tokens in the few-shot examples from lesson 11.

This is what real progress feels like.