Soba Labs Blog

Context engineering in deep agents: the four levers

9 min read Simon Budziak By Simon Budziak

A stylized terminal window in deep navy with a thin gold border. Glowing cream text bars fill it from the top until a luminous gold rule cuts across near the bottom, where the bars below dissolve into dust and a single gold thread carries a compacted trace onward.

A deep agent that stops making progress three hours into a task has usually not run out of reasoning. It has run out of room. The binding constraint on a long agent run is the context window, and the harness manages it with four separate mechanisms that most teams treat as one. LangChain’s deepagents docs name them plainly: input context, compression, isolation, and long-term memory. Each fires at a different moment, each has its own threshold, and each fails in its own way. This post covers what the four actually do, the exact numbers they trigger on, and the five things we change before any of it touches client work.

Code here is written against the Python deepagents 0.7.0 prerelease line (0.7.0b2, published 2026-07-24). The current stable release is 0.6.12. Where a feature needs a specific version, it says so.

Why a long run degrades at 85 percent, not at 100

The intuition most people carry is that an agent works fine until the context window fills, then breaks. What actually happens is that three automatic mechanisms fire well before the ceiling, each one quietly changing what the model can see.

The first is offloading. When a single tool call returns more than 20,000 tokens, the harness writes the response to the configured backend and substitutes it with a file path reference plus a preview of the first 10 lines. The agent still knows the data exists and can go read it. It just no longer carries the whole thing in the conversation.

The second is truncation of old tool inputs. Once session context crosses 85% of the model’s available window, older tool call inputs are replaced with a pointer to the file on disk.

The third is summarization, and it is the one with teeth. Every create_deep_agent call includes SummarizationMiddleware in the default middleware stack. It triggers at 85% of the model’s max_input_tokens and keeps roughly 10% of tokens as recent context. If the model profile is unavailable, it falls back to a 170,000-token trigger with 6 messages kept.

That fallback deserves a hard look. On a model whose real window is well under 170,000 tokens, a missing profile means the trigger never fires on tokens, and the agent walks into a context overflow instead of a planned compaction. There is a catch net (any ContextOverflowError from a model call makes the deep agent fall back to summarization and retry with the summary plus recent messages), but a catch net is not a plan.

The practical version: your agent is not one long conversation. It is a conversation that gets quietly rewritten at 85%, and the quality of that rewrite is the quality of the second half of the run.

This Is Fine meme. The dog sits calmly in a burning room. Caption above: agent at 84 percent of the context window. Caption below: summarization triggers at 85 percent.

Compression that does not destroy the record

The part of the design worth stealing, whatever framework you use, is that deepagents summarization has two outputs, not one.

The first is the in-context summary: an LLM generates a structured summary covering session intent, artifacts created, and next steps, and that replaces the full conversation history in the agent’s working memory. The second is filesystem preservation: a text rendering of the original conversation messages is written to the filesystem as a canonical record.

The agent forgets, the record does not. That distinction is the whole reason this pattern is safe to run on client work. The working memory shrinks so the run can continue, while the thing an auditor, a debugger, or a future you would need stays on disk and stays searchable. Offloading works the same way: the file path and the ten-line preview are a promise that the data is recoverable, not a deletion.

If you take one design idea from the harness into your own system, take this one. Compression that throws information away is a bug waiting for a postmortem. Compression that moves information from working memory to a retrievable record is an architecture.

A navy reference card titled The four context levers, listing each along a gold thread. One, input context, proactive: what every model call pays for before the agent does anything. Two, compression, marked reactive: offloads any tool result over twenty thousand tokens, summarizes at eighty-five percent of max input tokens, keeps about ten percent as recent context. Three, isolation, proactive: a subagent runs in its own fresh context window and hands back one compact result. Four, long-term memory, proactive: AGENTS dot md always loaded, skills on demand.
Four levers, four different moments. Only compression is reactive; the other three are decisions you make up front.

Isolation: what a subagent actually buys you

Compression is what happens when you have already filled the window. Isolation is how you avoid filling it.

The harness ships a built-in task tool that spawns ephemeral subagents. The docs are precise about the contract: each invocation gets fresh context, the subagent runs autonomously until completion, it makes a single handoff of one final report, and it is stateless and cannot send multiple messages back. Heavy subtask work stays isolated and comes back compressed into a compact result.

There is also a general-purpose subagent available at all times whose stated primary purpose is exactly this: the main agent can delegate a complex task and get a concise answer back without bloat from intermediate tool calls.

from deepagents import create_deep_agent

researcher = {
    "name": "researcher",
    "description": "Runs deep source research and returns a short synthesis.",
    "system_prompt": (
        "Research the question thoroughly.\n"
        "Save raw sources and full extracts to /data/research/.\n"
        "Return only the synthesis, under 400 words. "
        "Do not include raw search results or tool output."
    ),
    "tools": [],
    "model": "anthropic:claude-sonnet-4-6",
}

agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-6",
    subagents=[researcher],
)

