Frontier Engineering
← Context & Memory

Context Isolation via Subagents

Also known as: context quarantine, exploration sandboxing, clean-room subtasks

Send noisy, exploratory work to a subagent with a disposable context and keep only its conclusion in the main thread.

8 min read

The problem

Exploration is dirty. Finding the right file takes twelve searches that return mostly irrelevant results; diagnosing an error takes six failed hypotheses. All of that lands in the main agent's context and stays there, and the residue actively hurts: the model keeps referring back to rejected hypotheses, re-reads paths it already ruled out, and reasons increasingly poorly as the ratio of noise to signal climbs.

The pattern

Push exploratory work into a subagent with its own context. The subagent burns as much context as the search requires, and returns only its conclusion — a file path, a diagnosis, a short answer — to the parent. The parent's context grows by a sentence instead of thirty thousand tokens, and the failed attempts vanish with the subagent.

Use when

  • A subtask has a high ratio of intermediate noise to final signal — search, triage, exploration
  • The answer is small even though finding it is expensive
  • The main agent is degrading over a long run as context fills with dead ends
  • You want to try several approaches without contaminating the main line of reasoning

Avoid when

  • The parent needs the intermediate detail — it is evidence, not noise
  • The subtask is cheap enough that spawn overhead dominates
  • The subagent would need most of the parent's context to do its job, defeating the isolation

Tradeoffs

  • GainMain-thread context stays clean, so late-run reasoning stays as good as early-run

    CostThe parent cannot see how the conclusion was reached, which complicates debugging

  • GainFailed exploration costs nothing permanent — it is discarded with the subagent

    CostA subagent that misunderstands its brief fails invisibly and confidently

  • GainSeveral approaches can be tried in parallel without interfering

    CostToken spend rises; the parent pays for exploration it never sees


This is orchestrator–worker applied for a different reason. There, the motive is parallelism; here, it is hygiene — you delegate not to go faster but to keep the mess out of the room where the important reasoning happens.

The failed hypotheses and dead-end searches are discarded with the subagent.

Delegate the search, keep the answer

The parent asks a question, the subagent answers it. What makes the pattern work is a return contract narrow enough to force compression: a schema with a short answer field, the evidence that mattered, and an explicit confidence. Without a schema, subagents return everything they saw and the isolation buys nothing.

class Finding(BaseModel):
    answer: str = Field(max_length=1200)   # the conclusion, not the journey
    evidence: list[str] = Field(max_length=5)
    confidence: Literal["high", "medium", "low"]
    caveats: str | None = None


INVESTIGATE_TOOL = {
    "name": "investigate",
    "description": (
        "Delegate an open-ended search or diagnosis to a fresh agent with its own "
        "context. Use when finding the answer will take many steps but the answer "
        "itself is short. The subagent sees only what you pass in `context` — it "
        "cannot see this conversation."
    ),
    "parameters": {
        "type": "object",
        "properties": {
            "question": {"type": "string", "description": "One specific question to answer."},
            "context": {"type": "string", "description": "Everything the subagent needs. It has no other context."},
            "done_when": {"type": "string", "description": "What a complete answer contains."},
        },
        "required": ["question", "context", "done_when"],
    },
}
warning

The subagent cannot see the parent's conversation, and parents forget this constantly — dispatching "find where that's configured" with no antecedent for "that". Make the context parameter required, and validate that it is non-trivial before spawning.

Bounding the sandbox

An isolated context is also an unsupervised one. Give every subagent a step ceiling, a token ceiling, and a read-only tool set by default — exploration should not be able to mutate anything. A subagent that exhausts its budget returns what it has with low confidence rather than failing outright, which is usually still useful to the parent.

async def investigate(question: str, context: str, done_when: str) -> Finding:
    budget = Budget(max_steps=15, max_tokens=60_000, wall_clock_s=90)

    try:
        return await agent_loop(
            system=INVESTIGATOR_SYSTEM,
            task=f"Question: {question}\n\nContext:\n{context}\n\nComplete when: {done_when}",
            tools=READ_ONLY_TOOLS,      # exploration never mutates
            budget=budget,
            response_schema=Finding,
        )
    except BudgetExhausted as e:
        # Partial knowledge beats a hard failure — say so honestly.
        return Finding(
            answer=e.best_partial_answer or "Could not determine within budget.",
            evidence=e.evidence[:5],
            confidence="low",
            caveats=f"Stopped after {e.steps} steps: {e.reason}",
        )

Keeping the trail without keeping the tokens

The cost of isolation is that the parent cannot see the work. Resolve that outside the context: persist the subagent's full transcript to storage keyed by an id, and include only the id in what returns to the parent. Debugging retrieves the transcript on demand; the context never pays for it.

  • Force compression with a narrow return schema — otherwise you get the whole transcript back
  • Require an explicit context parameter; the subagent cannot see the parent's thread
  • Cap steps, tokens, and wall clock; return low-confidence partials instead of failing
  • Default subagents to read-only tools — unsupervised exploration should not mutate
  • Persist full transcripts out of band and return only an id for later inspection
tip

Watch the ratio of subagent tokens spent to characters returned. A subagent that burns 50k tokens to return two sentences is doing exactly its job; one that burns 50k and returns 8k of prose is leaking the mess back into the parent and needs a tighter schema.