Curating what the model actually reads
Prompt engineering is the visible iceberg. Underneath sits the bigger discipline: deciding what content occupies the context window on every turn, in what order, fresh or cached, full or summarized. Six concepts, from the model's view to context rot.
The 6 concepts of context engineering
Tap any concept to jump to its 5-card cluster.
- 1Prompt vs. Context EngineeringThe reframe: writing one message vs curating the stack
- 2What the Model SeesInventory the six layers of every API call
- 3The Four LeversAdd, compress, retrieve, offload — the only moves
- 4Static vs. Dynamic ContextOrder matters: prefix caching only hits exact matches
- 5Lost in the MiddlePosition effects: front and back beat middle
- 6Context RotWhy long sessions degrade and how to compact
Two different crafts
In M03 you learned prompt engineering — the craft of writing a single message that guides Claude toward an answer. Roles, few-shot examples, chain-of-thought, output rules. That's the visible iceberg.
Underneath sits context engineering: deciding what content occupies the context window on each turn, in what order, and how it gets compressed or fetched. Prompt engineering is one piece of it — the authoring of the system prompt or current user message. Context engineering is the whole budgeting problem around that one message.
Most learners obsess over the last line they typed. The other 95% of tokens — tool definitions, history, retrieved chunks, tool results — sets the priors, constraints, and (when neglected) the failure modes.
The closing argument vs. the evidence binder
BEFORE: Picture a trial. The lawyer hands the jury an evidence binder — contracts, photos, prior testimony, exhibits. Hours later, the lawyer delivers a closing argument. The jury hears the closing last, but they've been reading the binder all day.
PAIN: Rookie lawyers polish the closing argument and ignore what's in the binder. The jury reaches its verdict based mostly on the binder. A brilliant closing on top of a sloppy binder loses the case — and the lawyer never figures out why.
MAPPING: The closing argument is your prompt. The binder is your context. Claude is the jury — it reads everything. If your retrieved chunks are irrelevant, your tool definitions are vague, or your history is full of resolved errors, no amount of clever prompting fixes the verdict.
Different questions, different levers
- Prompt engineering asks "what words?"Pick the right phrasing, role, examples, output format for one message. It's a writing problem.
- Context engineering asks "what at all?"Decide what gets included in the window, in what order, fresh or cached, full or summarized. It's a budgeting problem.
- Both run on every callYou always do both whether you realize it or not. Every API call has a system prompt (prompt eng) and a tool/history/RAG stack (context eng).
- Failure modes are differentBad prompt = vague or wrong-format output. Bad context = confident, well-formatted output that uses outdated, wrong, or missing information.
Which lever am I pulling?
# Agent gave a bad answer. First diagnostic move: IF output format is wrong: # wrong JSON shape, missing fields, wrong tone FIX the system prompt # prompt engineering ELIF output cites a wrong fact: # content is wrong, but format is fine ASK: "what did the model actually see?" CHECK retrieved chunks, history, tool results FIX the context stack # context engineering ELIF agent loops or contradicts itself: # late in a long session SUSPECT context rot — compact history ELSE: CHECK tool definitions & ordering # static-first, critical info at start/end
Rule of thumb: format problems = prompt. Fact problems = context. The vast majority of "Claude got it wrong" issues in production are context problems wearing prompt clothes.
Misconceptions
Prompt engineering writes one message. Context engineering curates the whole stack.
By M11 you'll be juggling both on every agent you build. M03B makes the budgeting half explicit so you stop debugging "the prompt" when the bug is somewhere else in the window.
Six layers, one prompt
Before you can engineer context, you have to know what's in it. On every Anthropic API call, Claude receives a layered stack that the SDK assembles from your system, tools, and messages arguments — plus anything you injected via RAG or tool results.
Internally that all becomes one sequence of tokens the model reads in order. There are six distinct layers, and on a typical turn-8 agent call only about 1% of those tokens were typed by your user. The other 99% is content you assembled.
Layers of a sandwich
BEFORE: Picture a deli sandwich. Bread, mayo, lettuce, meat, cheese, pickles, top bread. The customer ordering it usually only mentions one ingredient ("turkey, no mayo") — everything else is what the deli assembles.
PAIN: Customers blame the cook for a bad sandwich based purely on the meat. But the bread was stale, the lettuce was wilted, and the cheese was the wrong kind. The "meat" is 15% of the bites.
MAPPING: The "meat" is the user's current message. The bread is the system prompt. The lettuce is tool definitions. The cheese is conversation history. The pickles are retrieved docs. The mayo is past tool results. Claude tastes all of it. Fix what's spoiled, not what's loudest.
The six layers in order
- System promptYour instructions, persona, output rules. Set once per conversation. Static across turns. Typically 200–800 tokens.
- Tool definitionsJSON Schema for every tool you've registered. Claude reads them all even on turns where no tool is called. ~250 tokens per tool.
- Conversation historyEvery prior user/assistant message in order. Grows monotonically every turn. Usually the heaviest layer in a long session.
- Retrieved documentsChunks injected by your RAG pipeline (M09). Different on every turn. Quality and ordering matter heavily.
- Tool resultsJSON outputs of past tool calls, embedded as messages. Accumulates with every tool use, including failed and abandoned ones.
- Current user turnThe message you just appended. Smallest layer, last in position — high attention from the model.
Inventory pseudocode
Before optimizing anything, count what's actually in the window:
# Take stock of every layer before you tune anything FUNCTION inventory(request): layers = { "system" : tokens(request.system_prompt), "tools" : tokens(JSON(request.tools)), "history" : sum(tokens(m) FOR m IN request.history), "retrieved" : sum(tokens(d) FOR d IN request.docs), "tool_res" : sum(tokens(r) FOR r IN request.tool_results), "current" : tokens(request.user_message), } total = sum(layers.values()) FOR EACH (name, n) IN layers: PRINT name, n, percent(n / total) # Heaviest layer = first tuning target RETURN sort_desc_by_value(layers)
If history is 60%, summarize. If retrieved is 60%, re-rank and drop. If tool defs are 60%, you have too many tools registered for this turn.
Misconceptions
The user typed 85 tokens. The model received 9,685 tokens. 99% of what shaped Claude's answer was content you assembled.
From now on, when an agent gives a bad answer, your first question isn't "was the prompt wrong?" It's "what did the model actually see?"
Only four moves exist
When the context stack gets too big, too noisy, or too stale, you have exactly four moves. Every context-engineering decision — every one — comes down to picking one:
Add · Compress · Retrieve · Offload.
The rest of Track 3 (M08 conversation trimming, M09 RAG, M10 advanced RAG, M11 memory) is just specialized applications of these four. Internalize them now and the rest is configuration.
Packing for a two-week trip
BEFORE: Your suitcase is full and you're holding one more item. You have four options. (1) Throw it in. (2) Compress your shirts with packing cubes so they take half the space. (3) Leave it home and ship it ahead for pickup if you need it. (4) Hand it to a travel buddy meeting you there.
PAIN: Travelers default to option 1, the bag bursts, and they pay overweight fees or randomly leave things behind at the airport. No deliberate choice gets made.
MAPPING: Those four moves are add, compress, retrieve, offload — the only four moves for curating context. Every RAG technique, memory architecture, and multi-agent pattern you'll learn later is one of these four wearing a different costume.
One fact, four lives
Same fact — "the 2019 GE Capital filing for Acme Corp lists collateral worth $42.3M" — lived four different lives:
- ADD (38 tok / turn)Pasted the full sentence into context every turn. Exact wording preserved. Burns tokens forever.
- COMPRESS (8 tok / turn)"Acme has a 2019 collateral filing." Gist preserved, exact $42.3M lost. Fine for "what does this filing cover?", fatal for "what's the collateral value?"
- RETRIEVE (0 tok / turn)Stored in a vector DB. Costs 0 tokens until the user asks something matching. Then fetched fresh.
- OFFLOAD (variable)Subagent reads the whole filing in its own context, returns a one-sentence verdict to your main agent.
There is no universally right answer. The lever depends on access frequency, freshness needs, and how lossy you can afford to be.
The four levers side by side
| Lever | Cost | Best when |
|---|---|---|
| Add | Full tokens each turn | Small, every-turn, exact wording matters (policies, user name, current date) |
| Compress | Few tokens, lossy | Gist matters, specifics don't ("we already discussed X and decided Y") |
| Retrieve | 0 idle, lookup cost | One of many candidates; only relevant ones matter (10K docs, fetch 3) |
| Offload | Subagent budget | Fact needs its own reasoning chain that would pollute main context |
# Quick chooser IF small AND every_turn AND exact: ADD ELIF gist_enough: COMPRESS ELIF sparse_access AND lossless: RETRIEVE ELIF needs_own_reasoning: OFFLOAD
Misconceptions
Every context decision is one of four moves: add, compress, retrieve, offload.
Pick by access frequency and lossy-tolerance. The fanciest techniques in Track 3 are just these four levers in custom packaging.
Order changes the bill
Inside your context stack, some layers don't change between turns — the system prompt, the tool definitions, sometimes a fixed reference doc. Other layers change every call — the conversation history grows, retrieved chunks differ per query. This split has a name and a consequence.
Anthropic's prompt caching reuses prefixes that match exactly. If you put dynamic content before static content, none of the static content can be cached — the prefix has diverged. You're billed full price every turn for content that never changed. Ordering matters; same content, wrong order, ~6.5x the cost.
The pre-printed form
BEFORE: Picture a clinic. Every patient fills out the same intake form: name, date, allergies, chief complaint. The clinic pre-prints page 1 (boilerplate consent language) and leaves page 2 blank for the patient.
PAIN: A rookie clinic prints page 2 first and page 1 second — with the patient's signature line on page 1 referring to "the above terms" that aren't above yet. Every form has to be reprinted from scratch because the order is wrong.
MAPPING: Static content (consent language) goes first so it can be pre-printed and reused. Dynamic content (this patient's answers) goes second. Same with caching: static first, dynamic last, and the static prefix is reused for ~10% of the original cost.
The ordering rule
- Static content goes firstSystem prompt, tool definitions, fixed reference docs. Identical across all calls in the session.
- Dynamic content goes lastConversation history, current retrieved chunks, current user message. Different every call.
- Cache reuses exact prefixesAnthropic stores the result of processing the static prefix. Next call with the same prefix? Cached tokens cost ~10% of regular input tokens.
- One byte change kills the prefixIf anything before a layer changes — even one character — that layer and everything after it is "new" and billed full price. Static must come first.
ContextBudget — assemble in the right order
# Assemble a prompt that maximizes cache hits and stays # under a token budget. Used in M08 (history), M11 (memory). CLASS ContextBudget: fields: system, tools, ref_doc, # STATIC history, retrieved, current,# DYNAMIC target_budget FUNCTION account(): RETURN {layer: tokens(content) FOR EACH layer} FUNCTION fits(): RETURN sum(account().values()) <= target_budget FUNCTION build_messages(): # STATIC PREFIX (cacheable, order locked) prefix = [system, tools, ref_doc] # DYNAMIC SUFFIX suffix = [history, retrieved, current] RETURN prefix + suffix FUNCTION apply_strategy(): IF NOT fits(): history = compress(history, keep_recent=6) IF NOT fits(): retrieved = retrieved[:2] # keep top 2 only
The same ContextBudget pattern reappears in M08 (history compression) and M11 (memory layers). Locking the static prefix is what makes prompt caching pay.
Misconceptions
cache_control on everything to be safe."Static first, dynamic last. System → tools → reference doc → history → retrieved → current user.
Same content, wrong order, 6.5x the cost. Across 1,000 daily calls, that's a five-figure annual swing on message ordering alone. Cert note: prompt-caching pricing/TTL details are out of scope, but recognizing prefix-stable ordering is in scope.
Front and back beat middle
Even if every byte of your context is high-quality, where you place it matters. Large language models attend more strongly to content at the start and end of the window than to content in the middle. It's a property of how attention layers work, replicated across nearly every model family.
The phenomenon was named "lost in the middle" by Liu et al. 2023. They placed the same answer at different positions in a long context and measured retrieval accuracy: ~76% at the start, ~47% in the middle, ~71% at the end. Critical instructions buried at line 47 of a system prompt get half the attention they'd get at line 1.
The 23-bullet meeting agenda
BEFORE: Think about a 23-bullet meeting agenda. A week later, ask a participant what was decided. They'll remember bullet 1 (the opening goal) and bullet 23 (the action items). Maybe one heated discussion in the middle.
PAIN: Organizers stuff the most important detail at bullet 12 — "the meeting hit its stride" — and a week later nobody remembers it. The decision is real but its position kills its visibility.
MAPPING: Claude reads context the same way. Position 1 and the very end recall with high fidelity. Information buried in the middle of a long context, even though literally present, is sometimes effectively invisible to the model's reasoning.
Recall by position
- Attention is U-shapedPlot recall accuracy vs. position and you get a U: high at the start, dip in the middle, high again at the end.
- The effect persists at every scaleSame pattern in 8K, 32K, 200K, and 1M-token windows. A bigger window doesn't fix the middle dip — it just makes the middle bigger.
- Last position is the hottestThe current user message is at the END of the context. That's why it shapes intent so strongly — not because of role, but because of position.
- Reproduces across familiesDocumented for Claude, GPT-4, Llama, Gemini. It's a property of transformer attention, not a vendor-specific quirk.
Position-aware assembly
# Position-aware context assembly FUNCTION assemble_prompt(case_facts, retrieved, history, user_q): # SLOT 1 (START) — highest recall start = [ system_prompt, case_facts, # critical immutables go here ] # MIDDLE — lowest recall, use for bulk/optional middle = [ history, retrieved[:-1], # all retrieved EXCEPT the best chunk ] # SLOT 2 (END) — second-highest recall end = [ retrieved[-1], # best chunk closest to question case_facts_repeat, # ~50 tok bookend, big payoff user_q, ] RETURN start + middle + end
Three concrete rules: (1) critical instruction at start of system prompt, not buried at line 47; (2) best retrieved chunk last, closest to question (re-ranking is exactly this); (3) repeat critical case facts at start AND end. The ~50-token tax pays back big in recall.
Misconceptions
Critical instructions at start. Best retrieved chunk at end. Bulk content in the middle.
Cert tip (Domain 5.1): trick questions describe a long-context agent that "reads" a 50-page document and ask why it misses a fact in the middle. The answer is always position effects, not "the model wasn't given the document."
Long sessions accumulate junk
Long-running agents accumulate junk. Tool results from searches that went nowhere. Plans that got abandoned three turns later. User instructions that were superseded ("actually, ignore that last bit"). Errors that were resolved. The model dutifully attends to all of it — including the parts that no longer apply.
By turn 30, your agent retries already-failed searches, contradicts its own corrections, and answers questions you never asked. Token usage may still be well under the limit. The fix isn't a bigger window — it's a smaller, cleaner one. Signal-to-noise matters more than raw size.
The 15-times-forwarded email
BEFORE: Think about an email thread that's been forwarded fifteen times with everyone leaving the full quoted history. The current message sits at the top; below it stretches "Re: Re: Re: Re: FW: FW:" all the way down.
PAIN: A new participant joins, reads top-down, and acts on stale information from forward #3 because that's where the dollar figure happened to live. Every reply makes the thread harder to read, not easier. Decisions get re-litigated. The thread goes net-negative on information.
MAPPING: The same thing happens in agent context windows. Each turn appends. Nothing self-cleans. By turn 25 the model reads a thread that's mostly noise, and a small but critical fact at turn 12 is buried under tool-result garbage from turns 13–24.
Before and after compaction
- Detect rotSignal: agent works fine for 10 turns, then quality drops. Token usage may still be under the limit. Watch for retried failed searches, contradictions, and answers drifting off-topic.
- Identify keep vs dropKeep: case facts, active user instructions, the current question, anything the user explicitly affirmed. Drop: failed tool calls, superseded instructions, duplicate searches, abandoned plans.
- Compact, don't truncateSummarize older turns into a single message with explicit preservation rules: keep every named entity, ID, dollar amount, date, and active user instruction. Drop only resolved noise.
- Result: shorter and cleanerThe post-compaction context isn't just shorter (3 messages vs 11). It's also cleaner — current question now adjacent to relevant facts, no longer buried.
M08 operationalizes this for conversation history specifically. The principle generalizes: compaction isn't just about budget, it's about signal.
Context-rot mitigation
# Run after every N turns OR when budget exceeded FUNCTION compact_history(history, keep_recent=6): IF length(history) <= keep_recent: RETURN history # nothing to do yet older = history[: -keep_recent] recent = history[-keep_recent :] # IMPORTANT: explicit preservation rules instruction = """Summarize this transcript in 3-5 sentences. PRESERVE: every named entity, ID, dollar amount, date, count, and explicit user instruction. DROP: redundant or resolved tool errors, abandoned plans, superseded instructions.""" TRY: summary = call_claude(haiku, instruction + older) EXCEPT api_error: RETURN recent # fall back to truncation RETURN [{role: "user", content: "[Summary] " + summary}] + recent
The preservation prompt is load-bearing. Without it, summarization eats specifics first — the model strips the $42.3M and keeps the prose. M08 covers this in depth; M11 layers it into the memory architecture.
Misconceptions
Context rot degrades agents below the token limit. The fix isn't a bigger window — it's a cleaner one.
Compact older turns with explicit preservation rules. The principle is broader than history: any layer can rot. Audit and prune.
One question per concept
Tap a card to reveal the answer.
system, tools, and messages arguments. That 99% sets the priors, constraints, and failure modes — the user's question only signals intent.Open the full module on desktop
The desktop version walks through a complete ContextBudget implementation in Python and Node.js: per-layer token accounting, the compress lever with named-entity preservation, strategy dispatch, and a hands-on lab that overflows a 4K budget on a noisy UCC research session and brings it back under without losing case facts.
Module 3B of 30 · Track 1: Foundations