Note what the system prompt is doing. It is not decorating the subagent with a persona, it is enforcing the contract that makes isolation pay off.

That matters because the harness documents its own failure mode honestly: context still gets bloated despite using subagents. The two fixes it names are the two lines above. Instruct the subagent to return only the essential summary and keep it short, and have it write large data to the filesystem so only the analysis comes back. A subagent that returns 8,000 tokens of raw findings has spent a whole extra context window to save you nothing.

Isolation is also the expensive lever. We covered the trade in Deep agents in production: in Anthropic’s own reporting, multi-agent systems use roughly 15 times the tokens of a chat. You spend that to protect the main agent’s window, so make sure the thing coming back is worth what the fan-out cost.

Progressive disclosure: memory and skills

The fourth lever is the one that runs before the agent has done anything at all, and it is the cheapest to get right.

Memory is what loads on every single turn. Files like AGENTS.md are always loaded when configured, and the context-engineering docs give one instruction about them: keep memory minimal to avoid context overload; use skills for detailed workflows. Every token in an always-loaded file is a token you pay for on every model call for the entire run.

Skills are the counterpart. The agent reads skill frontmatter at startup, then loads the full skill content only when it decides that skill is relevant. The docs call this progressive disclosure, and it is the right default for anything procedural: a fifteen-step deployment runbook should cost you one line of description until the moment the agent is actually deploying something.

The rule that falls out is simple enough to apply without thinking. If a piece of context is needed on every turn, it belongs in memory and it must be small. If it is needed occasionally but in full, it belongs in a skill and it must be discoverable. Getting this backwards is the most common way we see a deep agent start a run already halfway through its budget.

One related setting most teams miss: unused built-in tools still send their full schemas on every turn. Passing excluded_tools for tools the agent should never call (write_file and execute on a read-only agent, for instance) shrinks the baseline prompt for the entire run. It is configuration, not compression, and it is free.

Compact on purpose, not at the threshold

Automatic summarization fires when the context crosses 85%. That moment is decided by arithmetic, and arithmetic does not know whether the agent just finished a clean unit of work or is halfway through reasoning about one.

You can hand the agent the decision instead. Passing create_summarization_tool_middleware gives it a compact_conversation tool it can call on demand, for example between tasks, rather than waiting for the threshold.

from deepagents import create_deep_agent
from deepagents.backends import StateBackend
from deepagents.middleware.summarization import create_summarization_tool_middleware

model = "anthropic:claude-sonnet-4-6"
backend = StateBackend()

agent = create_deep_agent(
    model=model,
    middleware=[
        create_summarization_tool_middleware(model, backend),
    ],
)

Adding the tool does not disable automatic summarization. Both paths share the same summarization engine and the same state, so you get a deliberate compaction point at a task boundary plus the automatic one as a floor. For a long agent that works through a todo list, compacting between items is close to free and it means the summary is written at a moment where “artifacts created, next steps” is an honest description of where things stand.

What we change before it touches client work

Five things, in the order we check them.

Pin the summarization trigger explicitly. Do not inherit the 170,000-token fallback by accident. If the model profile is not resolving, set the threshold yourself against the window you are actually paying for.

Decide the memory budget before the first run. Measure what AGENTS.md and any always-loaded file cost per turn, multiply by the expected number of model calls, and treat that as a fixed cost of the run. It usually surprises people.

Write the subagent return contract into the system prompt. Word limit, no raw tool output, raw data to the filesystem. Every time. The isolation only pays if the handoff is small.

Know that a middleware override replaces rather than merges. Passing a middleware instance whose .name matches a default swaps it out in place, which requires deepagents>=0.7.0a3. The replacement must be fully configured on its own, and this bites hardest on FilesystemMiddleware, which will not inherit the backend= and permissions= you passed to create_deep_agent().

Keep the human gate on the actions, not on the context. None of these four levers decides whether the agent should be allowed to do the thing it is about to do. That is a separate control, and it should never be based on the model’s own read of how sure it is, which we covered in Stop asking the LLM how confident it is.

The takeaway

Deep agents give you a genuinely good default stack for context, and the defaults are documented down to the threshold. The mistake is treating that stack as a single feature called “it handles long conversations”. It is four levers with four different jobs: input context sets what you pay on every turn, compression rescues you at 85%, isolation stops you getting there, and long-term memory decides what survives the run at all.

Get the two proactive levers right, memory budget and subagent contracts, and the reactive one rarely has to do heavy lifting. Get them wrong and you are relying on an automatic summary, written at an arbitrary moment, to carry the second half of your agent’s reasoning. That is a lot of trust to place in an arithmetic threshold.

Sources

Book a call

Let's find the work you can hand off.

Bring one workflow that's slow, repetitive, or stuck on a few experts. We'll map what a custom agentic system could do, and what should stay human.

No sales pitch. Just a straight read on whether it should become software, training, or nothing yet.

Not ready for a call? Write to [email protected]