AI EngineeringZero to ProductionHome·About·What’s new·Contact
Anthropic Skills · Chapter K5

Subagents

Delegate to specialized agents with isolated context: defining subagents, parallel fan-out/verify/synthesize (modeled in Python), when not to delegate, and a shared subagent library.

⏱️ ~2.5 hours🧪 6 labs🎯 Beginner→Tech-lead

Learning objectives

  • Explain subagents and the context-isolation problem they solve.
  • Define a subagent with its own prompt, tools, and model.
  • Delegate and run subagents in parallel.
  • Design and govern multi-subagent workflows.
▶ Runnable companionCode saved under code/ak5-subagents/. Python runs offline; configs are ready to use.

1 · Why subagents essential

Start with the failure you've probably watched happen. You give one agent a big job — "audit this codebase for security issues" — and it dutifully starts reading. File after file streams into its context: the whole auth module, three dead-end configs, a pile of grep output. By the time it's ready to actually reason about the bug on line 12, its memory is stuffed with a thousand details that no longer matter, and the useful signal is drowning in the noise it gathered getting there. The agent didn't get dumber; its workspace got cluttered.

Here's the mental model that fixes this: an agent's context is a desk, not a filing cabinet. It's small, it's where all the thinking happens, and everything you pile on it competes for the same limited surface. A single agent doing everything has to keep every scrap of every subtask on that one desk at once. That's the core problem subagents solve — not "the agent can't do the work," but "the agent can't do the work and keep a clean desk at the same time."

Think of how a good manager delegates. You don't ask your researcher to narrate every article they skimmed — you ask them to go read the pile and come back with a one-paragraph summary. You get the conclusion without inheriting the process. A subagent is exactly that: a colleague with their own desk, who does the messy reading in their own room and hands you back only the answer.

A single agent doing everything fills its context with detail irrelevant to the next step. A subagent is a specialized agent the main session delegates to: its own prompt, tools, model, and — crucially — its own context. It does a job and returns just the conclusion, keeping the main thread clean.

The mistake to name up front, because the rest of this chapter keeps circling back to it: once delegation feels good, the temptation is to delegate everything. But every subagent is a fresh colleague you have to brief from scratch and wait for — it starts with a blank desk and costs you a round trip. Spin one up for a two-line edit and you've hired a contractor to change a lightbulb. The skill isn't "delegate more"; it's knowing which jobs are worth the hand-off and which the main agent should just do itself. Hold that tension as you read on — you'll meet it formally as the over-delegation anti-pattern in §6.

Main agent orchestrates Delegate task scoped job Subagent own tools/model Returns result not the transcript Main continues context stays clean
🗺️ How to read this diagram

This picture shows what a subagent is: instead of doing everything itself, the main agent hands off one well-defined job to a fresh helper that has its own memory (its own context), and gets back only the answer. Read the boxes left to right — they are the life of one delegated task.

  • Main agent (orchestrates) — the session you're talking to. It decides a piece of work is worth handing off rather than doing inline.
  • Delegate task (scoped job) — the main agent describes one narrow job, e.g. "review this file". Scoped means small and specific, not "do the whole project".
  • Subagent (own context) — a fresh agent spun up just for this job, with its own tools and model and a blank memory. It can read a hundred files here without cluttering the main agent's memory.
  • Returns conclusion (not the transcript) — the subagent sends back only its final answer, not everything it read and thought along the way. That's the whole trick.
  • Main continues (context stays clean) — the main agent carries on with its memory uncluttered, holding just the conclusion it needed.

In short: A subagent is like asking a colleague to research something and give you a one-paragraph summary — you get the answer without having to read everything they read.

2 · Context isolation, quantified essential

"Keeps the context clean" sounds nice but vague — so let's put a number on it, because the number is genuinely startling. In §1 we argued isolation matters; here you'll feel how much. The claim is simple: whether a subtask costs you 100 tokens or 8,000 tokens of your main agent's precious desk space is entirely a choice about where the work happens.

Recall why this is life-or-death for a long task. A context window is a hard ceiling — a fixed number of tokens the model can hold at once. Do six file-heavy subtasks inline and each one's full material (every file read, every intermediate thought) stacks up in that one window until you hit the wall and the agent starts forgetting the beginning of its own job. Do them as subagents and only the conclusions come home. The subagent burns through 8,000 tokens of reading in its own throwaway window; your main agent receives three lines.

The analogy that makes it click: it's the difference between a colleague forwarding you every email in a 200-message thread versus forwarding you the one-line decision at the end. Same information reaches you either way — but one buries your inbox and the other respects it. Isolation is just deciding the intermediate mess stays in their inbox.

The subagent reads a hundred files and returns three lines; the main agent never sees those files — only the answer. That isolation is what lets long, complex work fit in a context window.

Where people trip: they assume delegation must be slower because it's "more agents" — and forget that the main agent's context is the scarce resource, not the raw compute. The subagent still does all 8,000 tokens of work; you just don't carry it. The lab below runs the arithmetic both ways so the ~10× gap isn't a slogan but a figure you watched compute.

