Chapter 3 of 8
OpenAI Swarm: Multi-Agent Orchestration
Swarm's lightweight multi-agent coordination, handoff mechanism for seamless agent switching, and comparison with CrewAI and LangGraph.
OpenAI Swarm: Multi-Agent Orchestration
OpenAI released Swarm in October 2024 as an educational framework demonstrating a minimalist approach to multi-agent orchestration. Unlike heavyweight platforms that build graph-based state machines or role hierarchies, Swarm reduces multi-agent coordination to two primitives: agents and handoffs. The entire framework lives in a single Python file under 1,000 lines, and the repository at github.com/openai/swarm has accumulated significant attention from practitioners looking for a simple coordination layer to wrap around OpenAI's function-calling API.
For loop engineers, Swarm matters because it exposes the handoff pattern explicitly — the same pattern that Claude Code uses internally when a sub-agent delegates work and returns control. Understanding Swarm's handoff mechanism clarifies how any multi-agent loop manages context transfer between specialized agents.
Core Architecture: Agents and Handoffs
Swarm's design philosophy is that multi-agent coordination should be as simple as function calling. Every agent is a Python class with two attributes: a set of instructions (the system prompt) and a list of functions it can call. When an agent decides it cannot handle a request, it returns a special result object that triggers a handoff to another agent.
┌──────────────────────────────────────────────────────────────┐
│ Swarm Execution Loop │
│ │
│ User Message │
│ │ │
│ ▼ │
│ ┌───────────┐ function call ┌───────────┐ function call │
│ │ Agent A │──────────────►│ Agent B │─────────────► │
│ │ (Triage) │ │ (Research) │ │
│ └───────────┘ └───────────┘ │
│ ▲ ▼ │
│ │ ┌───────────┐
│ └──────────────────────────────────────────│ Agent C │
│ handoff return │ (Writer) │
│ └───────────┘
│ │
│ ▼
│ Final Response
│ (return to user)
└──────────────────────────────────────────────────────────────┘
The critical insight is that Swarm does not use a DAG, state graph, or message bus. The handoff is a simple return value — the agent function returns the name of the next agent, and the Swarm loop routes the conversation to that agent. Context accumulates in a shared message list that all agents in the chain can read.
Installation and First Agent
Swarm requires Python 3.10+ and an OpenAI API key. Install it directly from the repository:
pip install git+https://github.com/openai/swarm.git
# Set your API key
export OPENAI_API_KEY="sk-..."
A minimal Swarm agent takes a system prompt and a function list. The agent loop is managed by the Swarm client, which handles message passing and handoff routing automatically:
from swarm import Swarm, Agent
client = Swarm()
def transfer_to_analysis():
"""Transfer the user to the analysis agent."""
return analysis_agent
def transfer_to_writing():
"""Transfer the user to the writing agent."""
return writing_agent
triage_agent = Agent(
name="Triage Agent",
instructions=(
"You are a triage agent. Determine whether the user needs "
"data analysis or content writing. Call the appropriate "
"transfer function."
),
functions=[transfer_to_analysis, transfer_to_writing],
)
analysis_agent = Agent(
name="Analysis Agent",
instructions=(
"You are a data analysis specialist. Answer questions about "
"data, statistics, and quantitative analysis. If the user "
"needs something written, transfer to the writing agent."
),
functions=[transfer_to_writing],
)
writing_agent = Agent(
name="Writing Agent",
instructions=(
"You are a content writer. Produce clear, well-structured "
"prose based on user requests."
),
functions=[transfer_to_analysis],
)
response = client.run(
agent=triage_agent,
messages=[{"role": "user", "content": "Analyze the Q3 sales data"}],
)
print(response.messages[-1]["content"])
Run this script and Swarm handles the full lifecycle: the triage agent receives the message, identifies "analyze" as a signal for the analysis agent, calls transfer_to_analysis, and the loop re-routes with the original message plus the triage agent's reasoning visible in the shared context.
The Handoff Mechanism in Detail
The handoff is Swarm's central contribution. Every function can return either a normal string result (which becomes part of the conversation) or an Agent object (which triggers a handoff). This dual return type is what makes Swarm so lightweight — there is no separate protocol or message format for agent-to-agent transfer.
How Context Transfers
When a handoff occurs, Swarm copies the entire message history into the new agent's conversation. This means the receiving agent sees everything that happened before:
def transfer_with_context():
"""Handoff that preserves all prior conversation."""
return writing_agent
# If the triage agent called transfer_with_context(), the writing agent
# receives the full message list:
# [user: "analyze Q3 data", assistant: (triage reasoning),
# function_call: transfer_with_context, function_result: ...]
This shared context is both Swarm's strength and its weakness. For short conversations, full context transfer ensures the receiving agent has all relevant information. For long-running loops with many handoffs, the context window fills up quickly — the same token accumulation problem documented in loop engineering's context overflow failure mode.
Handoff with Variables
Functions can do work and initiate a handoff simultaneously. The function's return value goes into the message history as a tool message, while returning an Agent object triggers the handoff:
def analyze_and_handoff(claim: str):
analysis = f"Analysis of '{claim}': This claim requires verification."
return writing_agent # Handoff; analysis visible in context
# Pattern for combining work + handoff via separate functions:
def do_analysis(claim: str) -> str:
return f"Detailed analysis of: {claim}"
def then_handoff():
return writing_agent
writer_agent = Agent(
name="Writer",
instructions="Use the analysis results from the conversation to write.",
functions=[do_analysis, then_handoff],
)
Building a Loop Engineering Pipeline with Swarm
Swarm maps naturally onto the multi-agent loop pattern described in the loop engineering curriculum. Here is a complete pipeline that implements a code review loop with three specialized agents:
from swarm import Swarm, Agent
client = Swarm()
def transfer_to_reviewer():
return reviewer_agent
def transfer_to_fixer():
return fixer_agent
def transfer_to_verifier():
return verifier_agent
# Agent 1: Reviewer — identifies issues in code
reviewer_agent = Agent(
name="Reviewer",
model="gpt-4o",
instructions=(
"You are a code reviewer. Given code, identify bugs, "
"style issues, and potential improvements. List each "
"issue with file, line, and severity. If all issues "
"are resolved, return 'APPROVED'."
),
functions=[transfer_to_fixer, transfer_to_verifier],
)
# Agent 2: Fixer — applies patches to code
fixer_agent = Agent(
name="Fixer",
model="gpt-4o",
instructions=(
"You are a code fixer. Apply fixes for the issues "
"identified by the reviewer. Show the exact code "
"changes. When done, transfer to the verifier."
),
functions=[transfer_to_reviewer, transfer_to_verifier],
)
# Agent 3: Verifier — confirms fixes
verifier_agent = Agent(
name="Verifier",
model="gpt-4o",
instructions=(
"You are a verification agent. Check that all issues "
"from the reviewer have been fixed. If any remain, "
"transfer back to the fixer. If all clear, transfer "
"back to the reviewer for final approval."
),
functions=[transfer_to_reviewer, transfer_to_fixer],
)
response = client.run(
agent=reviewer_agent,
messages=[
{"role": "user", "content": "Review this function:\n```python\ndef add(a, b):\n return a - b\n```"}
],
max_turns=10, # Prevent infinite loops
)
for msg in response.messages:
print(f"[{msg['role']}] {msg.get('content', '')[:200]}")
This loop runs automatically. The reviewer spots the bug, the fixer corrects it, the verifier confirms, and the reviewer gives final approval — all within a single client.run() call. The max_turns parameter is critical for loop engineering: without it, a bug in the handoff logic could create an infinite cycle of agents bouncing between each other.
Adding a Human-in-the-Loop Gate
Production loop engineering requires human checkpoints. Swarm has no built-in approval gates, but you can split the run into phases:
# Phase 1: Agent-driven review
review = client.run(agent=reviewer_agent,
messages=[{"role": "user", "content": code_to_review}], max_turns=1)
# Phase 2: Human approval
approval = input("Proceed with fixes? (y/n): ")
if approval.lower() != "y":
exit("Loop cancelled by operator.")
# Phase 3: Agent-driven fix + verify (carries forward full context)
fix_and_verify = client.run(agent=fixer_agent, messages=review.messages, max_turns=5)
This mirrors Claude Code's interactive mode where the agent pauses at permission boundaries.
Guardrails: Preventing Infinite Agent Loops
Infinite handoff loops are the most common failure mode in Swarm-based systems. Two agents can ping-pong control back and forth indefinitely if their handoff conditions overlap. Two mechanisms address this:
max_turns parameter — caps total LLM calls across all agents:
response = client.run(agent=start_agent, messages=user_messages, max_turns=20)
if len(response.messages) > 18:
print("WARNING: Approaching turn limit — possible loop detected")
Prompt-level guard — since the framework does not detect context truncation, production agents should self-limit:
guarded_agent = Agent(
name="Guarded Agent",
instructions=(
"You MUST count how many times you have been called. "
"If this is your third invocation, STOP and return "
"'MAX_ITERATIONS_REACHED' instead of transferring."
),
functions=[transfer_to_other],
)
Comparison: Swarm vs CrewAI vs LangGraph
Swarm, CrewAI, and LangGraph represent three fundamentally different philosophies for multi-agent orchestration. Each has trade-offs that matter for loop engineering workflows.
| Dimension | Swarm | CrewAI | LangGraph |
|---|---|---|---|
| Core abstraction | Agent + handoff return | Role + task + crew | State graph + nodes + edges |
| Lines of code | ~1,000 (single file) | ~15,000 | ~20,000+ |
| Dependencies | OpenAI SDK only | LangChain, multiple providers | LangChain, Pydantic |
| State management | Shared message list | Task outputs, memory objects | Typed state objects, reducers |
| Control flow | Implicit (return values) | Explicit (task sequences) | Explicit (graph edges, conditional routing) |
| Observability | Minimal (print messages) | Moderate (crew logs) | Strong (LangSmith integration) |
| Human-in-the-loop | Manual (phase splitting) | Built-in (human_input flag) | Built-in (interrupt nodes) |
| Streaming | Not supported | Partial | Full support |
| Production readiness | Educational/experimental | Production (v0.86+) | Production |
| Non-OpenAI models | Not supported natively | Supported (Anthropic, local) | Supported (all providers) |
When to Use Each Framework
What is your orchestration complexity?
│
├─ Simple handoff chains (2-5 agents, linear flow)
│ ├─ OpenAI-only, need fast prototype ──► Swarm
│ └─ Need cross-provider support ────────► CrewAI
│
├─ Conditional routing + loops
│ ├─ Need state persistence ─────────────► LangGraph
│ └─ Prefer role-based metaphor ─────────► CrewAI
│
└─ Production pipeline with monitoring
├─ Need LangSmith tracing ─────────────► LangGraph
├─ Need simple deployment ─────────────► CrewAI + LangServe
└─ Need minimal infrastructure ─────────► Swarm (with custom guards)
Swarm vs CrewAI: Role-Based Differences
CrewAI wraps each agent in a "Role" metaphor with goals, backstories, and a task queue. Swarm has none of this — an agent is just a system prompt and functions. CrewAI's Process.hierarchical mode adds a manager agent that dynamically delegates tasks, similar to Swarm's handoff but with the manager maintaining a global view of task status. Swarm's approach is simpler but provides no task tracking — once a handoff occurs, the previous agent has no way to check if its request was fulfilled.
Swarm vs LangGraph: State Machine Differences
LangGraph builds multi-agent systems as explicit state machines with typed state objects, conditional edges, and checkpoint-based persistence. LangGraph's advantage for loop engineering is its built-in interrupt nodes — points where the graph pauses and waits for human input. Swarm has no equivalent; you must manually split the run and manage message threading yourself. LangGraph also supports checkpointing, persisting full state to a database at each node for crash recovery and audit. Swarm stores nothing — when the Python process exits, all state is lost.
Swarm in Production: Patterns and Limitations
Despite its experimental status, Swarm has been adopted in production systems that need a thin coordination layer. The most common pattern is using Swarm as the orchestration skeleton while adding custom middleware for monitoring, logging, and guardrails.
The Cross-Provider Challenge
Swarm's tight coupling to the OpenAI SDK is its biggest limitation for loop engineering. Claude Code, Cursor, and most production loop systems use multiple providers — Claude for deep reasoning, GPT-4o for function calling, local models for classification. Swarm does not support this natively. To use Claude with Swarm, you would need to either fork the framework or implement a custom client that translates Swarm's message format to Anthropic's API.
This is where LangGraph and CrewAI have a clear advantage: both support provider-agnostic agent definitions through LangChain's model abstraction. If your pipeline needs to switch between Claude, GPT-4o, and local Llama models based on task complexity, Swarm is not the right tool without significant modification.
Key Takeaways
Swarm's contribution to multi-agent orchestration is not its code — it is its conceptual clarity. The framework demonstrates that handoff-based coordination is sufficient for many multi-agent patterns, and that explicit state graphs are not always necessary.
Swarm Decision Framework:
│
├─ Do you need >3 agents with conditional routing?
│ └─ YES → Use LangGraph or CrewAI
│
├─ Do you need non-OpenAI models?
│ └─ YES → Use LangGraph or CrewAI
│
├─ Do you need persistence or replay?
│ └─ YES → Use LangGraph (checkpointing)
│
├─ Do you need a working prototype in <50 lines?
│ └─ YES → Swarm is ideal
│
└─ Do you want to understand how handoffs work
before adopting a heavier framework?
└─ YES → Study Swarm first, then graduate to
CrewAI or LangGraph
For loop engineers, Swarm is best understood as a reference implementation. Study its handoff mechanism to understand how Claude Code's sub-agent delegation works under the hood, then apply the same pattern with the appropriate production framework. The handoff primitive is the atomic unit of multi-agent loop engineering, and Swarm makes it visible in the simplest possible form.
Related Resources
- Multi-agent orchestration patterns: See the Sub-Agent Orchestration Patterns guide for handoff patterns across frameworks
- Role-based alternatives: Compare with the MetaGPT multi-agent approach for SOP-driven pipelines
- Loop fundamentals: Build on the Multi-Agent Loop tutorial for the underlying theory