av / dives /Context Engineering: Exercises
source about me

Bonus dive

Exercises: make the learning stick

Reading code teaches you less than predicting what it will do and then checking. This file turns each section of the README into a few quick active-recall prompts.

How to use it: work the section first, then come back. Commit to an answer before you run or reveal. The prediction is where the learning happens, even (especially) when you're wrong. Answers are hidden behind ▸ toggles.

Everything runs offline on PROVIDER=mock: no key, no cost. The mock answers recall questions only from what's actually in the window, so "did it remember?" is a real, deterministic test.


Section 2: The window is a budget (offline)

Recall. Why is "memory" a budgeting problem and not a model feature? What two costs grow with every turn you keep in the history?

▸ Answer

The API is stateless. "Memory" is just the message list you resend, and that list has a hard token ceiling (the context window). Every kept turn costs tokens (you re-send and re-pay for it each request) and pushes you toward overflow (the request fails, or quality drops). So you must choose what to keep.


Section 3: The sliding window (mock)

Predict, then run. In examples/02_sliding_window.py, Dana says her name in turn 1, then the chat runs long on a small budget. When you finally ask "What's my name?", what does the model answer, and why?

▸ Answer

It can't recall it. Turn 1 scrolled off the back of the window, so it isn't in the messages sent. For the model the conversation is the window; delete the introduction and the name is genuinely gone. Bounded and cheap, but forgetful.


Section 4: Compaction (mock)

Predict. examples/03_compaction.py runs the SAME long chat on the SAME budget as §3, but with SummaryMemory. Does the model recall the name now? What exactly got kept, and what got thrown away?

▸ Answer

Yes: it recalls the name and the thing it was asked to remember. Compaction replaced the oldest turns with a short summary (which preserves the facts) and kept the recent turns verbatim. What's thrown away is the exact wording of old turns, and you pay one summarization call. The risk: a bad summary drops a detail.

Do. Lower the budget (SummaryMemory(budget_tokens=120, ...)) and rerun. Does the compaction count go up? Is the name still recalled?

▸ Answer

More compactions fire (the window fills sooner), but the name survives, because each compaction folds the prior summary into the next, and the canonical fact lines carry through. That's the point: bounded memory that doesn't forget the essentials.


Section 5: Long-term memory (mock)

Recall. Compaction kept a fact alive within one conversation. Why isn't that enough, and what does long-term memory do differently?

▸ Answer

A summary lives in that conversation's window. Close the session and it's gone. Long-term memory writes durable facts to a store outside any window and retrieves the relevant few back in when a new turn needs them. It's RAG pointed at the conversation: persist, then retrieve by relevance.

Predict, then run. In examples/04_long_term_memory.py, session two starts with an empty window. Why does it answer "When is my launch?" correctly with memory and fail without it?

▸ Answer

The window holds nothing from session one, so without retrieval the fact simply isn't present and the model says it doesn't have it. With long-term memory, the relevant fact ("launch is Friday") is recalled from the store and injected into the system prompt, back in the window where the model can use it.


Section 6: Order matters (offline)

Recall. What is the "lost in the middle" effect, and what does order_for_recall do about it?

▸ Answer

Models attend most reliably to the start and end of a long context and are worse at using material buried in the middle. order_for_recall places the highest-priority sections at the edges and the filler in the middle: same tokens, better odds the key chunk is actually used.


Section 7: Assembling under a budget (offline)

Predict. In examples/06_assemble_budget.py, the candidate sections total more than the budget. Which sections survive, and which is first to be cut?

▸ Answer

The highest-priority sections that fit survive: the system prompt and the user's question are non-negotiable; long-term memory and the on-topic retrieved doc make it; the marketing blog (lowest priority) is cut first. Prioritize, then pack: a giant low-priority doc must never crowd out a small essential one.


Section 8: Context rot (mock)

Predict, then run. examples/07_context_rot.py answers the same question with a lean context and a bloated one, where the bloat includes a distractor naming a different person. What are the TWO costs the bloated window pays?

▸ Answer

Both halves of context rot. Tokens: the bloated window costs ~10× the tokens (so ~10× the spend and more latency), on every turn. Quality: it returns the WRONG name: the buried distractor won. On the mock that flip is deterministic (it takes the last "my name is ..." it sees); on a real model the same rot is subtler, with padding dilutes the signal and pulls attention to a plausible irrelevant passage. Relevance beats volume.


Section 9: Pruning observations (offline)

Recall. Why do agents bloat their context faster than chats, and what does observation pruning keep vs. drop?

▸ Answer

Each agent step appends a tool call and its full (often huge) result, and most of those results are spent after one step. Pruning keeps the agent's reasoning and the most recent observations verbatim, and stubs/drops the old ones: a big token cut with the train of thought intact.


Section 10: Caching vs. compaction (offline)

Predict (09). Compaction keeps the window far smaller every turn than an append-only history. So it must cost less to run, right?

▸ Answer

Not necessarily. Here it costs about 1.5× more. Providers cache the prompt prefix, and any change to the prefix invalidates everything after it. Append-only never changes its prefix, so almost every token is a cheap cache read (~0.1×). Compaction rewrites the system prompt (the summary) and drops old turns, a prefix change, so those turns are cache misses billed at full write price (~1.25×) on the whole context. Fewer tokens, bigger bill.

Recall. If compaction can raise the bill, why compact at all, and how do you get its benefit without the cache penalty?

▸ Answer

You still need it eventually: an append-only window grows without bound, so on a very long conversation its cheap-but-huge reads finally cost more than a small uncached context. The two axes ("fewer tokens" vs "cheaper bill") cross over. The fix is cadence: compact rarely and in bulk so you pay the cache miss once instead of every turn, keep the stable part of the prefix byte-identical, and watch cache_read_input_tokens vs cache_creation_input_tokens to see which regime you're in.


Capstone: chat.py

Do. Run python hands_on/chat.py "Hi, my name is Dana. Remember our launch is Friday.", then run python hands_on/chat.py "When is my launch?" as a separate command. How does the second run know, when it shares no memory in-process with the first?

▸ Answer

The first run persisted the durable facts to .ctx_memory.json. The second run is a fresh process with an empty window, but it loads that file and recalls the launch fact into its system prompt. In-session memory (compaction) and cross-session memory (the store) are different layers; the capstone uses both.

Predict. Run the REPL with --show-context --budget 200 and chat past the budget. What happens to the "compactions" counter and the token line as you go, and why does the window never overflow?

▸ Answer

Compactions climb each time the budget is exceeded, and the token line stays at or under 200, because compaction folds old turns into the summary the moment they'd push you over. Bounded memory: the window can't grow without bound no matter how long you talk.

Stretch. Switch to PROVIDER=openai or claude (add a key) and repeat. Where does the real model do better than the mock, and where might it do worse?

▸ Answer

Better: it summarizes prose far more intelligently and recalls paraphrased facts the mock's keyword match would miss. Worse: it's nondeterministic, and a summary can quietly drop a detail, and recall can miss. That's exactly why memory quality is something you measure (the Evals dive), not assume.


Where to take it next

Invent your own. Take a long conversation you care about, pick a budget, and decide your policy: what to summarize, what to persist forever, what to drop. The first time your assistant recalls something from twenty turns (or twenty days) ago, under a fixed budget, context engineering has clicked.