context-window ceiling inline · everything piles up main 2000 +8000 +8000 +8000 +8000 +8000 +8000 = 50000 tokens 6 subtasks crash the ceiling delegated · only conclusions return main 2000 +100 +100 +100 ×6 = 2600 tokens the 8000-token reads stay in each sub-context Isolation is where the work happens, not how much. Inline, each subtask's full 8,000 tokens stack in the one window (50,000 total, hitting the ceiling); delegated, only ~100-token conclusions come home (2,600 total). Same work, ~10× less carried.
🗺️ How to read this diagram

Two stacks of the same six file-heavy subtasks. Compare the heights: the left is what lands in the main context when you do the work inline; the right is what lands when you delegate.

  • The dashed line at the top is the context-window ceiling — a hard limit on how many tokens the main agent can hold at once.
  • Left (inline): the small blue main 2000 block plus six fat amber/red bars of +8000 each — every subtask's full reading piles onto the one desk, totalling 50000 and slamming into the ceiling.
  • Right (delegated): the same main 2000 plus six thin green +100 slivers — each subagent did its 8,000-token reading in its own throwaway window and returned only a conclusion, totalling 2600.
  • The payoff is the height gap: red (crashing the ceiling) versus green (barely moving it) — the exact ~10× the lab computes.

In short: The subagent still does all 8,000 tokens of work; you just don't carry it. Only the conclusion comes home, so long tasks fit under the ceiling.

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Python · why isolation matters (runs)
isolation.pydef context_used(main_tokens, subtasks, tokens_per_subtask, inline):
    """inline=True: everything in the main context. inline=False: subagents isolate."""
    if inline:
        return main_tokens + subtasks * tokens_per_subtask       # all piled in
    return main_tokens + subtasks * 100                          # only the conclusions return

print("6 file-heavy subtasks, inline:  ", context_used(2000, 6, 8000, True), "tokens")
print("6 file-heavy subtasks, delegated:", context_used(2000, 6, 8000, False), "tokens")
print("delegation keeps the main thread ~10x leaner")
6 file-heavy subtasks, inline:   50000 tokens
6 file-heavy subtasks, delegated: 2600 tokens
delegation keeps the main thread ~10x leaner
▶ How this works

This tiny program puts a number on why delegation is worth it. It compares two ways of doing several file-heavy subtasks: cramming everything into one agent's memory (inline) versus sending each subtask to a subagent that returns only its conclusion (delegated).

  1. context_used(main_tokens, subtasks, tokens_per_subtask, inline) is a function (a reusable recipe) that estimates how many tokens — the chunks of text a model reads and is billed for — end up in the main agent's memory.
  2. When inline is True, it returns main_tokens + subtasks * tokens_per_subtask: every subtask's full material (8000 tokens each) piles into the one context.
  3. When inline is False (delegated), it returns main_tokens + subtasks * 100: each subagent reads its own big pile privately and sends back only a small ~100-token conclusion.
  4. The two print lines run the same 6 subtasks both ways so you can see the difference side by side.

What the output means: Inline costs 50000 tokens (2000 + 6×8000); delegated costs only 2600 (2000 + 6×100). Same work, roughly 10× less clutter in the main thread — which is exactly what lets long, complex work fit in the context window.

Try this: Change tokens_per_subtask from 8000 to 20000 (heavier files) and re-run. The inline number explodes while the delegated number barely moves — that gap is the case for subagents.

3 · Define a subagent intermediate

Here's the pleasant surprise: a subagent isn't a program you write, it's a job description you write. If you've been picturing threads, queues, and orchestration code, relax — defining a subagent is closer to writing a really good hiring brief than to writing software. You describe who this helper is, when to call them in, what they're allowed to touch, and how you want them to work. The system does the spinning-up.

Concretely, it's a plain Markdown file, and it has exactly two parts. The front-matter — the little block fenced by --- lines at the top — is the metadata: a name, a description of when to use it, the tools it's allowed to use, and optionally which model runs it. Everything below that block is the body, and the body is the subagent's system prompt — its standing instructions, its personality, its rules of engagement. That's the whole anatomy.

The description field earns special attention because it does more than document. It's the part the main agent reads to decide, on its own, when to hand a job to this subagent — so "Reviews a diff for bugs. Use proactively after code changes" isn't a comment, it's the trigger that makes delegation automatic instead of something you have to request every time. A vague description is a subagent that never gets called.

A subagent is a markdown file with front-matter: a name, a description of when to use it, its allowed tools, and optionally a model. The body is its system prompt.

The common mistake here is generosity with tools. It feels helpful to give a new subagent the full toolbox "just in case" — but a reviewer that can edit is a reviewer that might silently rewrite your code while you asked it to look. The safe instinct is the opposite: hand each subagent only the tools its one job actually requires (a reviewer needs Read and Grep, not Edit or git push). A narrow tool list isn't a limitation you're grudgingly accepting; it's a guardrail you're deliberately installing, and it's exactly the least-privilege idea the callout below drives home.

code-reviewer.md front-matter · metadata (fenced by ---) name: code-reviewer description: when to use it tools: [Read, Grep, Bash] model: sonnet body · the system prompt You are a focused code reviewer. Given a diff… A subagent is a job description, not code. One Markdown file, two parts: the front-matter block (name, description, tools, model) is the metadata the main agent reads to decide when to delegate; everything below it is the body — the subagent's standing system prompt.
🗺️ How to read this diagram

