The Swarm Pattern
The coordinator pattern puts one agent in charge. It receives the task, decomposes it, dispatches sub-tasks to workers, collects results, and synthesizes the final answer. The coordinator is the brain; the workers are the hands. This works well when the task has clear decomposition boundaries and one perspective is enough to judge the results.
But some problems do not decompose cleanly from the top down. Designing a product involves balancing engineering constraints against market demand against cost projections — and no single perspective can hold all three simultaneously. Evaluating a business strategy requires the finance view, the legal view, the competitive view, and the customer view, each challenging the others. Here, instead of correct decomposition, the work requires multiple experts to argue, build on each other's ideas, and converge on something none of them could have produced alone.
The swarm pattern addresses this. Instead of a central coordinator directing workers, a group of peer agents collaborate directly — sharing findings, critiquing proposals, handing off control, and iterating until they converge on a result. There is no boss. There is a dispatcher that routes the initial request into the swarm and relays the final answer back to the user, but the dispatcher does not orchestrate the workflow. The agents themselves decide who should work next, what needs more attention, and when the job is done.
This is the most flexible and the most dangerous multi-agent pattern. It can produce exceptionally creative, well-rounded results — the kind that come from genuine debate between different perspectives. It can also spiral into unproductive loops, burn through enormous token budgets, and fail to converge. Understanding when to reach for it and how to keep it under control is what separates a useful swarm from an expensive accident.
Swarm vs Coordinator #
The distinction matters because the two patterns look similar at first glance — both involve multiple specialized agents working on a task. The difference is in who decides what happens next.
In the coordinator pattern, there is a single agent making all routing decisions. Workers do not talk to each other. They receive a sub-task from the coordinator, produce a result, and send it back. The coordinator sees all results and decides the next step. Communication flows through the hub.
In the swarm pattern, agents talk to each other. Any agent can hand off control to any other agent. The communication topology is all-to-all, not hub-and-spoke. An agent working on the current step reads the shared context, does its work, and then decides which agent should go next — or declares the task complete.
Coordinator Pattern Swarm Pattern
┌────────────┐ ┌─────────┐
│ Coordinate │ │Dispatch │
└──────┬─────┘ └────┬────┘
┌────┼────┐ │
│ │ │ ┌──────────────┐
▼ ▼ ▼ │ The Swarm │
┌──┐ ┌──┐ ┌──┐ │ │
│W1│ │W2│ │W3│ │ A ◄──► B │
└──┘ └──┘ └──┘ │ ▲ ▲ │
│ │ │ │
Hub-and-spoke. │ ▼ ▼ │
Workers never │ C ◄──► D │
talk to each other. │ │
└──────────────┘
All-to-all.
Any agent can hand
off to any other.
| Dimension | Coordinator | Swarm |
|---|---|---|
| Control | Centralized — one agent decides | Decentralized — each agent decides the next step |
| Communication | Hub-and-spoke | All-to-all (peer-to-peer) |
| Topology | Star — workers connect only to the coordinator | Mesh — every agent can reach every other agent |
| Plan ownership | Coordinator holds the plan | Plan is emergent from agent interactions |
| Strengths | Predictable routing, clear accountability | Creative synthesis, multiple perspectives |
| Weaknesses | Bottleneck at the coordinator, single perspective | Convergence risk, high cost, hard to debug |
The coordinator pattern is a command structure. The swarm pattern is a collaboration.
How Handoffs Work #
The core mechanism of the swarm is the handoff. When an agent finishes its contribution to the current step, it does not return a result to a coordinator. Instead, it selects the next agent to take over and passes control — along with the shared context — directly to that agent.
A handoff contains two things: the target agent (who should go next) and the context (what has been done so far, including the current agent's contribution). The receiving agent picks up where the previous one left off, reads the accumulated context, does its own work, and hands off again.
class SwarmAgent:
"""An agent that can hand off control to peer agents."""
def __init__(self, name, system_prompt, tools, peers, model):
self.name = name
self.system_prompt = system_prompt
self.tools = tools
self.peers = peers # list of other SwarmAgents
self.model = model
def handoff_tools(self):
"""Generate tool definitions for handing off to each peer."""
return [
{
"name": f"handoff_to_{peer.name}",
"description": f"Hand off to {peer.name}: {peer.description}. "
f"Use when {peer.name}'s expertise is needed "
f"for the next step.",
"parameters": {
"type": "object",
"properties": {
"context": {
"type": "string",
"description": "Summary of what you have done "
"and what the next agent should "
"focus on.",
}
},
"required": ["context"],
},
}
for peer in self.peers
]
def available_tools(self):
"""All tools: the agent's own tools plus handoff tools."""
return self.tools + self.handoff_tools()
def run(self, messages):
"""Run this agent's ReAct loop until it hands off or finishes."""
all_tools = self.available_tools()
while True:
response = call_model(
model=self.model,
system=self.system_prompt,
messages=messages,
tools=all_tools,
)
if not response.has_tool_calls:
# Agent produced a final answer — no handoff
return {"type": "final", "output": response.text}
# Check for handoff first
for call in response.tool_calls:
if call.name.startswith("handoff_to_"):
target_name = call.name[len("handoff_to_"):]
return {
"type": "handoff",
"target": target_name,
"context": call.arguments["context"],
"messages": messages + [response.to_message()],
}
# No handoff — execute tools normally
messages.append(response.to_message())
for call in response.tool_calls:
result = execute_tool(call, self.tools)
messages.append(tool_result_message(call.id, result))
Each agent has two kinds of tools: its domain tools (search, write, calculate — whatever its specialty requires) and its handoff tools (one per peer agent). When the agent decides that another agent's expertise is needed, it calls the handoff tool instead of a domain tool. The swarm runner catches this and transfers control.
The Swarm Runner #
The runner is the loop that manages the swarm. It is not a coordinator — it does not decide which agent runs next. It just executes whatever the current agent tells it to do. If the agent calls a domain tool, the runner executes the tool and feeds the result back. If the agent hands off, the runner switches to the target agent and continues.
class SwarmRunner:
"""Executes the swarm loop until an agent produces a final answer."""
def __init__(self, agents, max_handoffs=15):
self.agents = {a.name: a for a in agents}
self.max_handoffs = max_handoffs
def run(self, initial_agent_name, task):
current_agent = self.agents[initial_agent_name]
messages = [{"role": "user", "content": task}]
handoff_count = 0
while handoff_count < self.max_handoffs:
result = current_agent.run(messages)
if result["type"] == "final":
return result["output"]
# Handoff to the next agent
handoff_count += 1
sender = current_agent.name
target_name = result["target"]
current_agent = self.agents[target_name]
# Carry forward the conversation with handoff context
messages = result["messages"]
messages.append({
"role": "user",
"content": f"[Handoff from {sender}] "
f"{result['context']}",
})
# Budget exhausted — force the current agent to conclude
messages.append({
"role": "user",
"content": "The handoff budget is exhausted. Produce a final "
"answer based on the work done so far.",
})
final = current_agent.run(messages)
return final.get("output", "Swarm failed to converge.")
The runner is deliberately simple. It tracks the current agent, passes messages through, counts handoffs, and enforces a budget. All the intelligence — which agent should go next, what the context should contain, when to stop — lives inside the agents themselves. The runner is just plumbing.
The Dispatcher #
The dispatcher is the entry point. It receives the user's request and decides which agent should start the swarm. Unlike a coordinator, the dispatcher does not manage the workflow after the initial routing. It hands off once and gets out of the way.
class Dispatcher:
"""Routes the initial request to the right starting agent."""
def __init__(self, agents, model):
self.agents = agents
self.model = model
def route(self, task):
"""Determine which agent should handle this task first."""
agent_descriptions = "\n".join(
f"- {a.name}: {a.description}" for a in self.agents
)
response = call_model(
model=self.model,
system=f"You are a dispatcher. Given a user request, decide "
f"which agent should handle it first.\n\n"
f"Available agents:\n{agent_descriptions}\n\n"
f"Respond with just the agent name.",
messages=[{"role": "user", "content": task}],
)
return response.text.strip()
In some designs, the dispatcher is not even a model call — it is a simple rule or classifier. If the request mentions code, start with the engineering agent. If it mentions budget, start with the finance agent. The dispatcher's routing does not need to be perfect because the swarm can self-correct: if the wrong agent starts, it will recognize the mismatch and hand off to the right one.
Shared Context and Convergence #
The hardest problem in swarm design is not the handoff mechanism — that is just plumbing. The hard problem is convergence: how do the agents arrive at a final answer that reflects all their contributions?
The Blackboard Pattern #
One common approach is a blackboard — a shared data structure that all agents can read and write. Instead of passing messages linearly, agents read the current state of the blackboard, add their contribution, and hand off. The blackboard accumulates the collective work.
class Blackboard:
"""Shared workspace for swarm agents."""
def __init__(self):
self.entries = []
self.status = "in_progress"
def write(self, agent_name, content, entry_type="contribution"):
self.entries.append({
"agent": agent_name,
"type": entry_type,
"content": content,
"timestamp": len(self.entries),
})
def read_all(self):
"""Return the full blackboard as a formatted string."""
lines = []
for entry in self.entries:
lines.append(
f"[{entry['agent']}] ({entry['type']}): {entry['content']}"
)
return "\n\n".join(lines)
def read_latest(self, n=5):
"""Return the most recent entries."""
recent = self.entries[-n:]
lines = []
for entry in recent:
lines.append(
f"[{entry['agent']}] ({entry['type']}): {entry['content']}"
)
return "\n\n".join(lines)
def mark_complete(self, final_answer):
self.status = "complete"
self.write("system", final_answer, entry_type="final_answer")
Each agent's system prompt tells it to read the blackboard, assess what has been done, add its contribution, and either hand off or declare completion. The blackboard gives every agent visibility into the full history of the collaboration — who said what, what was critiqued, what was revised, and what the current state of the solution is.
ENGINEER_PROMPT = """You are a software engineer in a product design swarm.
Read the blackboard to understand the current state of the discussion.
Your job is to assess technical feasibility, estimate implementation
complexity, and flag engineering constraints.
After reading the blackboard:
1. Add your engineering analysis as a contribution.
2. If you see claims from other agents that are technically incorrect,
add a critique.
3. If all perspectives have been covered and the team has converged,
call finish_task with the final consolidated proposal.
4. Otherwise, hand off to the agent whose perspective is most needed next.
"""
Convergence Strategies #
Without explicit convergence mechanisms, a swarm can debate forever. Each agent finds something to critique, revise, or extend, and the loop never terminates. Four strategies keep swarms from spiraling:
Handoff budgets. The simplest and most reliable. Set a maximum number of handoffs (the max_handoffs parameter in the runner). When the budget is exhausted, the current agent is forced to produce a final answer regardless of whether the swarm has converged. This is a blunt instrument — it guarantees termination but not quality.
Consensus detection. Each agent can read the blackboard and check whether the recent entries agree. If the last two or three agents have made contributions without raising objections, the swarm has implicitly converged. An agent that detects consensus calls a finish_task tool to end the loop.
def check_consensus(blackboard, window=3):
"""Check if the last N contributions contain no critiques."""
recent = blackboard.entries[-window:]
critique_count = sum(
1 for e in recent if e["type"] == "critique"
)
return critique_count == 0 and len(recent) >= window
Voting. After a fixed number of rounds, each agent votes on whether the current proposal is acceptable. If a threshold (e.g., all agents, or a supermajority) agrees, the swarm terminates. This is more deliberate than consensus detection but requires an extra round of model calls.
Designated synthesizer. One agent in the swarm is designated as the synthesizer — it does not contribute domain expertise but watches the collaboration and decides when enough perspectives have been heard. When it judges the discussion mature, it writes the final integrated answer. This is a lighter version of the coordinator pattern, but the synthesizer only controls termination, not routing.
A Concrete Example: Product Design Review #
To make this tangible, here is a swarm that reviews a product proposal. Three agents — an engineer, a market analyst, and a financial modeler — collaborate to evaluate a product idea and produce a consolidated assessment.
engineer = SwarmAgent(
name="engineer",
description="Assesses technical feasibility, estimates complexity, "
"identifies engineering risks and dependencies.",
system_prompt=ENGINEER_PROMPT,
tools=[search_docs, estimate_effort, check_dependencies],
peers=[], # Will be set after all agents are created
model="large-model",
)
analyst = SwarmAgent(
name="analyst",
description="Evaluates market fit, competitive landscape, target "
"audience, and demand signals.",
system_prompt="""You are a market analyst in a product design swarm.
Read the blackboard to understand the current state of the discussion.
Your job is to evaluate market demand, competitive positioning, and
target audience fit.
After reading the blackboard:
1. Add your market analysis as a contribution.
2. If engineering estimates seem disconnected from market timing,
add a critique.
3. If the team has converged, call finish_task.
4. Otherwise, hand off to whoever is most needed next.""",
tools=[market_search, competitor_analysis, trend_lookup],
peers=[],
model="large-model",
)
finance = SwarmAgent(
name="finance",
description="Models costs, revenue projections, margin analysis, "
"and financial viability.",
system_prompt="""You are a financial modeler in a product design swarm.
Read the blackboard to understand the current state of the discussion.
Your job is to model the financial viability: costs, revenue potential,
margins, and break-even timeline.
After reading the blackboard:
1. Add your financial analysis as a contribution.
2. If engineering cost estimates or market size claims seem unrealistic,
add a critique with your numbers.
3. If the team has converged, call finish_task.
4. Otherwise, hand off to whoever is most needed next.""",
tools=[cost_model, revenue_forecast, margin_calculator],
peers=[],
model="large-model",
)
# Wire up peer references
engineer.peers = [analyst, finance]
analyst.peers = [engineer, finance]
finance.peers = [engineer, analyst]
# Create and run the swarm
runner = SwarmRunner(
agents=[engineer, analyst, finance],
max_handoffs=12,
)
result = runner.run(
initial_agent_name="analyst",
task="Evaluate this product proposal: A browser extension that uses "
"AI to automatically summarize long email threads and highlight "
"action items. Target market is enterprise knowledge workers. "
"Freemium pricing model with a $10/month pro tier."
)
Here is what a typical execution looks like:
- The analyst reads the proposal, assesses market demand (enterprise email tools, existing competitors, willingness to pay), writes a contribution to the blackboard, and hands off to engineer.
- The engineer reads the analyst's market assessment and the original proposal, evaluates technical feasibility (browser extension APIs, AI summarization, email API access), estimates development effort, flags the email access permission model as a risk, and hands off to finance.
- The finance agent reads both contributions, models development cost against the freemium pricing, projects conversion rates, and flags that the $10/month price point may not cover API costs at scale. It critiques the analyst's market size assumption and hands back to analyst.
- The analyst reads the critique, adjusts the market sizing, responds to the pricing concern with data about comparable tools, and hands off to engineer.
- The engineer proposes a technical approach that reduces API costs (local summarization with a small model), which changes the financial picture. Hands off to finance.
- The finance agent re-runs projections with the lower cost structure, finds the margins workable, and — seeing that the last three entries are contributions without critiques — calls
finish_taskwith the consolidated assessment.
Six handoffs, three perspectives, two rounds of revision. No coordinator decided this sequence — it emerged from the agents' own judgments about what the discussion needed next.
When to Use a Swarm #
The swarm pattern is the most expensive and complex multi-agent architecture. It earns that cost in specific situations:
The problem needs multiple competing perspectives. Product design, strategy evaluation, risk assessment, policy analysis — any task where the quality of the answer depends on multiple experts challenging each other's assumptions. A coordinator can collect multiple perspectives, but it synthesizes them itself. A swarm lets the experts argue directly, which produces deeper integration.
There is no natural decomposition hierarchy. A coordinator needs to decompose the task top-down. If the task does not decompose cleanly — if every sub-task interacts with every other sub-task — the coordinator becomes a bottleneck. A swarm lets agents interact freely without routing everything through a central point.
Iterative refinement across perspectives. The review-and-critique pattern uses a generator and a critic. A swarm generalizes this to multiple generators and multiple critics, each with different expertise. The financial modeler critiques the engineering estimate; the engineer critiques the market sizing; the analyst critiques the pricing model. Multi-dimensional critique produces more robust results.
Exploration over exploitation. When you want creative, divergent solutions rather than efficient execution of a known procedure, swarms are a better fit. The lack of central control allows unexpected connections — the engineer's cost-cutting idea changes the financial model, which changes the market positioning. These emergent solutions would not arise in a top-down decomposition.
When Not to Use a Swarm #
The task has a clear decomposition. If you can write down the sub-tasks at design time, use a sequential or parallel pipeline. If you cannot write them down but a single brain can figure them out at runtime, use a coordinator. Swarms are for tasks where no single brain is sufficient.
Latency is critical. Swarms are inherently multi-round. Each handoff is at least one model call, and a productive swarm typically involves several handoffs. That is a several round-trip model calls in sequence, plus whatever tool calls each agent makes internally. For real-time applications, this latency is prohibitive.
Cost is tightly constrained. Each agent in each turn consumes tokens for reading the shared context, reasoning, and producing output. A three-agent swarm with ten handoffs means ten full model invocations, each reading an increasingly long context. Token costs compound with every handoff. For budget-sensitive applications, a coordinator that makes precise, minimal calls is cheaper.
The agents do not genuinely have different expertise. If you create three swarm agents with overlapping capabilities, they will agree with each other quickly and add no value over a single agent. A swarm's value comes from genuine specialization — agents that see different things, use different tools, and apply different judgment criteria. Without that, you are paying for consensus without substance.
Failure Modes and Defenses #
Swarms fail in characteristic ways. Understanding the failure modes is essential for building ones that work.
Circular Handoffs #
Agent A hands off to Agent B, who hands off to Agent C, who hands back to Agent A. Each agent reads the context, adds a minor contribution, and passes it along. The swarm cycles without making progress. This happens when agents are not sure who should take the lead and default to deferring.
Defense: Track handoff history and inject it into the context. If an agent sees that control has already visited it twice, the system prompt should instruct it to either make a substantive contribution or call finish_task instead of handing off again.
class HandoffTracker:
"""Tracks handoff history to detect and prevent cycles."""
def __init__(self):
self.history = []
def record(self, from_agent, to_agent):
self.history.append((from_agent, to_agent))
def visit_count(self, agent_name):
return sum(1 for _, to in self.history if to == agent_name)
def is_cycling(self, window=6):
"""Detect if the last N handoffs form a repeating pattern."""
if len(self.history) < window:
return False
recent = [to for _, to in self.history[-window:]]
half = window // 2
return recent[:half] == recent[half:]
def summary(self):
return " → ".join(to for _, to in self.history)
Groupthink #
All agents agree too quickly. The first agent makes a claim, the second endorses it, the third endorses it, and the swarm converges on a potentially flawed conclusion because no agent pushed back. This is the opposite of cycling — the swarm terminates but produces a low-quality result because the collaborative critique never happened.
Defense: Assign at least one agent an adversarial role. Its system prompt explicitly instructs it to challenge assumptions, look for flaws, and raise objections even when the proposal looks solid. This agent is not trying to be difficult — it is playing devil's advocate to force the swarm to defend its reasoning.
ADVERSARIAL_PROMPT = """You are a critical reviewer in a product design swarm.
Your role is to stress-test the team's proposals. For every claim,
ask: What evidence supports this? What could go wrong? What are we
not considering?
Rules:
1. NEVER simply agree with the current proposal. Always identify at
least one risk, gap, or questionable assumption.
2. Your critiques must be specific and constructive — not vague
objections.
3. If you genuinely cannot find a flaw after thorough analysis,
acknowledge the proposal's strength but flag the area of highest
uncertainty.
4. You may call finish_task ONLY after you have raised at least two
substantive critiques in the current discussion.
"""
Context Explosion #
Every handoff adds to the message history. By the tenth handoff, the context contains the full reasoning of ten agent turns, each reading and responding to all previous turns. Token consumption grows quadratically — each successive turn reads everything before it. The context window fills up, and agents start losing track of earlier contributions.
Defense: Compress the context at regular intervals. After every N handoffs, a summarization step condenses the blackboard into a compact state document. Agents read the summary plus only the last few raw contributions, keeping the context window manageable.
class CompressingRunner(SwarmRunner):
"""Swarm runner that periodically compresses context."""
def __init__(self, *args, compress_every=4, summarizer_model="small-model",
**kwargs):
super().__init__(*args, **kwargs)
self.compress_every = compress_every
self.summarizer_model = summarizer_model
def run(self, initial_agent_name, task):
current_agent = self.agents[initial_agent_name]
messages = [{"role": "user", "content": task}]
handoff_count = 0
while handoff_count < self.max_handoffs:
result = current_agent.run(messages)
if result["type"] == "final":
return result["output"]
handoff_count += 1
target_name = result["target"]
current_agent = self.agents[target_name]
messages = result["messages"]
messages.append({
"role": "user",
"content": f"[Handoff from previous agent] "
f"{result['context']}",
})
# Compress context periodically
if handoff_count % self.compress_every == 0:
messages = self._compress(messages)
return self._force_finish(current_agent, messages)
def _compress(self, messages):
"""Summarize the conversation so far into a compact state."""
full_text = "\n".join(
m.get("content", "") for m in messages if m.get("content")
)
summary = call_model(
model=self.summarizer_model,
system="Summarize this multi-agent discussion into a concise "
"state document. Preserve: key decisions, open questions, "
"critiques, and the current proposal state. Discard "
"redundant discussion and superseded proposals.",
messages=[{"role": "user", "content": full_text}],
)
return [
{"role": "user", "content": f"[Discussion summary]\n"
f"{summary.text}"},
]
Deadlock #
No agent wants to go next. Every agent's reasoning concludes that another agent should handle the current state, but none of them generates a handoff. This is rare but happens when the task reaches a state that does not clearly match any agent's expertise — a gap between specializations.
Defense: The runner detects stalls (an agent produces a final answer that is actually a punt or a question rather than a substantive conclusion) and forces a handoff to a default agent or the agent with the broadest scope.
Design Guidelines #
Building a productive swarm requires more upfront design work than any other multi-agent pattern. Here are the guidelines that matter most:
Keep the swarm small. Three to five agents is the sweet spot. More agents mean more handoffs, more context, and more opportunities for cycling. Every agent you add should bring a genuinely different perspective — not a slightly different angle on the same domain.
Make each agent's expertise non-overlapping. If two agents can both do the same analysis, they will produce redundant contributions and the swarm wastes handoffs on agreement instead of debate. Define clear boundaries: the engineer does not assess market fit; the analyst does not estimate engineering costs.
Give agents explicit handoff criteria. The system prompt should tell each agent when to hand off and to whom. "If you identify a cost concern, hand off to finance. If you identify a technical risk, hand off to engineer." Without this, agents default to handing off to whichever agent's name they notice first.
Always set a handoff budget. Never run a swarm without a max_handoffs cap. The budget should be generous enough for a productive discussion (two to three rounds through all agents) but tight enough to prevent runaway costs. For a three-agent swarm, twelve to fifteen handoffs is usually sufficient.
Log the handoff chain. The sequence of handoffs is the trace of the swarm's reasoning. Log it, visualize it, and use it for debugging. When a swarm produces a bad result, the handoff chain tells you where the discussion went wrong — which agent failed to catch a problem, which handoff skipped a necessary perspective, where the cycle started.
Test with adversarial inputs. Swarms are most likely to fail on edge cases: tasks that are too simple (the swarm overthinks), tasks that are ambiguous (agents cannot agree on scope), and tasks that fall between specializations (no agent claims ownership). Build a test suite that includes these cases.
Trade-Offs #
| Dimension | Swarm | Coordinator | Static Pipeline |
|---|---|---|---|
| Flexibility | Highest — agents self-organize | High — dynamic but centrally planned | Lowest — fixed topology |
| Cost | Highest — many model calls, growing context | Medium — coordinator + workers | Lowest — predictable |
| Latency | Highest — sequential handoffs | Medium — parallel dispatch possible | Lowest — known step count |
| Output quality | Highest for multi-perspective tasks | Good for decomposable tasks | Good for structured tasks |
| Debugging | Hardest — non-deterministic trace | Hard — variable trace | Easiest — fixed trace |
| Convergence risk | Real — may loop or deadlock | Low — coordinator controls termination | None — deterministic |
| Implementation effort | Highest — handoffs, context, convergence | Medium — coordinator prompt, workers | Lowest — sequential calls |
The pattern progression in multi-agent systems follows a clear arc: static pipelines are cheapest and most predictable, coordinators trade predictability for flexibility, and swarms trade cost for depth. Each step up the ladder adds capability and risk. You should climb only as far as the problem demands.
Conclusion #
The swarm pattern is a decentralized multi-agent architecture where peer agents collaborate through direct handoffs instead of centralized coordination. It is the most powerful and the most expensive pattern in the multi-agent toolkit.
Key takeaways:
- A swarm replaces centralized coordination with peer-to-peer handoffs — each agent decides who should go next based on its own assessment of the task state
- The dispatcher routes the initial request into the swarm but does not orchestrate the workflow; the swarm runner manages the loop but does not make routing decisions
- Handoffs carry shared context (often via a blackboard) so each agent can read the full collaboration history and build on previous contributions
- Convergence is the central challenge — use handoff budgets, consensus detection, voting, or a designated synthesizer to prevent infinite loops
- Swarms excel at tasks requiring multiple competing perspectives: product design, strategy review, risk assessment, and policy analysis
- Keep swarms small (three to five agents) with non-overlapping expertise and explicit handoff criteria in each agent's system prompt
- Defend against circular handoffs (track history), groupthink (add an adversarial agent), context explosion (periodic compression), and deadlock (stall detection)
- Prefer simpler patterns first — use a swarm only when the task genuinely requires multiple experts to challenge each other's assumptions, not just multiple specialists working independently