The shared budget — sharing the context window.
The window holds far more than your question — and it all has to fit at once.
Last page, the window held video frames, and the oldest fell out once it filled. A real conversation is no different — except the window has to hold far more than your latest question.
On every turn, four things share that one window: the standing instructions you gave the model (its “system prompt”), the whole chat so far, your new message, and the room it needs to write a reply. All four must fit together.
And the window never grows. A long chat simply fills it. Once it's full, the oldest parts of the conversation are dropped to make room — exactly like those frames sliding off the screen.
Add a few turns and watch the window fill. Keep going, and you'll see the earliest turn get pushed out.
It's all one list — and it all costs tokens.
Every turn, you send the model the same kind of list: the system prompt, the history, and your new message. The whole thing must fit the window, with room kept for the reply.
messages = [
{"role": "system", "content": briefing}, # set once, sent every turn
*history, # every past turn — this keeps growing
{"role": "user", "content": new_message}, # your latest message
]
# the whole list must fit the window; trim the oldest history if it won't
reply = umpire.chat(messages, max_output_tokens=400) # reply space, reserved up front
One window, shared by everything.
None of it gets special treatment. As the chat grows, it all eats into the same fixed window — and once that's full, the earliest turns fall out so the newest can fit.
So a big part of building with an LLM is choosing what to keep in the window — and what to drop, shorten, or look up only when you need it.