- Explain context engineering and how it extends prompt engineering to everything in the context window
- Allocate a token budget across system prompt, tools, retrieval, memory, history and output for a given model
- Implement conversation-history management with truncation and summarisation
- Order context for quality (long material first, question last) and for prompt caching (static first, dynamic last)
- Describe context rot and lost-in-the-middle effects and design against them
- Compare just-in-time retrieval, compaction, external notes and sub-agents for long-running agents
Before this: Context Windows and Memory, Prompting Fundamentals, Advanced prompting techniques
A chat with a model is one prompt. An application or agent is a machine that assembles a prompt on every call from many parts: standing instructions, tool definitions, retrieved documents, stored memories, conversation history and the newest message. Context engineering is the discipline of deciding what goes into that window, in what form, in what order and at what size. Anthropic's engineering team described it in 2025 as the natural evolution of prompt engineering: curating the smallest set of high-signal tokens that makes the desired behaviour most likely. In production systems it usually matters more than the wording of any single instruction.
Intuition: packing a briefcase for a long trip
The model is a traveller who can carry exactly one briefcase per call, of fixed size, and who forgets everything not in it. Pack too little and they lack what they need. Pack too much and they cannot find the important paper among the clutter, and the briefcase costs more to carry. Good packing means: essentials always in the same pocket (so security can wave them through quickly, which is what caching does), today's papers on top, old receipts summarised onto one page, and a note telling them where the filing cabinet is so they can fetch the rest when needed.
What goes into the context window
| Component | Role | Changes how often | Typical size |
|---|---|---|---|
| System prompt | Role, rules, output format, policies | Rarely (per release) | 500 to 5,000 tokens |
| Tool definitions | Names, descriptions and JSON schemas of callable tools | Rarely | 100 to 500 tokens per tool |
| Few-shot examples | Canonical demonstrations | Rarely | 100 to 500 tokens each |
| Long-term memory | Facts about the user or project carried across sessions | Per user or session | 200 to 3,000 tokens |
| Retrieved documents | Chunks from search or RAG relevant to this turn | Every turn | 500 to 20,000+ tokens |
| Conversation history | Earlier user and assistant turns, tool calls and results | Grows every turn | Unbounded unless managed |
| Current message | The user's newest request | Every turn | Varies |
| Output reservation | Room for the answer, and for reasoning tokens on reasoning models | Every turn | 1,000 to 64,000 tokens |
The context window is a hard limit on input plus output (for most APIs, reasoning tokens count against it too). But the practical limit is lower, for three reasons: cost (you pay for every input token on every call), latency (prefill time grows with input length), and quality.
Context rot and position effects
Long-context models can accept hundreds of thousands of tokens, but that does not mean they use all of them equally well.
- Lost in the middle. Liu et al. (2023) showed that on multi-document question answering, models used relevant information best when it sat at the beginning or end of the context and worst when it was in the middle.
- Context rot. A 2025 Chroma technical report tested 18 models and found that performance degraded as input length grew, even on simple retrieval and text-replication tasks; distracting passages that resembled the answer made it worse; and on a conversational memory benchmark, giving models a focused prompt with only the relevant part beat giving them the full history of roughly 113,000 tokens.
Anthropic's framing is an attention budget: every token added competes for the model's attention, so each one should earn its place. Practical consequences: retrieve fewer, better chunks; remove stale tool output; summarise old turns; and test your application at the context lengths it will actually reach, not just on short demos.
Ordering: quality and caching
Two ordering principles apply, and fortunately they mostly agree.
For quality: put long reference material near the top and the specific question and instructions for this turn at the end. Anthropic's documentation reports that placing the query after long documents (20,000+ tokens) can improve response quality by up to 30% in its tests. Asking the model to quote the relevant passages before answering also helps it find what matters.
For caching: prompt caching stores the model's processed state for a prompt prefix so later calls sharing that exact prefix skip recomputation and pay a reduced rate. As of November 2026, OpenAI documents that caching requires the entire prefix to match exactly, that the minimum cacheable prefix is 1,024 tokens on its recent models, and that cached reads are billed at a steep discount (0.1 times the normal input rate on most of its newest models, and less on some). Other providers, including Anthropic and Google, offer caching with explicit cache markers or automatic detection, each with its own prices and lifetimes; check current docs.
The layout that serves both goals:
[1] System prompt (static: identical for every user)
[2] Tool definitions (static)
[3] Few-shot examples (static)
--- cache boundary A: shared by everyone ---
[4] User memory / project notes (static within a session)
--- cache boundary B ---
[5] Conversation history (append-only; grows at the end)
[6] Retrieved documents for this turn
[7] Current user message + turn-specific instructions
The rule: static first, dynamic last, never edit the middle. A timestamp in the system prompt, tools reordered between calls, or a user's name injected into line 1 breaks the cache for everything after it. Placing retrieved documents in [6] keeps them near the question, which helps quality. (If you need very large shared documents, put them in the static block instead.)
Worked example: a token budget for a support agent
Suppose a model with a 200,000-token context window, used as a customer-support agent.
Step 1: reserve output. The agent uses a reasoning model and sometimes writes long answers. Reserve 32,000 tokens. Input budget: .
Step 2: fixed components. System prompt 4,000 tokens. Tools: 25 tools at about 350 tokens each . User memory 2,000. Fixed total .
Step 3: per-turn components. Retrieval returns 8 chunks of about 1,000 tokens . Current message up to 2,000. Per-turn total .
Step 4: history. The hard ceiling for history is tokens. But because of context rot, cost and latency, the team sets a soft target of 60,000 total input tokens per call, leaving tokens for history. Policy: keep the most recent turns verbatim; when history exceeds 35,250 tokens, replace the oldest turns with a summary of at most 2,000 tokens.
Step 5: cost with and without caching. Take a hypothetical price of 3 US dollars per million input tokens, cache reads at 0.1 times that rate, and cache writes at 1.25 times (a premium some providers charge; others charge none). Over a 40-turn session, the static prefix (14,750 tokens) is sent 40 times.
- Without caching: tokens, .
- With caching: first call writes at token-equivalents; 39 reads at each, . Total token-equivalents .
That is about an 87% saving on the static part alone, before counting the growing history, which can also be cached incrementally because it is append-only. Real numbers depend on the provider's prices and on the cache expiring between turns.
Step 6: tool bloat check. The 8,750 tokens of tools are 59% of the fixed budget. If analytics show that 10 of the 25 tools are almost never used, moving them behind a single "search more tools" tool, or into a sub-agent, saves around 3,500 tokens per call and reduces confusion between similar tools.
Managing conversation history
Three standard strategies, often combined:
- Truncation (sliding window): keep the last turns or last tokens. Simple and predictable, but every time the window slides the start of the history changes, which breaks the cache for it, and early facts (the user's name, the original goal) are lost.
- Summarisation (compaction): replace old turns with a model-written summary. Preserves key facts at a fraction of the size, at the cost of an extra call and some information loss. Anthropic describes clearing old tool results as the lightest form of compaction: the raw 30,000-token log the agent already read rarely needs to be shown again.
- Pinning: always keep certain items, such as the user's original request, decisions made and open to-dos, regardless of age.
def est_tokens(text: str) -> int:
return max(1, len(text) // 4) # rough heuristic: ~4 characters per token in English
def summarize(turns): # stand-in; in production, call a model
return "Summary of earlier conversation: " + " | ".join(t["content"][:40] for t in turns)
def build_history(turns, budget, keep_recent=6, pinned=None):
# return a history list that fits in `budget` tokens
pinned = pinned or []
recent = turns[-keep_recent:]
older = turns[:-keep_recent]
used = sum(est_tokens(t["content"]) for t in pinned + recent)
out = list(pinned)
if older:
summary = {"role": "user", "content": summarize(older)}
if used + est_tokens(summary["content"]) <= budget:
out.append(summary)
used += est_tokens(summary["content"])
while recent and used > budget: # still too big: drop oldest recent turns
used -= est_tokens(recent.pop(0)["content"])
return out + recent
turns = [{"role": "user" if i % 2 == 0 else "assistant",
"content": f"turn {i} " + "x" * 400} for i in range(20)]
hist = build_history(turns, budget=1000)
print(len(hist), sum(est_tokens(t["content"]) for t in hist))
The heuristic of about four characters per token is only an English-text approximation; use the provider's tokenizer or token-counting endpoint for real budgets, especially for code and non-English text. To stay cache-friendly, recompute the summary only occasionally (for example, when history crosses the budget) rather than on every turn, so the prefix stays stable between compactions.
Context for agents: just-in-time and beyond
Long-running agents can generate far more context than any window holds: file contents, search results, command output. Patterns that work:
- Just-in-time retrieval: keep lightweight references (file paths, URLs, record IDs, queries) in context and load full content with tools only when needed, instead of pre-loading everything. Many coding agents use a hybrid: a few key files loaded up front, the rest explored on demand.
- Compaction: when nearing the limit, summarise the trajectory (decisions, open problems, key findings) and continue in a fresh window.
- Structured note-taking: the agent writes progress notes or a to-do file outside the context window and reads it back later, so state survives compaction.
- Sub-agents: a coordinator delegates focused sub-tasks to sub-agents with clean windows, which return condensed results (Anthropic mentions summaries of roughly 1,000 to 2,000 tokens) instead of their full exploration.
- Tool design: tools should return concise, relevant output (paginated, filtered, truncated with a note) rather than dumping 50,000 tokens of raw data into context.
Tools are context too
Every tool definition is sent on every call, and the model must choose among them on every step, so tool design is context design. Good tools have distinct purposes, unambiguous names (search_orders_by_customer_id rather than lookup), descriptions that say when to use and when not to use them, minimal required parameters and concise outputs. If a human engineer could not say for certain which of two tools fits a situation, the model will not either. When an agent needs dozens of tools, consider exposing a small core set plus a tool-search tool that loads others on demand, or splitting responsibilities across sub-agents that each see only their own tools. Measure tool-selection accuracy on an eval set just as you would measure answer quality, because a wrong tool call wastes a full round trip and pollutes the history with irrelevant output.
Memory
Memory is information persisted outside the model and selectively re-inserted: user preferences, project facts, prior decisions. Inserting all of it every time wastes budget and can mislead; better systems retrieve the relevant memories per turn, keep them short and dated, and treat them as data, not instructions. The AI 320 lesson ai-memory-systems covers designs in depth.
Common pitfalls
- Dumping everything in because the window is big. More context often lowers accuracy and always raises cost and latency.
- Breaking the cache by accident: timestamps, random IDs, unstable JSON key order or reordered tools near the top of the prompt.
- Unbounded tool output: a single log dump can push the system prompt's instructions out of effective attention.
- Lossy summaries of critical facts: summarisation silently drops the account number or the constraint the user stated in turn 2. Pin critical facts.
- Forgetting reasoning and output tokens in the budget, then getting truncated answers.
- Testing only short conversations. Bugs from context rot appear at turn 40, not turn 3.
- Overlapping tools with similar names and vague descriptions, which make the agent choose wrongly.
In practice
Treat the context as a designed artifact with an explicit budget per component, logged on every call. Engineers instrument token counts per section, cache-hit rates and quality metrics at different conversation lengths, and alert when a component grows unexpectedly. Start with a stable, cache-friendly layout (static first, dynamic last), retrieve narrowly, summarise or clear old material, and for agents prefer references plus just-in-time loading over pre-loading. Re-measure whenever you add a tool, change retrieval settings or switch models, because each model has different long-context behaviour and each provider prices caching differently.
Key takeaways
- Context engineering designs everything in the window (instructions, tools, retrieval, memory, history and output space), not just the user's prompt.
- Long contexts degrade quality (context rot, lost in the middle), so the goal is the smallest set of high-signal tokens, not the largest the window allows.
- Lay out context static first and dynamic last, and never edit the middle, to get both cache hits and good quality.
- Budget tokens explicitly per component, including reasoning and output, and manage history with truncation, summarisation and pinned facts.
- Long-running agents scale through just-in-time retrieval, compaction, external notes and sub-agents rather than ever-larger windows.
Terms in this lesson
- Context engineering
- Designing and managing the full set of tokens supplied to a model on each call, including instructions, tools, retrieved data, memory and history.
- Context rot
- The decline in a model's ability to use information accurately as the amount of input context grows.
- Lost in the middle
- The finding that models use information at the start or end of a long context better than information in the middle.
- Prompt caching
- Reusing a model's processed state for an identical prompt prefix across calls to cut cost and latency.
- Compaction
- Replacing older context with a condensed summary so a conversation or agent run can continue within the window.
- Just-in-time retrieval
- Keeping references in context and loading full content through tools only when it is needed.
- Token budget
- An explicit allocation of the context window's tokens among the components of a prompt and the expected output.
- Sub-agent
- A separate model instance given a focused task in its own clean context window that returns a condensed result to a coordinating agent.
Check your understanding
- 1.
Which change is most likely to break prompt caching for every request?
- 2.
A model has a 128,000-token window. You reserve 16,000 for output; system prompt and tools use 12,000; retrieval uses 6,000; the user message is 1,000. What is the maximum history size?
- 3.
According to Liu et al. (2023), where in a long context is relevant information typically used worst?
- 4.
An agent's coding session has read dozens of large files and is near its context limit. Which approach best follows current practice?
- 5.
A static prefix of 20,000 tokens is sent on 10 calls. Normal price is 2 US dollars per million input tokens; cache writes cost 1.25x and reads 0.1x. Ignoring expiry, what does the prefix cost with caching, in US dollars?
Exercises
Work each problem before opening the solution.
- Exercise 1Core
Reorder these components for a cache-friendly, quality-friendly prompt, and justify each placement: (a) today's date; (b) 12 tool definitions; (c) the user's question; (d) system prompt; (e) 5 retrieved policy chunks; (f) conversation history; (g) user profile memory.
Show solution
Order: (d) system prompt, (b) tools, (g) user memory, (f) history, (e) retrieved chunks, (a) today's date, (c) the question.
- (d) and (b) are identical for all users: first, so they form a shared cached prefix (keep tool order fixed).
- (g) is stable within a user's session: after the shared prefix, so it can be cached per session.
- (f) history is append-only: it grows at the end of the stable part, so earlier turns stay cacheable.
- (e) retrieved chunks change every turn: after the history and close to the question, which helps the model connect them.
- (a) the date changes daily: put it near the end (or in the user turn), never in the system prompt where it would break the cache.
- (c) the question goes last, after long material, which docs report improves quality.
- Exercise 2Core
Your agent's system prompt is 3,000 tokens, tools are 30 x 400 tokens, and each turn retrieves 10 chunks of 1,200 tokens. The window is 200,000 tokens with 20,000 reserved for output, and the team's soft cap is 50,000 input tokens per call. How many tokens remain for history under the soft cap? Propose two changes that together free at least 15,000 more tokens for history.
Show solution
Fixed: 3,000 + 30 x 400 = 3,000 + 12,000 = 15,000. Retrieval: 10 x 1,200 = 12,000. Total before history: 27,000 (ignoring the user message). History allowance under the soft cap: 50,000 - 27,000 = 23,000 tokens.
Two changes that free at least 15,000 tokens:
- Retrieve fewer, better chunks: add a reranker and keep the top 3 instead of 10: 3 x 1,200 = 3,600 tokens, freeing 12,000 - 3,600 = 8,400.
- Prune tools: keep the 12 most-used tools (12 x 400 = 4,800) and move the rest behind a tool-search tool or a sub-agent, freeing 12,000 - 4,800 = 7,200.
Together: 8,400 + 7,200 = 15,600 tokens freed, so history can grow to 23,000 + 15,600 = 38,600 tokens under the same soft cap. Tighter chunking and a shorter system prompt are further levers; the point is that tools and retrieval are usually the biggest ones.
- Exercise 3Challenge
Extend
build_historyfrom the lesson so that (1) messages marked{"pin": True}are never summarised or dropped, (2) the summary is regenerated only when the number of un-summarised older turns exceeds 10 (otherwise the previous summary is reused, for cache stability), and (3) it returns the total estimated tokens. Test it on 30 synthetic turns where turn 1 is pinned.Show solution
def est_tokens(text): return max(1, len(text) // 4) def summarize(turns): return "Summary: " + " | ".join(t["content"][:30] for t in turns) class History: def __init__(self, budget, keep_recent=6, resummarize_after=10): self.budget, self.keep_recent = budget, keep_recent self.resummarize_after = resummarize_after self.summary, self.summarized_upto = None, 0 # index into turns def build(self, turns): pinned = [t for t in turns if t.get("pin")] normal = [t for t in turns if not t.get("pin")] recent = normal[-self.keep_recent:] older = normal[:-self.keep_recent] if len(older) - self.summarized_upto > self.resummarize_after or ( older and self.summary is None): self.summary = {"role": "user", "content": summarize(older)} self.summarized_upto = len(older) parts = pinned + ([self.summary] if self.summary else []) used = sum(est_tokens(t["content"]) for t in parts + recent) while recent and used > self.budget: used -= est_tokens(recent.pop(0)["content"]) return parts + recent, used turns = [{"role": "user" if i % 2 == 0 else "assistant", "content": f"turn {i} " + "y" * 300, "pin": i == 1} for i in range(30)] h = History(budget=1200) msgs, total = h.build(turns) print(len(msgs), total, msgs[0]["content"][:10]) # pinned turn 1 comes firstPinned messages are separated out and always included. The summary is created on first need and refreshed only when more than 10 new older turns have accumulated, so the prefix stays identical between refreshes (better cache hits). Recent turns are dropped oldest-first only if still over budget; pinned content and the summary are never dropped (in production you would also alert if pinned content alone exceeds the budget).
- Exercise 4Challenge
Design a context strategy for a research agent that must read about 300 web pages (average 6,000 tokens each) to write a 3,000-word report, using a model with a 200,000-token window. Estimate why naive accumulation fails and describe your architecture with token estimates.
Show solution
Naive accumulation: 300 x 6,000 = 1,800,000 tokens, nine times the window, and even the first 30 pages (180,000 tokens) would fill it, with severe context rot long before.
Architecture:
- Coordinator agent keeps: system prompt (3,000), the research plan and outline (2,000), a running findings file (grows to about 15,000), and references (URLs, about 20 tokens each, 6,000 for 300). Stays under about 30,000 tokens.
- Reader sub-agents, each given a batch of about 10 pages (about 60,000 tokens plus a 2,000-token instruction) in a fresh window, extract only claims relevant to the outline, with quotes and URLs, and return a summary of about 1,500 tokens. 30 batches x 1,500 = 45,000 tokens of findings in total.
- Compaction of findings: the coordinator merges sub-agent summaries into the findings file section by section, deduplicating, keeping it at about 15,000 tokens; raw summaries are stored externally and re-read just-in-time when writing a section.
- Writing: for each report section, load the outline, that section's findings (about 3,000) and the relevant quotes on demand; write the section (about 1,000 tokens output). Total context per writing call stays around 10,000 to 20,000 tokens.
- Verification: a final pass checks every citation's quote against the stored page text.
No call exceeds about 65,000 tokens, quality-critical calls stay small, and progress survives in external notes rather than in one ever-growing window.
Further reading
- articleAnthropic Applied AI team, Effective context engineering for AI agents (2025)
- articleKelly Hong, Anton Troynikov & Jeff Huber, Context Rot: How Increasing Input Tokens Impacts LLM Performance (Chroma, 2025)
- paperNelson F. Liu et al., Lost in the Middle: How Language Models Use Long Contexts (2023)
- docsOpenAI, Prompt caching (API documentation)
- docsAnthropic, Prompting best practices: long context tips (Claude documentation)
Go further
Sources
Fact-checked and last reviewed 2026-11-05