Read the file top to bottom, the way the tool parses it. The outer box is the whole .md file; the two inner panels are its two — and only two — parts.

  • The upper amber panel is the front-matter: the metadata block fenced by --- lines. name identifies the agent, description tells the main agent when to reach for it, tools is its allow-list, and model picks which model runs it.
  • The lower green panel is the body: everything after the second ---. This free text becomes the subagent's system prompt — its standing instructions and rules of engagement.
  • The payoff is the split itself: no threads or orchestration code appear anywhere — defining a subagent is writing a hiring brief, and the description line is the trigger that makes delegation automatic.

In short: Front-matter = who/when/what-tools/which-model; body = the instructions. That is the entire anatomy of a subagent.

config · a code-reviewer subagent
code-reviewer.md---
name: code-reviewer
description: >
  Reviews a diff for bugs and security issues. Use proactively after code
  changes, or when the user asks for a review.
tools: [Read, Grep, Bash]
model: sonnet
---
You are a focused code reviewer. Given a diff:
- Find correctness bugs, edge cases, security issues (injection, secrets, authz).
- Cite file:line. Group by severity. Be terse — no praise, no restating code.
- Return ONLY the findings; the main agent will act on them.
▶ How this works

This is not Python — it's a subagent definition file (Markdown). It's how you actually create a subagent: a small header (called front-matter) describes the agent, and the text below it is the agent's instructions (its system prompt). Save this as a .md file and the tool can spin it up as a helper.

  1. The block fenced by --- lines at the top is the front-matter — settings in key: value form. Everything after the second --- is the prompt.
  2. name is the agent's id; description tells the main agent when to reach for it ("use proactively after code changes") — this is what makes delegation automatic.
  3. tools: [Read, Grep, Bash] is the allow-list of what this agent may do. A reviewer only needs to read and search code, so it gets no Edit or git push — that's least privilege: give each agent only the tools its job needs.
  4. model: sonnet picks which model runs it. The instructions below tell it to find bugs, cite file:line, and return only the findings — a scoped job with a clean output.

Try this: Imagine adding Edit to the tools list. Now a review could silently change your code — the opposite of what you want. Keeping the list narrow is a safety feature, not a limitation.

Least privilege for subagents tooGive a subagent only the tools its job needs — a reviewer needs Read/Grep, not Edit or git push. Narrow tools = safer, cheaper, more focused (the K1 permission idea, per-agent).

4 · Advanced — delegate and parallelize advanced

So far isolation has been about keeping your desk clean. Now it pays a second dividend — and it's the one people actually get excited about: speed. Because each subagent works in its own sealed context, they don't step on each other. And things that don't step on each other can happen at the same time.

Picture reviewing six modules. Done inline, the one agent grinds through them like a single checkout lane at a supermarket — module one, then two, then three, everyone waiting in a line thirty seconds long apiece. Fan the work out to subagents and you've opened six lanes: the reviews run side by side, and your total wait collapses from "the sum of all of them" to "roughly the slowest one." That shift — from a sum to a max — is the whole prize, and it's why "review these 6 modules" is the textbook case for parallel delegation.

The reason this works cleanly with subagents (and would be a nightmare with one shared context) is precisely the isolation from §2. Six agents writing into one desk would trample each other's notes; six agents each with their own desk simply can't. Independence is what unlocks parallelism — you get the speedup for free because you already paid for isolation.

Because each subagent has its own context, you can run several in parallel — the biggest speedup for wide tasks ("review these 6 modules"). Model the wall-clock win.

Two honest caveats, so the win doesn't turn into a trap. First, "parallel" only helps for genuinely independent work — if step three needs step two's answer, no amount of lanes will let you skip the queue; dependent steps still run in order. Second, you rarely get infinite lanes: there's a workers limit, so ten tasks with four workers run in waves, and your speedup caps at "however many waves you need." The model in the lab below makes both effects visible — watch the parallel time stop improving once every task fits in one wave.

serial · one lane, times add 30s 30s 30s 30s 30s 30s = 180s parallel · 4 workers, 2 waves, time = waves × slowest w1 w2 w3 w4 w1 w2 wave 1 (4 tasks) → 30s wave 2 (2 tasks) → 30s = 60s · 3× faster Parallel turns a sum into a max-per-wave. Six 30s reviews run back-to-back take 180s serially; with 4 workers they run in 2 waves of ~30s each = 60s. The speedup caps once every task fits in a single wave.
🗺️ How to read this diagram

Both rows are the same six module reviews (30 seconds each). The top row is doing them one at a time; the lower block is fanning them out to parallel subagents. Read each left→right as elapsed time.

  • The top amber row is serial: six 30s boxes laid end to end, so the times simply add up to 180s — you wait through every review in turn.
  • The lower green block is parallel with 4 workers: four tasks run side by side in wave 1, the remaining two in wave 2. Each wave takes only as long as its slowest task (30s).
  • The payoff is the dashed split and the totals: 2 waves × 30s = 60s, a 3× win — purely from independent subagents not waiting on each other.

In short: Serial cost is the sum; parallel cost is waves × slowest-in-a-wave. Add enough workers to fit one wave and the total drops to a single task's time.

