Supervisor
The hierarchical multi-agent pattern. Anthropic Multi-Agent Research System (2026 internal report) + Anthropic Agent Teams (Feb 2026). The 2026 production consensus: hierarchical Supervisor is the multi-agent pattern that earns its cost in production. Anthropic reports +90.2% on their MA research benchmark vs single-agent baseline.
prompt ───► manager ───► delegate(...) ─┬─► worker A ─┐
│ ├─► worker B ─┤ parallel
│ └─► worker C ─┤
▼ │
[worker outputs] ◄─────────────┘
│
├─► synthesize ──► output
│
└─► forward_message(worker) ──► verbatim outputPattern
The supervisor itself runs an architecture (default ReAct). Its tool host is augmented with two extra tools:
delegate(worker, instructions). The named workerAgentruns to completion with the given instructions and returns its final answer as the tool result.forward_message(worker). Same as above but returns the worker’s output verbatim with no synthesis pass at the supervisor.
Because ReAct’s tool dispatch is already parallel
(anyio.create_task_group over all tool calls in a turn), the
supervisor gets parallel delegation for free. Emit two delegate
calls in one turn and both workers run concurrently.
Usage
from loomflow import Agent
from loomflow.team import Team
researcher = Agent("Find sources, summarise key claims.",
model="claude-opus-4-7", tools=[search])
writer = Agent("Draft the article.",
model="claude-opus-4-7")
reviewer = Agent("Critique the draft for clarity and factual accuracy.",
model="gpt-4o")
team = Team.supervisor(
workers={"researcher": researcher, "writer": writer, "reviewer": reviewer},
instructions=(
"Manage the article pipeline. Delegate research first, then "
"writing, then a review pass. Synthesize the final answer."
),
model="claude-opus-4-7",
)
result = await team.run("Write an article about agent harnesses.")Or via the explicit Architecture form:
from loomflow import Agent
from loomflow.architecture import Supervisor
agent = Agent(
"Manage the article pipeline.",
model="claude-opus-4-7",
architecture=Supervisor(workers={
"researcher": researcher,
"writer": writer,
"reviewer": reviewer,
}),
)The two forms are interchangeable, Team.supervisor(...) is exactly
Agent(architecture=Supervisor(...)) under the hood. Use the Team
facade for single-level teams; use the explicit form for recursive
composition.
Dynamic spawning — allow_spawn=True
By default the roster is fixed at construction. With
allow_spawn=True the coordinator model can create new specialist
workers mid-run via an injected spawn_worker(role, instructions)
tool:
team = Team.supervisor(
workers={"researcher": researcher, "writer": writer},
instructions="Coordinate the report. Spawn extra specialists if needed.",
model="claude-opus-4-7",
allow_spawn=True,
max_spawned=3, # hard cap per run (default 5)
spawn_template=worker_template # model + tools spawned workers inherit
)How spawn_worker behaves:
rolemust be a valid Python identifier not already in the roster;instructionsbecome the new worker’s system prompt — that is all the worker knows about its job.- The spawned worker is immediately delegable: the
delegate/forward_messagetool definitions are rebuilt after every spawn, so the next model turn sees the new role in the enum. - The model may not choose the worker’s tools or model. Those
are inherited from
spawn_template(anAgentwhose model + tool host are cloned). With no template, spawned workers use the coordinator’s own model and get no tools — the v1 surface is deliberately minimal. - Guardrails return error strings to the model, not exceptions:
exceeding
max_spawned, invalid role names, duplicate roles, and empty instructions all come back as correctable tool results. - Each spawn emits a
supervisor.worker_spawnedarchitecture event (role, worker_id, running count) on the stream.
Ephemerality + tenant pinning
Spawned workers are ephemeral by design. They live in a per-run
overlay merged with the fixed roster for delegate lookup and die when
the run ends — never persisted into the agent-level worker registry,
so nothing spawned leaks across runs or across users. (Mid-run they
are mirrored into the shared registry so
send_message(to=<worker_id>) works; the run’s teardown pops those
entries.)
Tenant isolation holds from birth: a spawned worker is pinned to the
spawning run’s user_id at creation, and delegation to it goes
through the same acquire_worker_session discipline as persistent
workers — a different user’s run attempting to reach it is rejected.
Spawning is off by default (allow_spawn=False — zero behavior
change), and each spawned worker is a full agent whose spend rolls up
into the parent run’s usage and budget. Keep max_spawned small and
prefer a well-designed fixed roster; the built-in prompt already
tells the model to spawn only when no current worker fits.
Replay correctness
Each delegated worker run is journaled by (session_id, "delegate_<turn>_<worker>")
in the runtime. With a SqliteRuntime or PostgresRuntime, a crash
mid-supervisor-turn replays cleanly: completed worker runs return
cached results; only the un-completed work re-executes.
When Supervisor pays off
- Specialist workers with distinct skills (research vs write vs review).
- Tasks decompose into chunks the supervisor can hand off without hand-holding.
- Cost is acceptable at 2–4× ReAct. Typical for important research, multi-source content generation, complex coding tasks.
Workers are real Agent instances. Each has its own model,
memory, tools, and architecture. Workers can themselves use Reflexion,
ActorCritic, or even another Supervisor. See
Recursive composition.