Context engineering for agents
Module 05 showed how to write a good brief. An agent working for an hour builds most of its own context: it searches, reads files, runs commands and piles up tool output. Whether it succeeds depends less on the prompt than on what ends up in its context window, and what stays out. This module covers the techniques that decide that: just-in-time retrieval, code search and import graphs, compaction, notes files and subagents, when to use each, and a demo that finds a bug's real cause with a sixth of the tokens.
- Sam If I write a great prompt, isn't the agent set?
- Priya Only for the first minute. An agent working for an hour builds most of its own context: it searches, reads files, runs commands. Whether it succeeds depends on what ends up in its context window, and what stays out.
- Priya Think of a detective's desk. Dumping the whole archive on it buries the one clue that matters. A good detective keeps a short list of leads and pulls a file only when a lead points to it, keeps a notebook, sends a junior to read a thousand pages of phone records and report back one paragraph, and clears the desk before the next case.
- Sam What are the techniques?
- Priya A short instructions file loaded up front. Just-in-time retrieval: keep paths and queries, and read files when a step needs them. Structure-aware search: follow imports and calls from a starting point like a failing test. Compaction: summarise near the limit and continue fresh. Notes files the agent keeps outside the window. Subagents that explore in their own window and return a short summary, around one to two thousand tokens. And clear tools that return compact results.
- Sam Show me one task.
- Priya A refund of nineteen ninety-nine comes back as nineteen ninety-eight. The agent starts with the instructions file, writes a failing test, follows the test's imports to the rounding helper, sends a subagent through forty thousand tokens of logs and gets back one line, writes its progress to a notes file, fixes the bug, runs the tests, and clears the session before the next task.
- Sam And the prompt itself?
- Priya Anthropic says aim for the right altitude: not brittle if-else rules, not vague guidance. Start minimal and add instructions only for failures you've seen.
- Priya The lab gives you ten things you could put in front of the agent, from the failing test to forty thousand tokens of raw logs. Tick what goes in, and see the signal, the noise, and anything critical you left out.
- Sam When does each one pay off?
- Priya Instructions files for what the agent can't learn from the code. Just-in-time search for big codebases; Claude Code combines both. Subagents for wide exploration, as in Anthropic's research system. Compaction and notes for one long coupled task, which is Cognition's advice. And notes files for work that spans sessions.
- Sam And the demo?
- Priya Ten files and a rounding bug. Loading everything costs fifteen thousand seven hundred tokens. Keyword search for refund still costs eleven thousand nine hundred and misses the culprit, because the rounding file never says refund. Starting from the failing test and following imports finds it in four files and two and a half thousand tokens.
- Priya Your turn. Why did keyword search miss the rounding file?
- Priya Because the word refund never appears in it. Following imports reaches it.
- Priya Every coding agent has each piece: instruction files like CLAUDE.md and AGENTS.md, built-in search, compaction commands, memory, and subagents.
- Priya Leads, not the archive.
- Priya Loading the whole repository. Keyword search only. A bloated instructions file. Never clearing between tasks. Subagents that paste back everything. And tools that dump pages of output.
- Priya Measure a real bug fix, repeat it anchored on a failing test, send a subagent through a big log, keep a progress file across compactions, and trim your instructions file.
1. The idea
Anthropic describes context as a finite resource with diminishing returns: every token added competes for the model's attention, and accuracy falls as the window fills (Module 02's context rot). Context engineering is the job of keeping the smallest set of high-signal tokens in front of the model at each step.
| Technique | What it does | Fixes |
|---|---|---|
| Instructions up front | A short project file (CLAUDE.md, AGENTS.md) loaded at the start: commands, conventions, what to avoid | Rediscovering the same facts every session |
| Just-in-time retrieval | Keep references (paths, queries, URLs); load content only when a step needs it, with search tools like grep and glob | A window stuffed with files that turn out irrelevant |
| Structure-aware search | Follow imports, call graphs and symbol definitions (language servers, AST outlines) from a starting point such as a failing test | Keyword search missing the file that matters |
| Compaction | Summarise the conversation near the limit and continue in a fresh window; the lightest form is clearing old tool results | Long sessions running out of room or going stale |
| Notes files | The agent writes progress, decisions and to-dos to a file outside the window, and rereads it | Losing track across compactions and sessions |
| Subagents for isolation | A helper explores in its own window (tens of thousands of tokens) and returns a 1,000 to 2,000-token summary | Search noise and huge logs flooding the main context |
| Clear tools | Few, unambiguous tools that return compact results | The agent choosing the wrong tool, or tools returning pages of noise |
2. How it works
One bug fix, with the context assembled step by step.
The right altitude for instructions
Anthropic describes two failure modes for system prompts: brittle, hard-coded if-else rules on one side, vague guidance that assumes shared context on the other. Aim between them: specific enough to steer, general enough to let the model reason. Start minimal, and add an instruction only when you see a failure it would have prevented.
3. Try it yourself
Build the context for one task
The task: "A refund of $19.99 comes back as $19.98. Fix it." Tick what goes into the agent's context. The bar shows tokens, split into what helps and what's noise.
4. Why it matters at work
When to use which, from real systems
| Situation | Technique | Evidence |
|---|---|---|
| Facts the agent can't learn from the code: deploy steps, secrets to avoid, test commands | A short instructions file loaded up front | Claude Code's guide: tell the agent what it can't see; keep the file short |
| A large codebase, a specific task | Just-in-time retrieval with search, plus following imports from a failing test | Claude Code combines up-front instruction files with on-demand glob and grep (Anthropic, September 2025) |
| Wide exploration: many files, logs, web pages | Subagents that return short summaries | Anthropic's research system: parallel subagents, each condensing its findings for the lead (C11) |
| One long, coupled task | A single agent with compaction and notes, rather than splitting it | Cognition: share full context; for long tasks, compress history into key details and decisions (C12) |
| Work that spans sessions or days | A notes or progress file the agent maintains | Anthropic's example of an agent tracking progress across thousands of steps through notes |
Choosing files for a bug fix, in code
A ten-file repository and a rounding bug in billing/money.py. Loading everything costs 15,700 tokens. Keyword search for "refund" still costs 11,900 tokens, mostly a changelog and a UI file, and misses the culprit, because the word "refund" never appears in it. Starting at the failing test and following imports finds it in four files and 2,500 tokens. The program was run in all three languages; each prints the output below it.
# A small repository: file -> (tokens, files it imports, words it contains).
REPO = {
"api/routes.py": (1200, ["billing/refund.py", "auth/session.py"], {"refund", "login"}),
"billing/refund.py": (900, ["billing/money.py", "billing/policy.py"], {"refund"}),
"billing/money.py": (400, [], {"round", "cents"}),
"billing/policy.py": (500, [], {"refund", "window"}),
"billing/invoice.py": (1100, ["billing/money.py"], {"invoice"}),
"auth/session.py": (800, [], {"login"}),
"auth/oauth.py": (1500, [], {"login"}),
"ui/checkout.tsx": (2600, [], {"checkout", "refund"}),
"docs/CHANGELOG.md": (6000, [], {"refund", "invoice", "login"}),
"tests/test_refund.py": (700, ["billing/refund.py"], {"refund"}),
}
CULPRIT = "billing/money.py" # bug report: "a refund of $19.99 comes back as $19.98" (a rounding bug)
def everything():
return list(REPO)
def keyword(word):
return [f for f, (_, _, words) in REPO.items() if word in words]
def follow_imports(start):
"""Start from the failing test and follow imports, the way an agent uses search and an import graph."""
seen, todo = [], [start]
while todo:
f = todo.pop(0)
if f not in seen:
seen.append(f)
todo.extend(REPO[f][1])
return seen
for name, files in [("load everything", everything()), ('keyword search "refund"', keyword("refund")),
("failing test + imports", follow_imports("tests/test_refund.py"))]:
tokens = sum(REPO[f][0] for f in files)
found = "includes" if CULPRIT in files else "misses"
print(f"{name:24} {len(files):2} files {tokens:6,} tokens, {found} the culprit")
// A small repository: file -> [tokens, files it imports, words it contains].
const REPO = new Map<string, [number, string[], string[]]>([
["api/routes.py", [1200, ["billing/refund.py", "auth/session.py"], ["refund", "login"]]],
["billing/refund.py", [900, ["billing/money.py", "billing/policy.py"], ["refund"]]],
["billing/money.py", [400, [], ["round", "cents"]]],
["billing/policy.py", [500, [], ["refund", "window"]]],
["billing/invoice.py", [1100, ["billing/money.py"], ["invoice"]]],
["auth/session.py", [800, [], ["login"]]],
["auth/oauth.py", [1500, [], ["login"]]],
["ui/checkout.tsx", [2600, [], ["checkout", "refund"]]],
["docs/CHANGELOG.md", [6000, [], ["refund", "invoice", "login"]]],
["tests/test_refund.py", [700, ["billing/refund.py"], ["refund"]]],
]);
const CULPRIT = "billing/money.py"; // bug report: "a refund of $19.99 comes back as $19.98" (a rounding bug)
const everything = () => [...REPO.keys()];
const keyword = (word: string) => [...REPO].filter(([, [, , words]]) => words.includes(word)).map(([f]) => f);
// Start from the failing test and follow imports, the way an agent uses search and an import graph.
function followImports(start: string): string[] {
const seen: string[] = [], todo = [start];
while (todo.length) {
const f = todo.shift()!;
if (!seen.includes(f)) {
seen.push(f);
todo.push(...REPO.get(f)![1]);
}
}
return seen;
}
const runs: [string, string[]][] = [["load everything", everything()], ['keyword search "refund"', keyword("refund")],
["failing test + imports", followImports("tests/test_refund.py")]];
for (const [name, files] of runs) {
const tokens = files.reduce((s, f) => s + REPO.get(f)![0], 0);
const found = files.includes(CULPRIT) ? "includes" : "misses";
console.log(`${name.padEnd(24)} ${String(files.length).padStart(2)} files ${tokens.toLocaleString("en-US").padStart(6)} tokens, ${found} the culprit`);
}import java.util.*;
public class Main {
record FileInfo(int tokens, List<String> imports, Set<String> words) {}
// A small repository: file -> tokens, files it imports, words it contains.
static final Map<String, FileInfo> REPO = new LinkedHashMap<>();
static {
REPO.put("api/routes.py", new FileInfo(1200, List.of("billing/refund.py", "auth/session.py"), Set.of("refund", "login")));
REPO.put("billing/refund.py", new FileInfo(900, List.of("billing/money.py", "billing/policy.py"), Set.of("refund")));
REPO.put("billing/money.py", new FileInfo(400, List.of(), Set.of("round", "cents")));
REPO.put("billing/policy.py", new FileInfo(500, List.of(), Set.of("refund", "window")));
REPO.put("billing/invoice.py", new FileInfo(1100, List.of("billing/money.py"), Set.of("invoice")));
REPO.put("auth/session.py", new FileInfo(800, List.of(), Set.of("login")));
REPO.put("auth/oauth.py", new FileInfo(1500, List.of(), Set.of("login")));
REPO.put("ui/checkout.tsx", new FileInfo(2600, List.of(), Set.of("checkout", "refund")));
REPO.put("docs/CHANGELOG.md", new FileInfo(6000, List.of(), Set.of("refund", "invoice", "login")));
REPO.put("tests/test_refund.py", new FileInfo(700, List.of("billing/refund.py"), Set.of("refund")));
}
static final String CULPRIT = "billing/money.py"; // bug report: "a refund of $19.99 comes back as $19.98" (a rounding bug)
static List<String> keyword(String word) {
List<String> out = new ArrayList<>();
REPO.forEach((f, info) -> { if (info.words().contains(word)) out.add(f); });
return out;
}
// Start from the failing test and follow imports, the way an agent uses search and an import graph.
static List<String> followImports(String start) {
List<String> seen = new ArrayList<>();
Deque<String> todo = new ArrayDeque<>(List.of(start));
while (!todo.isEmpty()) {
String f = todo.poll();
if (!seen.contains(f)) { seen.add(f); todo.addAll(REPO.get(f).imports()); }
}
return seen;
}
public static void main(String[] args) {
Map<String, List<String>> runs = new LinkedHashMap<>();
runs.put("load everything", new ArrayList<>(REPO.keySet()));
runs.put("keyword search \"refund\"", keyword("refund"));
runs.put("failing test + imports", followImports("tests/test_refund.py"));
runs.forEach((name, files) -> {
int tokens = files.stream().mapToInt(f -> REPO.get(f).tokens()).sum();
String found = files.contains(CULPRIT) ? "includes" : "misses";
System.out.println(String.format(Locale.US, "%-24s %2d files %,6d tokens, %s the culprit", name, files.size(), tokens, found));
});
}
}load everything 10 files 15,700 tokens, includes the culprit
keyword search "refund" 6 files 11,900 tokens, misses the culprit
failing test + imports 4 files 2,500 tokens, includes the culprit
5. In your five tools
The names differ; every major coding agent has each piece. Checked 4 November 2026.
| Technique | Where you find it |
|---|---|
| Instructions up front | CLAUDE.md, AGENTS.md (read by Codex, Cursor, Copilot and others), .github/copilot-instructions.md; Module 09 |
| Just-in-time retrieval | Built-in search and read tools in every agent; codebase indexes for semantic search in Cursor and Copilot |
| Structure-aware search | Language-server tools and MCP servers for symbols and references; ask the agent to start from a failing test |
| Compaction | Automatic compaction, plus /compact with a focus and /clear between tasks in Claude Code; similar commands elsewhere |
| Notes files | Ask for a PROGRESS.md or task list the agent updates; memory features (Module 09) |
| Subagents | Every tool's subagents (Module 12): give them the search, keep their summaries |
6. Remember it
How this hook works: what “Leads, not the archive” means
The story → the concept
| Detective | Agent |
|---|---|
| The case file pinned to the wall | Instructions file loaded up front |
| Pulling a file when a lead points to it | Just-in-time retrieval |
| Following who called whom | Import and call graphs |
| The notebook | A notes or progress file |
| A junior summarising the phone records | A subagent returning a short summary |
| Clearing the desk | Compaction and /clear |
The trap it saves you from
"The model has a million-token window, so give it everything." More context isn't better context; the clue gets buried.
7. Common mistakes
8. Hands-on lab (roadmap week 5)
- Measure: run a real bug fix and note how many tokens the session used and which files the agent read.
- Anchor: repeat, starting from a failing test and asking the agent to follow imports. Compare files read and tokens.
- Isolate: send a subagent to analyse a large log and return ten lines. Compare with reading it in the main session.
- Notes: on a multi-hour task, have the agent keep
PROGRESS.md; compact twice; check it picks up where it left off. - Trim: cut your instructions file to what the agent can't learn from the code. Did anything get worse?
9. Self-check quiz
What is just-in-time retrieval?
Why did keyword search miss billing/money.py in the demo?
What should a subagent return to the main agent, per Anthropic's guidance?
You finish one task and start an unrelated one in the same session. What first?
What belongs in the instructions file loaded at the start?
10. Go deeper
| Resource | Time | Why |
|---|---|---|
| Effective context engineering for AI agents (Anthropic, September 2025) | 20 min | The techniques in this module, from the people who built Claude Code. |
| Best practices for Claude Code | 20 min | Instruction files, compaction with a focus, subagents for long output. |
| How we built our multi-agent research system (Anthropic, June 2025) | 20 min | Subagents as context isolation at scale. |
| Don't build multi-agents (Cognition, June 2025) | 10 min | Sharing context, and compression for long tasks. |
| Module 02: The context window | 30 min | Context rot and the basics of compaction. |