Python · serial vs parallel delegation (runs)
parallel.pydef wall_clock(tasks_secs, parallel, workers=4):
    if not parallel:
        return sum(tasks_secs)                       # one after another
    # parallel: time = ceil(n/workers) * slowest-in-a-wave (approx with equal tasks)
    import math
    waves = math.ceil(len(tasks_secs) / workers)
    return waves * max(tasks_secs)

tasks = [30, 30, 30, 30, 30, 30]                     # 6 module reviews, 30s each
print("serial:  ", wall_clock(tasks, parallel=False), "s")
print("parallel:", wall_clock(tasks, parallel=True, workers=4), "s")
serial:   180 s
parallel: 60 s
▶ How this works

Because each subagent works in its own context, you can run many at once instead of waiting for each to finish. This program estimates the real-world time saved (wall-clock time — the time you actually wait) for 6 reviews done serially versus in parallel.

  1. wall_clock(tasks_secs, parallel, workers=4) takes a list of how long each task takes, whether to run in parallel, and how many can run at the same time (workers).
  2. If parallel is false, it returns sum(tasks_secs) — every task runs one after another, so the times simply add up.
  3. If parallel, it splits the tasks into waves of 4 at a time: math.ceil(len(tasks) / workers) rounds up the number of waves (6 tasks ÷ 4 workers = 2 waves). Each wave takes as long as its slowest task, so time ≈ waves × max(tasks_secs).
  4. The test data is 6 module reviews at 30 seconds each; the two prints show both strategies.

What the output means: Serial takes 180 s (6×30). Parallel takes 60 s — 2 waves × 30 s each. Same work, 3× faster wall-clock, purely from running independent subagents side by side.

Try this: Raise workers to 6 so all six fit in one wave, and the parallel time drops to 30 s. More workers help only until every task fits in a single wave.

5 · Advanced — fan-out / verify / synthesize advanced

Parallel fan-out is fast — but speed without a safety check is how you ship confident nonsense. Six reviewers running at once will happily return six findings, and some of those findings will be wrong: a false positive, a misread, a "bug" that isn't. If you pipe all six straight into your answer, you've just parallelized your mistakes. This section adds the missing step that turns raw breadth into trustworthy breadth.

The pattern has a shape worth memorizing because you'll reuse it everywhere: fan out, verify, synthesize. Fan out to gather cheaply and in parallel; run a separate verifier subagent that tries to refute each finding; then synthesize only the survivors into one answer. It's the difference between a newsroom that prints every tip and one that runs each tip past a fact-checker before it goes to press — the fact-check is what makes the front page worth reading.

The single most important rule — and the one people quietly skip — is that the verifier must be a different agent from the one that gathered. Why? A gatherer checking its own work inherits its own blind spots; it already "believes" its finding and will rationalize it. A fresh subagent with its own context and no stake in the result approaches each claim as a skeptic. Independence isn't a nice-to-have here; it's the entire source of the check's value. Ask someone to grade their own exam and you know what you'll get.

A powerful pattern: split work, run a subagent per unit in parallel, have a verifier subagent adversarially check each finding, then synthesize survivors. Model the flow.

The coordination trap to watch: this pipeline has three stages and a real temptation to add more — a verifier for the verifier, a meta-synthesizer, a committee. Resist it. Each extra stage is another round trip, another hand-off, another chance for the signal to get garbled in translation, and the returns fall off fast. The elegance of fan-out/verify/synthesize is that three roles are usually enough: one to gather, one independent skeptic to filter, one to combine. The lab below models the flow with plain functions so you can see each stage do its job before any real API is involved.

The fan-out workflow

  1. Main splits the work into independent units (per module/file/option).
  2. A subagent per unit runs in parallel, each returning a structured finding.
  3. A verifier subagent adversarially checks each finding ("try to refute this").
  4. Main synthesizes the surviving findings into one answer.
Main splits work gatherer · auth gatherer · billing gatherer · search fan-out (parallel) Verifier refutes each fresh, independent agent Synthesize survivors only Fan out, verify, synthesize. Gather cheaply in parallel, then a separate verifier tries to refute each finding — weak claims (like the empty search or a "typo") die here — and only survivors reach the final answer.
🗺️ How to read this diagram

Follow the arrows left to right — they trace one question through the three-stage pipeline, from splitting the work to a single trusted answer.

  • Main (splits work) fans the job out into independent units; the three arrows branching to the green boxes are the fan-out — one gatherer subagent per module (auth, billing, search), all running in parallel.
  • The gatherers' arrows converge on the amber Verifier. Its label — fresh, independent agent — is the load-bearing rule: it did not gather, so it checks each finding as a skeptic and drops the ones it can refute.
  • The final arrow to Synthesize carries only the survivors; the parent combines those into one answer. The empty search module and any false positive never make it this far.

In short: Breadth from parallel gatherers, trust from an independent verifier, one clean answer from synthesis — three roles, no more.

Python · fan-out + adversarial verify (runs)
fanout.pydef review_module(name):                    # a 'subagent' returning findings
    fake = {"auth": ["hardcoded secret"], "billing": ["float money bug"], "search": []}
    return [{"module": name, "issue": i} for i in fake.get(name, [])]

def verify(finding):                        # a verifier subagent: is it real?
    return "secret" in finding["issue"] or "money" in finding["issue"]

