Skip to Content

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 output

Pattern

The supervisor itself runs an architecture (default ReAct). Its tool host is augmented with two extra tools:

  • delegate(worker, instructions). The named worker Agent runs 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:

  • role must be a valid Python identifier not already in the roster; instructions become the new worker’s system prompt — that is all the worker knows about its job.
  • The spawned worker is immediately delegable: the delegate / forward_message tool 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 (an Agent whose 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_spawned architecture 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.

Last updated on