Build

Swarm

Hierarchical routing where one agent's tools are other agents. Triage routes to specialists; specialists may hand off back. The pattern is a Loop over Agent calls — no SwarmAgent class needed.

A user message arrives that could be billing OR technical OR a refund. You don't want one giant prompt that knows everything — that's where agents get vague and hallucinate. You want a triage agent that figures out which specialist should handle this, then hands off. The specialist focuses on its domain. That's Swarm — and it's just a Loop over Agents with a route function.

What Swarm is

Swarm = a hierarchical agent pattern where one orchestrator routes each turn to a specialist agent. The route function is sync, pure, and returns the id of the next agent (or undefined to halt). Each handoff is one iteration of a Loop:

Loop {
  current_agent = route(input)
  if current_agent is undefined → exit
  result = current_agent.run(input)
  input = result + handoff annotation
}

In agentfootprint there is no SwarmAgent class. Swarm is a recipe — swarm({ agents, route, maxHandoffs }) constructs a runnable that wraps a Loop. The 4-composition substrate (Sequence/Parallel/Conditional/Loop) is enough; we don't need a 5th primitive.

The recipe

const router = swarm({  agents: [    { id: 'triage', runner: triage },    { id: 'billing', runner: billing },    { id: 'tech', runner: tech },  ],  // Route function — pure sync over the current message. First turn goes  // to triage, then to billing or tech based on content, then halts.  route: (input) => {    const msg = input.message.toLowerCase();    if (msg.includes('[billing]')) return undefined; // billing done → halt    if (msg.includes('[tech]')) return undefined; // tech done → halt    if (msg.includes('refund') || msg.includes('bill')) return 'billing';    if (msg.includes('status') || msg.includes('error')) return 'tech';    return 'triage'; // first turn  },  maxHandoffs: 5,});

The route function is the orchestrator's brain. It inspects the current input (often the previous handoff's result) and decides which agent to dispatch next. Returning undefined halts the loop — the most recent specialist's output becomes the final answer.

maxHandoffs caps the loop so a buggy route function can't infinite-loop. Production: 3–5 handoffs covers most triage flows.

Why route is sync, not LLM-driven

You CAN make route call an LLM internally — it's just a function. But the standard pattern is a deterministic route based on the previous specialist's output (often via convention markers like [BILLING] or [ESCALATE] in the message). That keeps the routing layer cheap, fast, and debuggable. The intelligence lives in the specialists; the routing is mechanical dispatch.

If you need LLM-driven routing, use Conditional with an LLM-based predicate — different shape, same outcome.

Multi-tenant identity

A swarm runner's input is { message: string } — and that single message is the only thing that flows from one specialist to the next (each agent's string output becomes the next iteration's { message }). swarm() does not carry a MemoryIdentity through the handoff chain for you.

If specialists need per-tenant memory, give each agent its identity directly. Agent.run({ message, identity }) accepts an optional MemoryIdentity on its input; bind the tenant scope when you build/run each specialist (e.g. via a closure that injects it), so memory reads/writes stay isolated even though the swarm only relays the message.

When to use Swarm vs Conditional vs Sequence

SituationUse
One classifier picks ONE specialist for the whole turnConditional
Specialists may hand back to triage or to each otherSwarm (this guide)
Fixed pipeline: research → write → edit (no branching)Sequence
Multi-perspective fan-out → mergeParallel

Anti-patterns

  • Don't put expensive logic in the route function. It runs every iteration. Keep it as cheap as text inspection.
  • Don't share state between specialists via globals. Use MemoryIdentity + memory layer for cross-agent state.
  • Don't omit maxHandoffs. A buggy route returning the same agent every time runs forever.

Next steps

  • Patterns overview — Reflexion, ToT, Debate, Map-Reduce all run the same way (recipes over the substrate)
  • Conditional — single-shot routing alternative

On this page