Subagents
A subagent is an agent loop your agent loop can call: the harness registers Task as one more tool, and when the model requests it, the handler runs the same call-inspect-dispatch-append cycle on a fresh messages[] seeded with nothing but the delegation prompt.
The child does its messy exploration in that private context, returns one line of text, and everything else it read is thrown away. That is the whole trick, and it has a name: context isolation.
Why bother? Because this append-only agent loop retains every tool result unless the harness removes or summarizes it. Ask a question that takes four archive searches and your conversation now carries four fat pages — a couple of thousand tokens — that remain in later model requests while this history is retained. Real runtimes may compact, truncate, or cache context. Delegate the same four searches to a subagent and your conversation grows by one result card (plus the Task call). In this lesson's run the difference is ~90 tokens versus ~2.1k for the identical work.
The catch is the same wall seen from the other side. The child's context contains the prompt and nothing else — not the conversation, not the user's earlier clarifications, not what you both agreed three messages ago. Whatever you didn't put in the prompt does not exist for the child. Half this lesson is watching that save you; the other half is watching it burn you.
This lesson builds directly on the agent loop (the child is that loop, run on a second array) and on tool calling (Task sits in the same dispatch map as search). The two lanes on screen are two messages[] arrays — the same accumulator the agent-loop lesson draws as a single column.
How Subagents Work: One Line Builds the Wall
The main loop's side is three lines: the model returns Task(prompt="…") the way it returns any tool request, the harness calls run_subagent(prompt), and the returned string is appended as an ordinary tool_result. The main loop cannot tell a subagent from a calculator.
TOOLS["Task"] = run_subagent
def run_subagent(prompt: str) -> str:
sub = [{"role": "user", "content": prompt}]
while True:
r = model.call(sub, tools=TOOL_SCHEMAS)
sub.append(assistant(r.content))
if r.stop_reason == "end_turn":
return r.text
sub.append(tool_results([TOOLS[c.name](**c.input) for c in tool_uses_of(r)]))
run_subagent is where the isolation happens, and it's one line: sub = [{"role": "user", "content": prompt}]. A brand-new array. The child's loop then runs exactly like the parent's — call the model, run the tool it asks for, append the result, repeat — but every append lands in sub, not in the main conversation. When the child's model finally answers with text, return r.text sends only that string back across. The sub array, with its thousands of tokens of search results, goes out of scope and dies.
Two consequences fall out of that one line, and they are the lesson:
The ledger. The main context grows by one result card per delegation (plus the Task call), no matter how much the child explored — O(1) result cards instead of O(k) tool-step records, assuming a bounded-size return; a card is not a fixed token count. And because each model call re-sends the whole conversation, inline pollution isn't a one-off cost: the 432-token search result appended at turn 1 remains input context at later turns; actual billing depends on the provider and caching. Keeping the trunk thin compounds too.
The blind spot. The prompt is the only channel in. The child doesn't inherit the conversation, so a constraint that lives earlier in the main thread — "I mean the 2011 original, not the 2024 remake" — is invisible to it unless the delegating model wrote it into the prompt. A subagent can answer its prompt perfectly and your question wrongly at the same time.
Subagents Step by Step: Watch the Two Counters
The default run delegates a self-contained research task. The user asks: Which year was the studio behind the original Aurora Run film founded? Watch the two token counters — that's where the phenomenon lives.
- Init. The main conversation holds one user card (~18 tok).
main = 18 tok. - Plan. The main model answers with a tool request:
Task(prompt="Find the founding year of the studio that produced the original 2011 film Aurora Run."). Read that prompt carefully — it carries its own constraint ("the original 2011 film"), which is what makes this task self-contained. The request card joins the trunk:main = 52 tok. - Fork. The harness spawns
sub = [prompt]— a second lane opens holding one ~26-token card. The trunk dims: the main conversation is now waiting, untouched. - Child search 1.
search["Aurora Run"]returns the archive's index entry (~432 tok with the request): the primary title is the 2024 remake, with a see-also for the 2011 film. That lands in the child's lane.mainhasn't moved. - Child search 2. The prompt says 2011, so the child follows the see-also:
search["Aurora Run (2011)"](~572 tok) — directed by Mara Voss, produced by Northlight. - Child search 3.
search["Northlight"](~392 tok) is a fork in the road: two companies match, Northlight Pictures and Northlight Films. - Child search 4.
search["Northlight Pictures"](~692 tok) settles it: founded in 1987, produced Aurora Run (2011). The child's context now holds ~2.1k tokens. The main counter still reads 52. - Conclude. The child's model answers with text: "Northlight Pictures — founded in 1987." Sixteen tokens. Of everything the child read, only this line will travel.
- Merge. That one card crosses back into the trunk as a
tool_result; the sub lane greys out —sub = discarded, ~2.1k tokens freed.main = 68 tok. - Final. The main model, seeing the result, answers the user: "The studio was Northlight Pictures, founded in 1987."
main = 90 tok. Four searches happened; the main conversation contains none of them.
Now flip mode to inline and run the identical task. Same four searches, same pages — but every result appends to the one and only messages[]. The counter climbs 432, then 1,004, then 1,396, then 2,088 tokens of exploration, and the run ends with main = 2,128 tok. Same answer, ~24× the retained main-context count in this fixture. That flip is the cleanest A/B in the lesson: the work is identical, only the destination of append changed.
Then try the Context-dependent task, and this is where the wall bites back. The user asks the director-birth-year question and adds a second message: "Careful — I mean the 2011 original, not the 2024 remake." Delegated, the model writes Task(prompt="Find the birth year of the director of the film Aurora Run.") — and the clarification isn't in it. The child searches, resolves "Aurora Run" to the archive's primary title (the remake), chases its director, and returns born 1989, confident and wrong. Run the same task inline and the loop gets it right — Mara Voss, born 1974 — because when the search surfaces both films, the clarification is sitting right there in the same messages[] the model re-reads on every call. Same task and archive, with scripted model choices; the only thing that changed is which side of the fork the constraint lived on.
Edge Cases
- A trivial delegation. Fork a subagent for a one-search task and the overhead (the Task card, the result card, a whole extra loop) buys you almost nothing — the ledger only pays off when the child's exploration is bigger than its conclusion. Delegate journeys, not errands.
- The child comes back empty or wrong. The parent gets one opaque line. It can't audit the child's reasoning — the evidence was discarded with the lane. If the conclusion smells off, the parent's only move is to re-delegate with a sharper prompt or do the work inline; there is no transcript to inspect.
- A prompt that should have carried context, and didn't. The blind-spot run is the canonical case: nothing errors, nothing warns. The child returns a well-formed answer to the question as written. Isolation failures are silent — which is exactly why the delegating model, not the harness, is responsible for packing the prompt.
- The child needs to ask the user something. It can't. There is no channel from the child to the conversation except the final return value. A subagent that hits genuine ambiguity can only guess or report the ambiguity as its answer.
Common Mistakes
- Treating the subagent as special harness machinery. It's
TOOLS["Task"] = run_subagent— one more entry in the same dispatch map assearch. The main loop doesn't branch on it, doesn't know it spawned a loop, and appends its result like any othertool_result. - Assuming the child sees the conversation. It sees the prompt string. Anything the user said earlier — constraints, corrections, preferences — must be written into the prompt by the delegating model, or it's gone. When a subagent returns a confidently wrong answer, check the prompt before you blame the model.
- Returning the transcript instead of the conclusion. If the child sends back everything it read "to be safe," the merge imports all the noise delegation existed to exclude. The return value should be the distilled answer; the exploration is disposable by design.
- Delegating to dodge the token bill and forgetting the bill compounds. The win isn't just that main stays small at the merge — it's that every later model call re-sends a thin conversation instead of a fat one. Conversely, inline noise isn't paid once; it's rent.
- Expecting the parent to catch the child's errors. The parent sees one line and has no evidence to check it against. Delegation trades auditability for cleanliness — the ReAct lesson is about exactly the grounding chain that a subagent's merge throws away.
A Note on Simplification
The harness mechanics on screen — the fork, the fresh array, the single-line return, the token accounting — are real and deterministic; that's the part this lesson exists to teach. The model's utterances (the delegation prompt, the search choices, the conclusions) are scripted for clarity, not recorded from a live model; tool-calling and react-loop show real recorded runs of the same loop machinery, and the 2011 film's facts here match react-loop's recorded trace. Token counts are illustrative sums over the conversation cards — a real run also carries a system prompt, tool schemas, and thinking tokens that this ledger omits. These isolation and communication limits belong to this fixture, not all subagent products. Production subagents add machinery too: they inherit tool definitions and a model choice, run under their own turn budgets, can execute in parallel, and often return structured output rather than a bare string. None of that changes the shape: fork a clean context, work in it, send one thing back.
Keep exploring
A few lessons that share the same technique or lead into the next idea.
Premium players need paid access. Every Explain doc is free.
Found a mistake or something unclear? Leave feedback.