Multiple agents and context isolation

Compaction and a memory store keep one agent coherent while its window fills. Neither helps with a subtask whose working material would never fit, or one whose noisy intermediate output would crowd out everything the main run needs. Isolation is the move that handles both, by giving the subtask a window of its own that nothing else has to read.

This is the fourth of the four things a team can do with a context budget, and it is the one that costs the most to get wrong. Writing persists material outside the window, selecting brings in only what is needed now, and compressing shortens what is already there, so all three operate on one window. Isolation creates a second one instead, and everything difficult about multiple agents follows from the fact that the two windows cannot see each other.

The sections below give the contract a subagent receives, set out what may cross the boundary in each direction, work the token and wall clock arithmetic on a task split three ways, and then take the costs of the handoff honestly. The last section covers the conditions under which one well built agent beats several.

Isolation as a move on the context budget

A research task makes the shape clear. An agent asked to compare three suppliers reads product pages, pricing tables and support documentation for each, which comes to around 9,000 tokens of raw material per supplier. Almost none of that material is needed to answer the question, and all of it is needed to work out the answer.

Run in one window, those 27,000 tokens accumulate in the transcript and are resent on every subsequent pass. Run in three isolated windows, each worker reads its own 9,000 tokens, writes a 300 token finding and disappears. The parent receives 900 tokens and never sees the rest.

The gain is not only cost. A worker that misreads a pricing table produces one wrong finding, and the other two workers are unaffected by it because they never read the table or the worker's reasoning about it. In a single window that misreading sits in the transcript and every later step reads it.

The contract a subagent receives

A subagent is a separate run of the agent loop, so it needs everything a run needs. The difference is that a parent supplies all of it explicitly, since the worker has no shared history to fall back on.

{
  "task": "Find the published list price, the minimum contract term and the support response time for Supplier C. Use only supplier-c.example and its documentation subdomain. Do not read review sites or comparison articles.",
  "tools": ["fetch_page", "search_site"],
  "budget": {
    "max_steps": 8,
    "max_tokens": 40000,
    "max_seconds": 120
  },
  "return_schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["supplier", "findings", "unresolved"],
    "properties": {
      "supplier": { "type": "string" },
      "findings": {
        "type": "array",
        "maxItems": 6,
        "items": {
          "type": "object",
          "additionalProperties": false,
          "required": ["field", "value", "source_url", "quote"],
          "properties": {
            "field": {
              "type": "string",
              "enum": ["list_price", "contract_term", "support_response"]
            },
            "value": { "type": "string", "maxLength": 120 },
            "source_url": { "type": "string" },
            "quote": { "type": "string", "maxLength": 200 }
          }
        }
      },
      "unresolved": {
        "type": "array",
        "items": { "type": "string", "maxLength": 120 },
        "description": "Fields the sources did not state. Never guess one."
      }
    }
  }
}

Four parts of that contract each stop a specific failure. The task statement names the sources and forbids the rest, because a worker with a browsing tool and a vague brief will read a comparison article and return somebody's marketing. The tool list is a subset, so the worker holds two tools out of the parent's twelve. The budget is the worker's own, so one slow supplier cannot spend the whole run's allowance. And the return schema forces a source URL and a quotation against every value, which is what lets the parent tell a finding from a guess.

The unresolved array matters as much as findings. A worker with no legal way to report a missing figure invents one, for exactly the reasons the page on refusals set out.

What crosses the boundary in each direction

Two things cross, and one thing must not.

Going in, a task statement. The parent writes down everything it knows that the worker will need. A parent that omits something gets an answer to a slightly different question and has no way to see which question was answered.

The brief a parent writes by default

  "Research Supplier C's pricing."
The same task, written for a worker holding no shared context

  "Find three things about Supplier C, for a finance team of forty people in
   the United Kingdom choosing an annual contract.

     1. The published list price for the tier covering forty seats.
     2. The minimum contract term.
     3. The stated support response time on that tier.

   Read only supplier-c.example and docs.supplier-c.example. Do not read
   review sites, comparison articles or press coverage.

   Quote the sentence each figure came from and give the page it appeared on.
   If a figure is not published, add it to unresolved. Never estimate a
   price."

Six things separate the two, and the parent already knew all six. The buyer, the seat count, the country, the permitted sources, the evidence each figure has to carry and what to do about a figure nobody publishes. The first brief leaves every one of them to the worker, which means the worker decides them and nothing records what it decided.

Coming out, a structured result. The return schema above, filled in. Bounded in size, typed and carrying its sources.

Never the transcript. Passing the worker's full conversation back to the parent undoes the entire exercise, because the 9,000 tokens are then in the parent's window after all. It also reintroduces the worker's mistaken reasoning into a context that was otherwise clean.

