What is Context Compaction?
Context Compaction is an AI design pattern for LLM apps and agents, in the Context and knowledge group. Summarize or prune the context as it grows, keeping what matters.
Renata's coding agent handles long tasks. Two or three hours. Migrating a module, upgrading dependencies, fixing a batch of tests.
At the start of a task, it's precise. After one hundred fifty turns, the context holds hundreds of tool results. Whole files, test output thousands of lines long, searches that led nowhere.
Near the end, the agent starts editing files it already fixed.
Reapplying changes that someone had already reverted, on purpose.
And forgetting the constraint agreed at the start. Don't touch the public API. On some tasks, the window overflows, and the run fails.
Starting a fresh session doesn't help. The agent would lose the plan, the decisions, and what's been done. In this lesson, you'll learn to keep an agent clear headed on a task longer than its context window can hold.
Treat the context as working memory, with ongoing maintenance. Three operations, in order of cost.
Prune. Delete the contents of old tool results that were already used, like a file read eighty turns ago. The call stays on record. The content goes.
Summarize. When the context passes a limit, replace the old part with a summary that keeps the goal, the decisions, the current state, and the next steps.
And externalize. Facts that must survive any summary, like the plan, the constraints, and the list of changed files, go to a file or state outside the context. The agent reads them when it needs to.
The trigger is a token limit, checked before each turn. The most recent turns always stay whole. And here's the division of labor. Code decides when to cut. The model only writes the summary.
The agent works as usual.
Before each turn, code measures the context.
The first measure is cheap. Delete the contents of old tool results.
If pruning was enough, the loop continues.
If not, a model summarizes everything before the latest turns.
And critical facts live outside the context, in a notes file or in state. The summary points to them.
Back to Renata. On turn three, she agreed: don't change the public API of the billing module. On turn eighty, the agent read the billing API file, about two thousand four hundred tokens, and ran the tests, about one thousand eight hundred tokens of log. On turn one hundred fifty, those results still ride along on every call, with dozens of others.
With illustrative numbers, by turn one hundred fifty, input already passes sixty thousand tokens per call. Without pruning, the next fifty turns resend that volume. About three million input tokens, just to finish the task.
Pruning swaps the old content for one line. The history still says the read happened. The two thousand four hundred tokens leave. And if the agent needs the file again, it reads it again. That's cheaper than carrying the file for fifty turns.
When pruning isn't enough, the summary comes in. Here's a schema that holds up in Renata's eval. The goal. Decisions. What's done. The current state. The next steps.
And this line. Constraints. Do not change the public API.
Here's one for you. After a compaction, Renata's agent changes a public signature. The summary had the goal, what's done, and the next steps. But no constraints field. What's the fix?
Put constraints in the summary schema, and in a notes file outside the context. Anything that must survive every cut needs a place in the summary, and a copy outside it. A goal and next steps don't store a do not.
Raising the limit so compaction never runs only delays the rot. And a bigger model doesn't remember what you removed from the context. Without that field, the compactor helps you forget the one thing that must not be forgotten.
Short tasks. The compaction step costs more than it saves.
Heavy reading in a well bounded subtask. Isolate it in a subagent. Sometimes the two patterns go together. Isolation keeps heavy material out. Compaction cleans what already got in.
Content that must be read in full for the answer, like a contract reviewed clause by clause. Summarizing there loses what matters.
And a short support chat. Say it ends in eight turns, and a colleague wants to compact every four, to save money. Don't. The history fits, and a summary only risks losing the original request, like the order number in the first message. Keep those facts as explicit state instead.
Now the common traps. Summarizing too early. Compacting every few turns spends a call, and deletes detail that would still be used. Measure first. Cut when the token curve hurts.
A schema with no constraints. We just saw that one. Your retention eval needs a case like it.
Cutting in the middle of a tool call. Separate a tool request from its result, and the next API request is invalid. The cut starts on a user turn.
And throwing the raw history away. You'll need it to debug a summary that invented a decision. Compaction changes what the model sees, not what you keep.
One question teams always ask. After pruning, the agent rereads a file it already read eighty turns ago. Is that a bug? No. Pruned content is gone on purpose. If the file still matters, the tool is called again. And the summary shouldn't copy whole files either. It stores decisions and state, not source code.
How do you evaluate it? Retention first. Ask questions about facts from early in the task, after compaction. That's the eval most people skip. Without it, compaction works on cost, and fails on the product.
Then the completion rate, and rework. Files edited twice, for the same reason.
Then tokens per turn, over the run. The curve should have teeth, not climb forever. And if the tokens before and after a compaction barely change, the old messages were never really replaced.
And the prompt cache. Changing the start of the context can reset it. Watch the cached tokens after each compaction.
Some providers already do part of this for you. Server side compaction, where the API summarizes old context on its own. Or context editing, where the API clears old tool results before the model sees them. Prefer those when they cover your case. And one privacy note. The summary and the notes file often end up in logs. If the conversation holds personal data, compaction creates one more copy. Restrict who can read those logs.
So here's the idea to take with you. A long task needs a maintained memory, not a growing pile. Prune what's been used, summarize the rest into a schema that has room for every do not, and keep the critical facts in a file no summary can delete. Then hour three looks like minute three.
In the next lesson, a smell about the shape of the agent itself: one agent trying to do everything. The mega agent. See you there.
Compactação de Contexto
Context Compaction is an AI design pattern for LLM apps and agents, in the Context and knowledge group. Summarize or prune the context as it grows, keeping what matters.
Summarize or prune the context as it grows, keeping what matters for the next steps. It is for long agents and conversations that would otherwise fill the window and get worse with every turn.
The minutes of a long meeting: nobody rewatches the recording, everybody reads the decisions.
Renata’s coding agent handles two- to three-hour tasks: migrating a module, upgrading dependencies, fixing a batch of tests. At the start of a task it is precise. After 150 turns, the context holds hundreds of tool results: whole files, test output thousands of lines long, searches that led nowhere.
Near the end, the agent starts editing files it already fixed, reapplying changes that were reverted, and forgetting the constraint agreed at the start (“do not touch the public API”). On some tasks the window overflows and the run fails.
That is the Context Rot smell: the history is still there, but it is no longer useful memory. The paper “Lost in the Middle” (Liu et al., 2023) helps explain the symptom. With a full window, the model uses the middle poorly, which is exactly where the turn-3 constraint sits.
Starting a new session does not help: the agent loses the plan, the decisions, and what has been done. Renata needs a way for the context to stay useful after hours.
The question this pattern answers: how do you keep an agent clear-headed on a task longer than its context window can hold?
Treat the context as working memory with ongoing maintenance. Three operations, in order of cost:
The trigger is a token limit, checked before each turn. The most recent turns always stay whole.
Anthropic’s “Building effective agents” (December 2024) treats compaction as context maintenance, not a prompt trick. Code decides when to cut. The model only writes the summary.
On turn 3, Renata agreed: “do not change the public API of the billing module”. On turn 80, the agent read billing/api.py (2,400 tokens) and ran the tests (1,800 tokens of log). On turn 150, those results still ride along on every call, with dozens of others.
For the account, suppose a 4,000-token system prompt and 400 tokens added per turn. By turn 150, input already passes 60,000 tokens. Without pruning, the next 50 turns resend that volume: about 3 million input tokens just to finish the task.
Pruning swaps the old content for one line:
tool_result read_file billing/api.py
[content removed; call the tool again if you need it]
History still says the read happened. The 2,400 tokens leave. If the agent needs the file again, it reads again. That is cheaper than carrying the file for 50 turns.
When pruning is not enough, the summary comes in. The schema that holds up in Renata’s eval looks like this:
{
"goal": "Migrate billing to API v2",
"constraints": ["do not change the public API"],
"decisions": ["keep the endpoint names", "adapt only the internal client"],
"done": ["internal client updated", "unit tests passing"],
"current_state": "contract test fails in test_public_api.py:41",
"next_steps": ["read the contract test", "fix the adapter, not the public signature"]
}
The turn-3 constraint survives only if the schema has a field for it, and if the same constraint lives in a notes file the summary cannot delete. Without that, the compactor “helps” you forget the one thing that must not be forgotten.
flowchart TD
L(["1. Agent turn"]):::llm --> C{"2. Context over the limit?"}
C -->|no| L
C -->|yes| P["3. Prune old results"]:::tool
P --> C2{"4. Still over?"}
C2 -->|no| L
C2 -->|yes| S(["5. Summarize the old part"]):::llm
S --> N[("6. External notes and state")]:::store
N --> L
schema Compaction { goal: string, decisions: list[string], done: list[string],
current_state: string, next_steps: list[string] }
function step(state):
if tokens(state.messages) > SOFT_LIMIT:
# 3. prune: keep the call, delete the old content
for r in old_tool_results(state.messages, older_than=20):
r.content = "[content removed; call the tool again if you need it]"
if tokens(state.messages) > HARD_LIMIT:
# 5. summarize the old part, keep the latest turns
old, recent = split(state.messages, keep_last=10)
summary = llm(COMPACT_PROMPT, old + read(state.notes_file)) -> Compaction
write(state.notes_file, summary) # 6. externalize
state.messages = [as_message(summary)] + recent
trace.span("compaction", tokens_before, tokens(state.messages))
return llm_with_tools(SYSTEM, TOOLS, state) # 1
tool_use from its tool_result makes the next request invalid. The cut starts on a user turn.| Aspect | What happens | How it shows up in the trace |
|---|---|---|
| Calls | One extra per compaction, none for pruning | occasional compaction span |
| Cost | The summary costs one call; the following turns get much cheaper | input tokens dropping after each compaction |
| Latency | The turn that compacts is slower | duration spike on the compaction turn |
| Cache | Changing the start of the context can reset the prompt cache | cache_read_input_tokens dropping after compaction |
| Failure: lost fact | The agent redoes work or breaks an old constraint | repeated action after a compaction |
| Failure: invented summary | The agent acts on something that did not happen | decision in the summary with no source in the history |
{ "task": "Migrate the billing module to the new API", "constraint_turn": 3, "constraint": "do not change the public API", "check": "no public signature changed" }
The retention case is the eval most people skip. Without it, compaction “works” on cost and fails on the product.
compaction span with tokens before and after and the generated summary.tokens_before and tokens_after barely change, the splice did not replace the old messages.The summary and the notes file often go to logs. If the conversation holds personal data (tax id, balance, an order number with a name), compaction creates another copy. Under Brazil’s LGPD, that is processing. Restrict who can read those logs, and do not send the raw history to a compaction model outside company policy.
| Pros | Cons |
|---|---|
| Long tasks fit in the window | Summaries lose details |
| Stable cost and latency per turn | One extra call per compaction |
| The agent focuses on what is relevant now | May invalidate the prompt cache |
| External notes survive any cut | One more prompt to write, version, and evaluate |
The Python tab prunes and summarizes by hand. The TypeScript tab uses the API’s server-side compaction.
Python. Use this when you want to control what gets pruned and the shape of the summary.
import json
import anthropic
client = anthropic.Anthropic()
SOFT, HARD, KEEP_LAST = 60_000, 120_000, 10
COMPACT_PROMPT = """Summarize the agent's work so far as JSON with the keys:
goal, constraints, decisions, done, current_state, next_steps.
Keep every constraint agreed with the user. Do not invent anything."""
def count(messages: list) -> int:
return client.messages.count_tokens(model="claude-opus-5-5", messages=messages).input_tokens
def prune(messages: list, older_than: int = 20) -> None: # 3. prune
for msg in messages[:-older_than]:
if msg["role"] == "user" and isinstance(msg["content"], list):
for block in msg["content"]:
if block.get("type") == "tool_result":
block["content"] = "[removed; call the tool again if you need it]"
def compact(messages: list, notes_path: str) -> list:
if count(messages) > SOFT:
prune(messages)
if count(messages) <= HARD:
return messages
old, recent = messages[:-KEEP_LAST], messages[-KEEP_LAST:]
while recent and recent[0]["role"] != "user": # the cut starts on a user turn
old.append(recent.pop(0))
reply = client.messages.create( # 5. summary
model="claude-opus-5-5", max_tokens=4000, system=COMPACT_PROMPT,
messages=[{"role": "user", "content": json.dumps(old, default=str)[:400_000]}],
)
summary = reply.content[-1].text
open(notes_path, "w", encoding="utf-8").write(summary) # 6. externalize
return [{"role": "user", "content": f"Summary of earlier work:\n{summary}"}] + recentWhat you gained: you decide what goes, the summary’s shape, and where it lives. What you lost: cutting in the right place (never separating a tool_use from its tool_result) is on you.
TypeScript. Use this when you would rather the API summarize on its own and do not want to maintain compaction code.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const messages: Anthropic.Beta.BetaMessageParam[] = [];
export async function turn(userMessage: string, tools: Anthropic.Beta.BetaTool[]) {
messages.push({ role: "user", content: userMessage });
const response = await client.beta.messages.create({
betas: ["compact-2026-01-12"],
model: "claude-opus-5-5",
max_tokens: 16000,
tools,
messages,
// 2 to 5. the API summarizes old context once it passes the limit
context_management: { edits: [{ type: "compact_20260112" }] },
});
// send back the full content: compaction blocks must return on the next call
messages.push({ role: "assistant", content: response.content });
return response;
}What you gained: no summary code to maintain. What you lost: control over the summary’s shape; keep critical notes in your own file anyway.
Answer the 6 questions to complete this lesson. Watching alone does not complete it: the check is what counts toward the trail and the certificate.
Context Compaction is an AI design pattern for LLM apps and agents, in the Context and knowledge group. Summarize or prune the context as it grows, keeping what matters.
In the real-world scenarios Coding agent, Back-office automation with durable execution, and Educational tutor.