modules = ["auth", "billing", "search"]
raw = [f for m in modules for f in review_module(m)]     # fan-out
confirmed = [f for f in raw if verify(f)]                # verify
print("raw findings:", len(raw), "| confirmed:", len(confirmed))
for f in confirmed: print("  -", f["module"], ":", f["issue"])
raw findings: 2 | confirmed: 2
  - auth : hardcoded secret
  - billing : float money bug
▶ How this works

This models a powerful team pattern: fan-out (send one subagent per unit of work), then verify (a second subagent double-checks each finding before you trust it). Here the 'subagents' are plain functions so the pattern is easy to see without any real API calls.

  1. review_module(name) plays the role of a review subagent: given a module name it returns a list of findings. The fake dictionary just supplies canned results so the demo runs offline (auth has a secret, billing a money bug, search is clean).
  2. verify(finding) is the verifier subagent: it returns True only if the finding looks genuinely serious (mentions a "secret" or "money"). This is the "try to refute it" step that filters out weak claims.
  3. raw = [f for m in modules for f in review_module(m)] is the fan-out: it runs the reviewer over every module and collects all findings into one list.
  4. confirmed = [f for f in raw if verify(f)] is the verify step: it keeps only the findings that survive the check. The loop then prints each survivor.

What the output means: raw findings: 2 | confirmed: 2 — two issues were found and both passed verification (the empty search module contributes nothing). You then see the auth secret and the billing money bug listed as the trusted, synthesized result.

Try this: Add "typo" to one module's fake findings and re-run. It shows up in raw but not in confirmed, because verify rejects it — that's the adversarial check earning its keep.

This is the managed Agents idea, by handA Bedrock Agent (AWS W5) or an orchestration framework runs loops like this for you. Understanding the manual fan-out/verify/synthesize means the managed versions hold no mystery — and you can build custom ones.

6 · Professional — when NOT to delegate professional

This is the section that separates people who use subagents from people who overuse them. Everything so far has sold you on delegation, and it works — which is exactly why the failure mode is enthusiasm. The engineer who just discovered subagents starts routing every task through one, and their agent gets slower and more expensive while feeling more sophisticated. Knowing when not to delegate is the more valuable half of the skill.

Remember the cost you keep forgetting: a subagent is a new colleague with a blank desk. Every delegation pays a fixed tax — you brief them from nothing, they spin up their own context, and you wait for the round trip. For a big, independent, exploration-heavy job that tax is trivial next to the isolation and parallelism you gain. For a one-line edit, the tax is the whole job — you spent more coordinating the hand-off than the work itself took. It's hiring a contractor, waiting for them to arrive, and briefing them, all to change a lightbulb you were standing next to.

There's also a disqualifier that overrides everything else, and it's the one beginners miss: if the task needs the parent's in-progress working state, you cannot delegate it — full stop, regardless of size. The subagent starts blank; it literally cannot see what the main agent is halfway through. Ask it to "finish the refactor I started" and it has no idea what you started. Shared context isn't a preference here; it's a hard wall.

Subagents add latency and cost (each spins up its own context). Delegate when work is genuinely independent or context-heavy; for a quick edit, the main agent is faster. Over-delegation is a real anti-pattern.