async def compare(suppliers: list[str], ctx: Session) -> Comparison:
    tasks = [build_task(s) for s in suppliers]

    results = await asyncio.gather(
        *(run_worker(t, ctx) for t in tasks),
        return_exceptions=True,
    )

    findings, failed = [], []
    for supplier, result in zip(suppliers, results):
        if isinstance(result, Exception) or result.stopped_reason:
            # A worker that hit its own budget is a fact about this run, not
            # a reason to abandon the other two.
            failed.append((supplier, describe(result)))
            continue
        findings.append(result)

    # The parent composes from 900 tokens of findings, never from transcripts.
    return call(COMPOSE_PROMPT, findings=findings, failed=failed,
                model=LARGE, schema=ComparisonSchema)

The failed list is the part that separates a usable design from a fragile one. One worker stopping at its step cap is an ordinary event, and a parent that treats it as an exception loses the two results that succeeded. A parent that carries the failure into the answer produces a comparison naming the supplier it could not research and the reason for it. The other design leaves a silent gap.

The arithmetic of one agent against three workers

The same research task, priced both ways. A step is a model call plus one observation of about 2,250 tokens, and the agent needs twelve steps in total whichever way the work is arranged.

One agent, twelve serial stepsParent and three workers
Fixed prefix per call1,600 tokens1,600 parent, 1,100 per worker
Model calls121 plan, 12 worker steps, 1 compose
Input tokens across every call177,60062,300
Output tokens1,8002,400
Parent window at the end30,400 tokens2,780 tokens
Wall clock at 3.2 seconds per step38.4 s17.8 s

The input figure is worth following, because it is where the saving lives. In one window the nth call carries 1,600 plus n minus one lots of 2,400, so twelve calls come to 19,200 plus 2,400 times 66, which is 177,600. Split three ways, each worker runs four steps over its own smaller prefix and comes to 19,280, the three together to 57,840, and the parent's plan and compose calls add 4,460.

The wall clock falls for a different reason. Three workers run at the same time, so the elapsed time matches the slowest worker and never the sum of all three, which is the same effect as fanning out a chain.

The cost of the handoff

Five costs come with the split, and none of them appears in the table above.

The parent has to specify completely. Anything the parent knows implicitly is invisible to the worker. This is genuinely difficult, and a badly specified task is the commonest reason a multi agent design underperforms the single agent it replaced.

The result loses what nobody thought to ask for. A 300 token finding against a fixed schema cannot carry the detail the parent did not know it would need. Discovering that at the compose step means running the worker again with a wider schema, which spends the saving twice.

The worker cannot ask. A worker meeting an ambiguity either guesses or returns an unresolved entry. Allowing it to ask the parent turns each question into a round trip, and enough round trips remove the point of isolating it.

Each worker pays its own prefix. A worker's system prompt and tool definitions are resent on every one of its steps, so a task split into twelve one step workers costs far more than one worker doing twelve steps.

The parent cannot see the reasoning. A finding that turns out to be wrong arrives with no trace of how the worker reached it, unless the application stored the worker's transcript separately. That storage is the thing to build before the first production incident rather than after it.

When one well built agent wins

The published evidence on multi agent systems is not flattering, and it is worth reading before a design commits to several. Mert Cemri and colleagues published an analysis in March 2025 of 150 execution traces from multi agent systems built on several frameworks, annotated by hand, and produced a taxonomy of fourteen failure modes grouped into three categories. Those categories are faults in how the system was designed, misalignment between the agents themselves, and failures to verify the task was actually done.

The middle category is the one that matters for this decision. It exists only because there is more than one agent, which means a team choosing a multi agent design has taken on a class of failure that a single agent cannot have. Coordination is where the reliability goes.

Three conditions between them decide it. Isolation wins when the subtask is read heavy, so a large volume of material collapses into a small result. It wins when the subtasks are genuinely independent, so they can run at the same time and one cannot corrupt another. And it wins when the return can be specified in advance, so a schema names everything the parent will need.

One agent wins whenever the work is a single thread of reasoning where each decision depends on the one before, since splitting that produces a chain of handoffs that each lose information. It also wins whenever the subtasks would need to negotiate, because negotiation between two agents is a conversation nobody is supervising. And it wins by default, since the cheapest way to avoid coordination failures is to have no coordination.

The most restrained version of the split is worth stating on its own. Workers read and the parent writes. A worker holding only retrieval tools cannot take an action the parent never approved, which removes the most serious thing that can go wrong in a structure where one model is directing another.

Tools, a loop, memory and isolation have each bought an agent something and brought a new way to fail with it. Tools let an agent act and let it act wrongly. A loop lets it persist and lets it run away. Memory lets it remember and lets it remember something false. Isolation lets it scale and lets two agents disagree in a conversation nobody reads. Those failures have names, they recur, and they have controls.

Common misconceptions

“More agents on a problem means more capability applied to it.”

Coordination is a source of failure a single agent does not have. Mert Cemri and colleagues annotated 150 execution traces from multi agent systems in March 2025 and named fourteen failure modes in three categories, one of which covers misalignment between the agents and exists only because there is more than one. Splitting a task adds that category to whatever the task already carried.

Where this is examined
Prompt and Context Engineering
Tools and Agents, 18 per cent of the exam.
Related material
Book
AI Engineering, On splitting agent work and what the split costs.
Book
Designing Data-Intensive Applications, On coordination between processes and why it is the hard part.
Concepts