2.5 Supervision
Routing (2.4) sends a task to the right agent. But some tasks aren’t one job — they’re several independent jobs that a single agent would do one after another, slowly. That’s failure mode #5 — sub-tasks run serially — with a nasty twin: if one of those sub-tasks throws, the whole run dies with it.
Think “research this company”: check pricing, summarize reviews, look up inventory. None depends on the others. Doing them in sequence wastes time; letting one failure sink the other two wastes everything.
Enter the supervisor
Section titled “Enter the supervisor”A supervisor is an agent whose job is coordinating other agents:
- Plan — break the task into independent sub-tasks.
- Fan out — launch a sub-agent for each, in parallel, each isolated from the others (isolation we already have from 2.2).
- Fan in — collect their results as typed contracts so they can be merged.
- Synthesize — combine them into one answer, degrading gracefully if some failed.
Watch it fan out (and survive a failure)
Section titled “Watch it fan out (and survive a failure)”Press dispatch. Three sub-agents run at the same time — and one of them fails on purpose. Watch what the supervisor does with the two that succeed.
Run serially, these three would finish one after another. In parallel they finish in about the time of the slowest one — and because each runs in isolation, one crashing doesn't take the others down. The supervisor's job is to fan out, collect whatever comes back, and make something of it.
Two payoffs. Speed: in parallel, the batch finishes in about the time of the slowest sub-agent, not the sum of all three. Resilience: sub-agent B failing doesn’t crash the run — the supervisor merges the results it did get and returns a degraded-but-useful answer. Partial failure is not total failure.
What that looks like in code
Section titled “What that looks like in code”async function supervise(task) { const subTasks = plan(task); // 1. break into independent pieces
// 2 + 3. run them all at once; settle so one failure doesn't reject the batch. const results = await Promise.allSettled( subTasks.map((st) => runSubAgent(st)), // each isolated, in parallel );
const ok = results.filter((r) => r.status === 'fulfilled').map((r) => r.value); const failed = results.length - ok.length;
return synthesize(ok, { degraded: failed > 0 }); // 4. merge what we got}Promise.allSettled is the whole trick: it waits for every sub-agent and never rejects
just because one did. You keep the successes, note the failures, and synthesize from
what you have.
In production
Section titled “In production”- Orchestration graphs (LangGraph, Mastra) and map-reduce-over-agents patterns give you supervisors, parallel fan-out, and typed result contracts.
You built the fan-out/fan-in-with-partial-failure core yourself.