So the rule of thumb worth internalizing: delegate for breadth and isolation (independent work, heavy exploration, things you'd rather keep off the main desk); stay inline for small, sequential, or context-bound work. The little decision function below encodes exactly that priority order — quick-edit bails first, then the genuinely-worth-it cases, then the default "not worth the spin-up." Read it as the checklist you'll run in your head before every hand-off.

a job arrives quick edit? yes inline no independent AND context-heavy? yes delegate +parallelize no context-heavy alone? yes delegate no inline · not worth it The delegation checklist, top to bottom. First match wins: a quick edit bails to inline immediately; independent + context-heavy is the ideal delegate-and-parallelize case; context-heavy alone still delegates; everything else stays inline.
🗺️ How to read this diagram

Read it top to bottom as a series of gates — exactly the order the should_delegate function checks them. The first gate a job passes decides its fate; it never reaches the ones below.

  • The amber diamonds are the three questions in order: quick edit? → independent AND context-heavy? → context-heavy alone?
  • Red boxes are "stay inline": a quick edit exits at the top, and anything that falls through all three gates lands in the bottom "not worth the spin-up" box.
  • Green boxes are "delegate": independent + context-heavy earns the ideal delegate and parallelize; context-heavy on its own still delegates just to keep the main desk clean.
  • The payoff is that being independent alone has no green exit — it drops straight past every gate to inline. Independence without heavy context isn't enough to justify the round trip.

In short: Quick edit → inline. Context-heavy → delegate (and parallelize if also independent). Otherwise → inline. First match wins.

Python · should this be delegated? (runs)
delegate_decision.pydef should_delegate(independent, context_heavy, quick_edit):
    if quick_edit: return False, "trivial — main agent is faster"
    if independent and context_heavy: return True, "isolate + parallelize"
    if context_heavy: return True, "isolate to keep main context clean"
    return False, "not worth the spin-up overhead"

for case in [(True,True,False),(False,True,False),(False,False,True),(False,False,False)]:
    print(case, "->", should_delegate(*case))
(True, True, False) -> (True, 'isolate + parallelize')
(False, True, False) -> (True, 'isolate to keep main context clean')
(False, False, True) -> (False, 'trivial — main agent is faster')
(False, False, False) -> (False, 'not worth the spin-up overhead')
▶ How this works

Subagents aren't free — each one spins up its own context, which costs time and money. This function encodes the rule of thumb for when to delegate and when not to, so you don't over-use subagents (a real anti-pattern).

  1. should_delegate(independent, context_heavy, quick_edit) takes three yes/no facts about the work and returns a (decision, reason) pair.
  2. The checks run in order, and the first match wins. if quick_edit: return False — a trivial change is faster done inline, so bail out immediately.
  3. if independent and context_heavy → delegate to both isolate and parallelize (the ideal case). if context_heavy alone → still delegate, just to keep the main context clean.
  4. If none of those hold, the final return False says it's not worth the spin-up overhead. The loop feeds four example cases through and prints each verdict.

What the output means: Each line pairs the inputs with the decision, e.g. (True, True, False) -> (True, 'isolate + parallelize'). The two True results are context-heavy work; the two False results are a quick edit and a trivial task — exactly when the main agent should just do it.

Try this: Trace should_delegate(True, False, False) by hand (independent but light, not a quick edit): it falls past every if to the last line and returns False — being independent alone isn't enough to justify a subagent.

7 · Tech-lead — a subagent library + composition tech-lead

Up to now you've been defining subagents one at a time, for yourself. A tech lead thinks one level up: subagents as shared infrastructure. The shift is from "I'll write a reviewer when I need one" to "our team has a reviewer, a researcher, a test-writer, and everyone reaches for the same well-tuned ones." That curated set is a subagent library, and building it is the difference between everyone reinventing a mediocre reviewer and everyone inheriting a great one.

The mental model is a specialist team, not a Swiss-army knife. A hospital doesn't hire one person who does surgery, anesthesia, and billing; it hires narrow experts and an orchestrator who routes each patient to the right one. Your library works the same way: a few sharply-scoped agents (each with least-privilege tools and a right-sized model — a cheap model for search-heavy exploring, a stronger one for verification) plus a selector that picks by task kind. Locate something? Route to the explorer. Confirm a finding? The verifier. Review a diff? The reviewer.

Composition is where it gets powerful, and it ties back to the Skills you met in K4: a subagent can use a Skill to do its job your way. A reviewer subagent that loads your house code-review Skill enforces your standards; a research subagent that loads a "cite sources" Skill always shows its work. The lead who curates both the agents and the Skills they load hands the whole team specialized, house-standard helpers — and that stack is the backbone of the capstone workbench.

A lead builds a library of subagents (reviewer, researcher, test-writer) the team shares, and composes them with Skills (K4): a subagent uses a Skill to do its job your way. This is the backbone of the capstone workbench.

The failure mode at library scale is scope overlap. The moment two agents both plausibly handle "look at this code," your orchestrator's routing becomes a coin flip and the whole system gets unpredictable. A healthy library keeps scopes disjoint — each task kind maps to exactly one agent — and marks the read-only agents as parallel-safe so the fan-out patterns from §4–§5 can lean on them freely. Curating a library is less about adding agents and more about keeping their boundaries clean; an overlapping library is often worse than no library at all.

Orchestrator routes by task kind locate confirm review explorer · Read/Grep/Glob cheap model · parallel-safe verifier · Read/Grep strong model · independent reviewer · Read strong model · diff bugs each task kind → exactly one agent scopes disjoint A library is narrow specialists plus a router. The orchestrator maps each task kind to exactly one sharply-scoped subagent (least-privilege tools, right-sized model). Non-overlapping scopes keep routing deterministic; overlap turns it into a coin flip.
🗺️ How to read this diagram

Read left to right: one orchestrator on the left, the curated library of subagents on the right, and labelled arrows showing which task kind goes where.

  • The Orchestrator box is the router — it looks at the kind of task and picks a specialist, rather than doing the work itself.
  • Each labelled arrow (locate, confirm, review) lands on exactly one agent: the read-only explorer (cheap model, parallel-safe), the independent verifier, and the reviewer. The tool list under each name is its least-privilege scope.
  • The payoff note on the right — each task kind → exactly one agent, scopes disjoint — is what keeps routing deterministic. If two boxes both claimed "look at code," the arrow's target would become a coin flip.

In short: Curate a few narrow, least-privilege specialists and let the orchestrator match task kind to agent. Disjoint scopes are what make the library reliable.

Subagents + Skills composeA reviewer subagent that loads your house code-review Skill; a research subagent that loads a "cite sources" Skill. A lead who curates both gives the team specialized, house-standard agents — reusable across every project.

Exercise AK5.1 — Build a review crew

Context: A parallel review crew is the fan-out pattern made concrete: least-privilege reviewers running independently, then a consolidation step — and a wall-clock comparison that shows why parallel beats serial.

Your task: Write a code-reviewer subagent with least-privilege tools, run three of them in parallel over three files, consolidate the results, and justify and measure the choice.

Requirements:

  • The code-reviewer subagent has least-privilege (read-only) tools
  • The main agent runs three reviewers in parallel, one per file
  • The results are consolidated into a single review
  • Justify the delegation with should_delegate
  • Compare wall-clock parallel vs serial using the speedup model from the ladder

💡 Hint: Because the three reviews are independent and read-only, they're the textbook fan-out case — let should_delegate confirm it before you parallelize.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Why a subagent — context isolationBeginner

Context: A subagent runs a task in its own context and returns only a conclusion, so the exploration cost — files read, dead ends — stays isolated and never bloats the main thread.

Your task: Explain the benefit of a subagent with a concrete before/after of what lands in the main context.

Requirements:

  • Show a "without subagent" case where search noise (many file reads, dead ends) floods the main context
  • Show a "with subagent" case where the same search runs in a sub-context
  • The parent receives only the distilled one-line conclusion
  • Make clear the exploration cost is discarded with the sub-context
  • Explain why fan-out searches are a natural fit

💡 Hint: Contrast what the main thread absorbs in each case — the win is what it doesn't see.

Show solution

Isolation keeps the main thread clean — only the conclusion returns:

Without subagent:  main context absorbs 40 files of grep output,
                   3 dead-end reads, and the eventual answer.
                   -> context bloated with search noise.

With subagent:     "Find where auth tokens are validated" runs in a
                   sub-context; it reads 40 files there and returns
                   "validated in src/auth/verify.ts:reviewToken()".
                   -> main context gets the one-line conclusion only.

The exploration cost (files read, false starts) stays in the subagent's context and is discarded; the parent keeps only the distilled result. That's why fan-out searches are a natural fit — the noise never touches the main thread.

Exercise 2 · Define a subagentIntermediate

Context: Subagents are defined as files with frontmatter — a restricted tool set bounds what one can do, and a cheaper model fits search-heavy work — so a read-only explorer can't accidentally change anything.

Your task: Sketch a read-only "explorer" subagent defined as a file with frontmatter. Config — verify keys in the docs.

Requirements:

  • Defined as a Markdown file with frontmatter (e.g. under .claude/agents/)
  • Frontmatter has a name and a description saying when to use it
  • The tools list is read-only (Read/Grep/Glob; no Edit/Write/Bash)
  • A cheaper model is chosen to fit search-heavy work
  • The body is the subagent's system prompt / instructions
  • Verify the exact frontmatter keys and the agents directory in the docs

💡 Hint: The frontmatter is the scope — the tool list is what actually guarantees it can't modify a file.

Show solution

A subagent is a Markdown file with frontmatter + a system prompt:

# .claude/agents/explorer.md
---
name: explorer
description: Read-only code explorer. Use to locate where something is
  implemented across the codebase without editing anything.
tools: Read, Grep, Glob         # no Edit/Write/Bash -> can't change anything
model: claude-haiku-4-5         # cheap model for search-heavy work
---

You locate code. Search broadly, read excerpts, and return the file paths
and symbols that answer the question. Do not modify any file.

The frontmatter scopes the subagent: a restricted tool set (read-only) bounds what it can do, and a cheaper model fits search-heavy work. The body is its instructions. Verify the exact frontmatter keys and the agents directory in the Claude Code docs.

Exercise 3 · Delegate and parallelizeAdvanced

Context: Fanning out independent subagents turns a sum into a max: work that ran serially finishes in about the time of the slowest branch when parallelized — but only genuinely independent work qualifies.

Your task: Model the speedup of running four independent searches in parallel subagents versus serially.

Requirements:

  • Represent each task by a duration
  • Serial total is the sum of the durations
  • Parallel total is the max of the durations (wait for the slowest)
  • Compute and report the speedup
  • State the caveat: only genuinely independent work can be parallelized; dependent steps still run in order

💡 Hint: Serial is sum, parallel is max — the ratio is your speedup.

Show solution

Independent work fans out; total time approaches the slowest branch, not the sum:

tasks = [3, 5, 2, 4]      # seconds each, independent searches

serial   = sum(tasks)                 # 14s — one after another
parallel = max(tasks)                 # 5s  — all at once, wait for slowest
print(f"serial: {serial}s  parallel: {parallel}s  speedup: {serial/parallel:.1f}x")
# serial: 14s  parallel: 5s  speedup: 2.8x

Fanning out independent subagents turns a sum into a max: four searches that took 14s serially finish in ~5s in parallel, and each one's noise stays isolated. Only parallelize genuinely independent work — dependent steps must still run in order.

Exercise 4 · Fan-out / verify / synthesizeExpert

Context: The strong multi-agent pattern is fan out to gather, run a fresh verifier, then synthesize — the verifier must be a separate context, because a gatherer checking its own work inherits its own blind spots.

Your task: Model the fan-out / verify / synthesize pipeline and explain why the verifier must be independent.

Requirements:

  • Fan-out stage: parallel gatherer subagents each return a finding
  • Verify stage: a fresh subagent that did not gather checks the findings against the source
  • Synthesize stage: the parent combines verified findings into one answer
  • Explain why the verifier must be a separate context (avoid inherited blind spots)
  • Each stage's exploration cost stays isolated in its own context

💡 Hint: The load-bearing rule is separation: whoever verifies must not be whoever gathered.

Show solution

Three roles, each in its own context, so verification is unbiased:

def pipeline(question):
    # 1. FAN-OUT: parallel gatherers, each returns a finding
    findings = [subagent("explorer", q) for q in split(question)]
    # 2. VERIFY: a FRESH subagent that did not gather, checks the findings
    verdict  = subagent("verifier",
                        f"Independently confirm these against the code: {findings}")
    # 3. SYNTHESIZE: the parent combines verified findings into one answer
    return synthesize(findings, verdict)

The verifier must be a separate context that did not produce the findings — a gatherer checking its own work inherits its own blind spots. Fan-out gets breadth cheaply, an independent verifier catches the errors, and the parent synthesizes; each stage's exploration cost stays isolated.

Exercise 5 · When NOT to delegateProfessional

Context: A subagent starts with a blank context and costs a round trip, so delegation pays off for independent, exploration-heavy work but hurts for small, sequential, context-bound tasks.

Your task: Write the rule for when delegating to a subagent hurts more than it helps.

Requirements:

  • The rule takes: independent, exploration-heavy, and needs-shared-context
  • Needs the parent's in-progress state → do it inline (the subagent can't see it)
  • Independent and exploration-heavy → delegate (isolate noise, maybe parallelize)
  • A single read or quick sequential edit → cheaper inline
  • Demonstrate the outcomes with example cases

