Topic 54

Context Isolation Is the Real Reason

Isolation

The argument for a subagent that survives scrutiny is that some material must not be in the main context. A subagent that reads forty carrier tracking records and returns three sentences keeps 12,000 tokens of scan codes and depot names out of the decision context, and the decision that follows is made on a clean buffer rather than on the far end of a wall of noise.

That is a measurable improvement rather than an architectural preference, and it is the only justification in this chapter that does not depend on taste. It also comes with an interface problem that is easy to underrate: everything the parent will ever know about that work is whatever fits in the summary, so the summary is not a convenience — it is the boundary, and it needs designing like one.

Two message lists, and the only thing that crosses is the value your code returns
Parent context · clean, and the whole point is that it stays that way
The seller-liability decision — taken on an 8,000-token returns budget
Envelope downone questionfacts, not transcripta slice of the budget
Summary upthe findingstatus · coverage · confidenceevidence ids
the boundary — a subagent is a tool whose implementation happens to be another agent, and nothing leaks unless you write the line that leaks it
Subagent frame · a fresh loop, its own window, thrown away at the end
What it read40 carrier recordsabout 12,000 tokens
What it may doread-only tool listnothing that moves money
What the loop writesstatus · coveragestop reason

The Mechanism

A subagent is the same loop from Chapter 1, started fresh. Your dispatcher builds a new message list with its own short system prompt, its own tool list and the task it has been given, then runs decide-act-observe until the subagent answers or hits the budget it was handed. From the parent's side, none of that is visible. The parent asked for a tool, and one tool result came back.

That is the whole trick, and it is worth being unromantic about it: a subagent is a tool whose implementation happens to be another agent. The isolation comes from no property of the model and no framework feature. It is the plain fact that the parent's message list and the child's message list are two different Python objects, and only the value your code chooses to return crosses from one to the other. Nothing leaks unless you write the line that leaks it.

When Isolation Genuinely Pays

Three shapes come up repeatedly. The first is bulk reading: many documents, one conclusion. Sundry's damage disputes against a repeat seller ask whether a parcel was mishandled in transit or whether the packaging is systematically weak, and answering it means reading the carrier's scan history for the last forty deliveries of the same product. Those forty records are around 12,000 tokens against a returns budget of 8,000, and 11,800 of them are irrelevant the moment the pattern is spotted.

The second is untrusted content. The seller-written product description comes back inside every get_order response, and Chapter 12 is about what happens when somebody writes instructions into it. A subagent with no write tools can read that material and return a structured verdict, which narrows the channel from the seller's prose into the parent's decision context from "several paragraphs" to "a handful of typed fields". It narrows the channel; it does not close it, and nothing in this chapter should be read as a fix for injection. The controls that hold are still the ones outside the model.

The third is the long side quest. A ticket where the buyer, the seller and the carrier disagree can spend eleven turns establishing who is right about the delivery date, and every one of those turns stays in the thread afterwards, pushing the customer's actual request further from the decision. Chapter 5 measured what that does: a constraint sitting 12,000 tokens behind more recent, more specific material loses on every axis. Isolating the side quest keeps the main thread short, which is the thing that was actually helping.

The Summary Is the Interface

Everything the parent will ever know is in the summary, so it gets a schema and structured output (Chapter 2), never free prose. Prose returns two things that should never travel together: a conclusion, and a tone of confidence that the parent inherits without inheriting a single piece of the evidence behind it.

The contract a Sundry read-only subagent returns — the parent sees this and nothing else
{
  "task": "damage_pattern_check",
  "status": "partial",          # complete | partial | aborted
  "coverage": {"requested": 40, "read": 12},
  "stop_reason": "turn_budget_exhausted",
  "finding": "6 of 12 deliveries carry a damage scan; all 6 at depot LDS-2",
  "confidence": "low",           # low, on 12 of 40 records
  "evidence": ["SU-88421", "SU-90317", "SU-91044"],
  "tokens_read": 3620
}

Six fields do the work. The status says whether the job finished. The coverage pair says how much of it was done, in the units the task was stated in. The stop reason distinguishes a job that ended because it was done from one that ended because it ran out of turns. The finding is the answer, in one sentence with numbers in it. The confidence is a signal the parent can branch on rather than a phrase it has to interpret. And the evidence list gives order ids the parent can pull in full if the decision turns out to hinge on them, which converts the summary from a dead end into a pointer.

Three fields on that list are not written by the model at all. The dispatcher fills status, coverage and stop_reason from the loop's own bookkeeping — how many records it actually fetched, why the loop exited — because those are facts about the run rather than opinions about the work. That distinction is the single most useful line of code in this topic, and the next section is about what happens without it.

The Confident-Summary Failure

