Recursive Delegation in Swarm, CrewAI, and LangGraph
How OpenAI Swarm, CrewAI, and LangGraph implement recursive delegation. Each framework handles context passing, result aggregation, and agent spawning differently.
From Pattern to Implementation
The Recursive Delegation pattern describes the concept: agents spawn child agents with scoped contexts, creating a tree of delegated work. The concept is simple; the implementation choices are where teams get stuck.
OpenAI Swarm, CrewAI, and LangGraph all support recursive delegation, but they make fundamentally different decisions about context isolation, and that decision shapes everything downstream.
OpenAI Swarm
Swarm is OpenAI’s experimental framework for multi-agent orchestration. Agent handoffs are its core abstraction, which makes recursive delegation easy to set up and hard to scale.
The core model: agents are defined with instructions, functions, and handoff targets. When Agent A hands off to Agent B, the full conversation history comes along. This makes result aggregation trivial; everything is in the shared history. It also means context grows with every handoff unless you explicitly trim it.
Swarm does not enforce context isolation. Each agent sees the full conversation history unless you filter it yourself. This gives you control, but requires discipline to implement Isolate properly.
from swarm import Swarm, Agent
leaf_agent = Agent(
name="LeafWorker",
instructions="Process the assigned subtask with your focused context."
)
def spawn_child(context_subset):
return leaf_agent
parent_agent = Agent(
name="Parent",
instructions="""You are a delegator. When given a large task:
1. Break it into subtasks
2. For each subtask, call spawn_child with the relevant context subset
3. Synthesize the results""",
functions=[spawn_child]
)
CrewAI
CrewAI makes the opposite bet from Swarm: isolation by default. Agents have roles, goals, and backstories. Tasks define expected outputs. Crews orchestrate the whole thing into a process that can be sequential, hierarchical, or custom.
When a worker agent receives a task, it gets only the task description, relevant tools, and whatever context the manager explicitly passes. No shared history leaking between agents. This is the right default for most multi-agent systems, because context bleed between agents is one of the fastest ways to degrade quality.
The manager agent receives task outputs and synthesizes them. It never sees the full context each worker operated in, only the results.
from crewai import Agent, Task, Crew, Process
worker = Agent(
role="Specialist",
goal="Process your assigned subtask with focused attention",
backstory="You are an expert in your specific domain."
)
manager = Agent(
role="Manager",
goal="Break down complex tasks and delegate to specialists",
backstory="You coordinate work across a team of specialists.",
allow_delegation=True
)
task = Task(
description="Analyze this codebase for security issues",
expected_output="A security audit report",
agent=manager
)
crew = Crew(
agents=[manager, worker],
tasks=[task],
process=Process.hierarchical,
manager_agent=manager
)
result = crew.kickoff()
The abstraction is higher-level than Swarm. You trade flexibility for structured process management and built-in isolation. For most teams, that’s a good trade.
LangGraph
LangGraph sits between Swarm and CrewAI on the abstraction spectrum. You model your agent system as a graph: state flows between nodes, edges define transitions (including conditional routing), and subgraphs enable recursive patterns.
The key differentiator: you define an explicit state schema. Every node receives typed state and returns typed updates. This gives you precise control over what context each agent sees, at the cost of more boilerplate than either Swarm or CrewAI.
from langgraph.graph import StateGraph, END
from typing import TypedDict, List, Annotated
import operator
class AgentState(TypedDict):
task: str
context: str
results: Annotated[List[str], operator.add]
depth: int
def parent_node(state: AgentState):
if state["depth"] >= 2:
return {"results": [f"Completed: {state['task']}"]}
# break_into_subtasks and extract_relevant_context are your decomposition logic
subtasks = break_into_subtasks(state["task"])
return {
"task": subtasks[0],
"context": extract_relevant_context(state["context"], subtasks[0]),
"depth": state["depth"] + 1
}
def child_node(state: AgentState):
# process_subtask: your LLM call or tool invocation goes here
result = llm.run(state["task"], context=state["context"])
return {"results": [result]}
def aggregate_node(state: AgentState):
final_result = synthesize(state["results"])
return {"results": [final_result]}
workflow = StateGraph(AgentState)
workflow.add_node("parent", parent_node)
workflow.add_node("child", child_node)
workflow.add_node("aggregate", aggregate_node)
workflow.set_entry_point("parent")
workflow.add_conditional_edges(
"parent",
lambda s: "delegate" if s["depth"] < 2 else "complete",
{"delegate": "child", "complete": "aggregate"}
)
workflow.add_edge("child", "aggregate")
workflow.add_edge("aggregate", END)
app = workflow.compile()
More code to write, but when your workflow has branching logic or needs to maintain custom state across steps, the explicitness pays for itself in debuggability.
Comparison
| Framework | Abstraction Level | Context Control | Delegation Style | Best For |
|---|---|---|---|---|
| Swarm | Low | Manual via history | Handoff-based | Simple delegation, rapid prototyping |
| CrewAI | High | Automatic per-task | Hierarchical process | Structured workflows, role-based teams |
| LangGraph | Medium | Explicit state | Graph-based | Complex routing, custom state management |
The Context Management Decision
This is the decision that matters most, and it’s the one these frameworks make differently.
Swarm shares everything by default. Context grows with every handoff, which is simple to reason about when you have 2-3 agents but becomes a liability beyond that. CrewAI takes the opposite approach: isolation by default, where each agent sees only what you explicitly provide. Safer, but you need to think carefully about what context the manager passes to workers, because under-sharing is as bad as over-sharing. LangGraph forces the question by requiring an explicit state schema; nothing flows between nodes unless you put it there. More work upfront, but the most predictable behavior at runtime.
Recommendations
Start with CrewAI unless you have a specific reason not to. Isolation by default is the right starting point for most multi-agent systems, and the role-based abstraction maps naturally to how people think about dividing work.
Swarm is useful for prototypes where you want to validate the delegation structure before committing to a framework, but don’t ship it without adding context filtering. Shared history is a footgun for anything beyond 3-4 agents because context bloats faster than you’d expect.
If CrewAI’s abstractions don’t fit your workflow, reach for LangGraph. Complex conditional routing, cycles, or custom state shapes that don’t map to a role/task model are where the explicit graph earns its boilerplate. The extra code is worth it when you need to debug exactly what context each agent received and why.