💡 Hint: The disqualifier is shared context — if the task needs the parent's working state, no delegation, regardless of size.

Show solution

Delegate for breadth/isolation; stay direct for small, sequential, context-bound work:

def should_delegate(independent, exploration_heavy, needs_shared_context):
    if needs_shared_context:
        return "NO — subagent can't see the parent's working state; do it inline"
    if independent and exploration_heavy:
        return "YES — isolate the noise and/or run in parallel"
    return "NO — a single file read or sequential edit is cheaper inline"

print(should_delegate(True, True, False))   # YES (fan-out search)
print(should_delegate(False, False, True))  # NO (needs shared context)
print(should_delegate(False, False, False)) # NO (trivial inline task)

A subagent starts with a blank context, so anything depending on the parent's in-progress state must stay inline. Delegation pays off when the task is independent and exploration-heavy (isolate the noise, maybe parallelize); a single read or a quick sequential edit is cheaper done directly.

Exercise 6 · A subagent library + composition (tech-lead)Industry scenario

Context: A healthy subagent library is a few sharply-scoped agents (least-privilege tools, right-sized model) plus an orchestrator that routes by task kind — scopes must not overlap and read-only agents can be marked parallel-safe.

Your task: As tech lead, design a small library of composable subagents and the rule the orchestrator uses to pick among them.