A subagent that runs out of turns writes a tidy conclusion anyway. It is not lying and it is not broken; it is a language model asked for a summary, and a summary of incomplete work reads exactly like a summary of complete work. The Sundry version cost a real customer a real outcome, on the same task before that contract existed: the pattern-check subagent was given four turns, got through 12 of the 40 deliveries it had been handed, and returned one line of prose — "no consistent pattern of transit damage across recent deliveries" — with nothing in it about the 28 it never read. The parent read that as settled, declined the seller-liability claim, and the buyer filed again nine days later with photographs.

The parent had no way to know. There was nothing in the returned text that distinguished twelve records from forty, and asking the model to promise it will always mention incompleteness is the same move as asking it to promise it will never hallucinate — it works most of the time, which is another way of saying it fails on the cases you would have wanted it to catch. The fix is structural. The loop knows it exhausted the budget, because the loop is the thing that counted the turns, so the loop writes the field. The model contributes the finding, and only the finding.

Then the parent has to act on it, which is the half teams skip after adding the field. Sundry's rule is that a decision that moves money or refuses a claim requires status: "complete". Anything else routes to a second pass with a larger budget or to escalate_to_human with the coverage numbers attached. A completion status nobody branches on is a comment.

Sizing the Boundary

A subagent worth having does enough work to justify its own overhead. That overhead at Sundry is about 700 tokens of prompt and schemas on every one of the child's turns — less than the 980 a full specialist carries, because the tool list is shorter — plus two extra model calls, one to build the envelope and one to read the summary, and the 4.4 seconds of serial latency any handoff costs. A subagent that makes one tool call and returns what it got is a function call with a language model attached, and it is strictly worse than the function call: slower, more expensive, and capable of paraphrasing the result incorrectly.

Sundry's threshold is written into the code review checklist rather than into a prompt: a subagent must be expected to make at least four tool calls, or to read at least 3,000 tokens of material that will not cross the boundary. Under that line, the work goes into the dispatcher as a plain function. Above it, isolation earns its keep on the arithmetic alone. And in both cases the child's full trace is logged with the parent's run id, because the parent seeing only the summary is a design decision about the model's context — not about yours, when the thing has misbehaved at two in the morning and you need to know what it actually read.

Common Mistakes
  • Returning prose from a subagent — the parent inherits the child's confidence without a single piece of its evidence, and a fluent paragraph about twelve records is indistinguishable from a fluent paragraph about forty.
  • Leaving completion status out of the summary, or letting the model write it — partial work is reported as complete, the parent acts on it, and the failure surfaces as a customer complaint nine days later.
  • Isolating trivial work — a subagent for one tool call adds 700 tokens of prefix, two model calls and 4.4 seconds, and the only thing it can do that a function cannot is get the answer wrong.
  • Giving an isolation subagent write tools — the point of reading untrusted seller text in a separate context was that nothing it says can move money, and one issue_refund in its tool list gives that away.
Best Practices
  • Require a structured summary with required fields, a completion status, coverage numbers and a confidence signal, and reject any subagent result that does not validate against the schema.
  • Have the dispatcher write status, coverage and stop reason from the loop's own bookkeeping rather than asking the model to report on itself.
  • Give read-only subagents read-only tool lists, enforced at the dispatcher where authorization already lives, and never in the child's prompt.
  • Log the child's full trace against the parent's run id, so the summary is what the model saw and never what you are stuck debugging with.
Comparable toolsCoding agents subagent search over a large repositoryFramework subagents the same boundary with defaults attachedMap-reduce over documents the same idea without a loopStructured output the schema that makes the summary usable

Knowledge Check

What does context isolation actually buy when a subagent reads forty carrier records and returns three sentences?

  • The parent decides on a clean buffer, because the 12,000 tokens never enter its context at all
  • A smaller bill, because the forty records are read once by a cheaper model instead of by the main one
  • A faster answer, since the child works through the records while the parent continues with the ticket
  • Better accuracy on the records themselves, because a dedicated agent is specialized for reading them

Why does the dispatcher, rather than the model, fill in the summary's completion status?

  • Because the loop counted the turns, so completion is a fact about the run rather than a judgement about the work
  • Because generating that field in the model's output would add tokens to every subagent call that returns
  • Because the provider already returns a stop reason on every call, and writing a second completion field would duplicate it
  • Because schema validation rejects a summary whose status the model got wrong, which the model cannot detect

A pattern-check subagent hits its four-turn budget after 12 of 40 deliveries and reports no consistent damage pattern. What is the failure here?

  • The parent cannot tell a finished job from an abandoned one, so it declines a claim on a third of the evidence
  • The subagent invented deliveries it never read, so the finding describes records that do not exist
  • The turn budget was set too low, and raising it to twelve turns removes the class of failure entirely
  • The subagent acted on its own finding by refusing the claim before the parent had reviewed it

When is a subagent boundary not worth having?

  • When it makes one tool call and hands back what it got
  • When it reads forty documents to answer a single question
  • When the material it reads was written by a third party
  • When the work would otherwise take eleven turns on the thread

You got correct