Requirements:

  • A registry of a few narrow subagents, each with its tool set, model, and purpose
  • Each is least-privilege (read-only where possible) and right-sized on model
  • A selector routes by task kind (e.g. locate → explorer, confirm → verifier, review → reviewer)
  • They compose via the fan-out / verify / synthesize pattern
  • Library health depends on non-overlapping scopes and marking read-only agents parallel-safe
  • Verify frontmatter/registry details in the docs

💡 Hint: Keep each agent narrow and let the orchestrator match task kind to agent — overlap between scopes is what breaks routing.

Show solution

A few sharply-scoped subagents plus an orchestrator that routes by task kind:

REGISTRY = {
    "explorer":  {"tools": ["Read","Grep","Glob"], "model": "claude-haiku-4-5",
                  "use": "locate code / gather facts, read-only"},
    "verifier":  {"tools": ["Read","Grep"],        "model": "claude-opus-4-8",
                  "use": "independently confirm findings"},
    "reviewer":  {"tools": ["Read"],               "model": "claude-opus-4-8",
                  "use": "review a diff for bugs"},
}

def pick(task_kind):
    return {"locate": "explorer", "confirm": "verifier",
            "review": "reviewer"}.get(task_kind, "explorer")

print(pick("locate"))   # explorer (cheap, read-only, parallel-safe)
print(pick("confirm"))  # verifier (independent, higher model)

Keep each subagent narrow (least-privilege tools, right-sized model) and compose them via the fan-out/verify/synthesize pattern. The orchestrator routes by task kind; the library stays healthy when scopes don't overlap and read-only agents are marked parallel-safe. Verify frontmatter/registry details in the docs.

✓ Checkpoint — you can move on when you can…

  • Explain context isolation and quantify its benefit.
  • Define a least-privilege subagent.
  • Delegate, parallelize, and run fan-out/verify/synthesize.
  • Decide when NOT to delegate; curate a subagent library.

Knowledge check check yourself

✓ Knowledge check

Why does delegating a context-heavy subtask to a subagent keep the main session's context lean?

Show answer
A subagent has its own isolated context, so it reads all the files and does the fiddly work in its own thread and returns only a short conclusion (~100 tokens). The main session never absorbs the intermediate tokens, so six file-heavy subtasks stay around 2600 tokens instead of ~50000 if done inline.
✓ Knowledge check

In the fan-out/verify/synthesize pattern, what does the verifier subagent do, and why add that extra step?

Show answer
The verifier adversarially re-checks each finding by trying to refute it, so weak or false-positive claims get filtered out before synthesis. Without it, unverified findings from parallel workers would surface directly, so the verify step is what makes only robust issues survive into the final result.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in