Multi-agent in LangGraph: supervisor and swarm
Implement the two most useful topologies with LangGraph's supervisor prebuilts and shared state.
Why this matters
LangGraph multi-agent patterns are the de-facto industry implementation in 2026. The two patterns you must master are supervisor (one boss, many specialists) and swarm (peer-to-peer hand-offs). Both are first-class in the langgraph-supervisor and langgraph-swarm libraries.
Learning objectives
- Build a supervisor team that delegates to specialists.
- Build a swarm that hands off control between peers.
- Use the prebuilt
langgraph-supervisorandlanggraph-swarm. - Stream events from a multi-agent graph.
1. Supervisor pattern (the most common)
A supervisor LLM reads the user request, picks a worker, dispatches, gathers result, decides next worker or terminate.
Hand-rolled (so you understand it)
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langgraph.graph.message import add_messages
from typing import Annotated
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4.1-mini")
class TeamState(TypedDict):
messages: Annotated[list, add_messages]
next: Literal["researcher", "coder", "writer", "FINISH"]
SUPERVISOR_SYS = """You are a supervisor managing 3 specialists:
- researcher: gathers facts and citations from the web/KB
- coder: writes and runs code
- writer: composes the final user-facing answer
Given the conversation so far, output ONLY the JSON {"next": "<worker>"}
where worker is researcher, coder, writer, or FINISH (when the user task is complete)."""
class Route(BaseModel):
next: Literal["researcher", "coder", "writer", "FINISH"]
def supervisor(state):
decision = llm.with_structured_output(Route).invoke(
[SystemMessage(SUPERVISOR_SYS)] + state["messages"]
)
return {"next": decision.next}
def researcher(state):
out = llm.invoke([SystemMessage("You research with citations."),
*state["messages"]]).content
return {"messages": [AIMessage(content=out, name="researcher")]}
def coder(state):
out = llm.invoke([SystemMessage("You write & explain code."),
*state["messages"]]).content
return {"messages": [AIMessage(content=out, name="coder")]}
def writer(state):
out = llm.invoke([SystemMessage("You compose final answers crisply."),
*state["messages"]]).content
return {"messages": [AIMessage(content=out, name="writer")]}
g = StateGraph(TeamState)
g.add_node("supervisor", supervisor)
g.add_node("researcher", researcher)
g.add_node("coder", coder)
g.add_node("writer", writer)
g.add_edge(START, "supervisor")
for w in ("researcher", "coder", "writer"):
g.add_edge(w, "supervisor") # workers always report back
g.add_conditional_edges(
"supervisor",
lambda s: s["next"],
{"researcher": "researcher", "coder": "coder", "writer": "writer", "FINISH": END},
)
team = g.compile()That is the entire supervisor pattern in 60 lines. Routing is done by an LLM with structured output, every worker returns to the supervisor, the supervisor decides FINISH.
Prebuilt with langgraph-supervisor
# uv add langgraph-supervisor
from langgraph_supervisor import create_supervisor
from langgraph.prebuilt import create_react_agent
researcher = create_react_agent(model="openai:gpt-4.1-mini", tools=[tavily_tool], name="researcher",
prompt="You gather facts and cite URLs.")
coder = create_react_agent(model="openai:gpt-4.1-mini", tools=[python_tool], name="coder",
prompt="You write and run Python.")
writer = create_react_agent(model="openai:gpt-4.1-mini", tools=[], name="writer",
prompt="You write polished answers.")
team = create_supervisor(
[researcher, coder, writer],
model=ChatOpenAI(model="gpt-4.1-mini"),
prompt=("You are a supervisor managing researcher, coder, writer. "
"Delegate tasks. End when answer is ready."),
).compile()That is it. Clean, production-ready, integrates with checkpointers and LangSmith automatically.
2. Swarm pattern (peer hand-offs)
In a swarm, no boss. Each agent is responsible for itself and decides to hand off to a peer when the next step is outside its skill.
langgraph-swarm provides this:
# uv add langgraph-swarm
from langgraph_swarm import create_swarm, create_handoff_tool
from langgraph.prebuilt import create_react_agent
handoff_to_finance = create_handoff_tool(agent_name="finance", description="Hand off to finance for billing/refunds.")
handoff_to_tech = create_handoff_tool(agent_name="tech", description="Hand off to tech for bugs/setup.")
triage = create_react_agent(model="openai:gpt-4.1-mini",
tools=[handoff_to_finance, handoff_to_tech], name="triage",
prompt="Greet the user. If billing-related, hand off to finance. If technical, to tech.")
finance = create_react_agent(model="openai:gpt-4.1-mini",
tools=[get_invoice, refund], name="finance",
prompt="You handle billing/refunds. If question is technical, hand back to triage.")
tech = create_react_agent(model="openai:gpt-4.1-mini",
tools=[search_kb, ticket_create], name="tech",
prompt="You solve technical issues.")
swarm = create_swarm(
[triage, finance, tech],
default_active_agent="triage",
).compile()Behaviour:
- Conversation starts with
triage. - Whenever an agent calls a
handoff_*tool, control transfers. - The recipient sees the full message history and continues.
3. Streaming and inspecting team runs
config = {"configurable": {"thread_id": "u-42"}}
for ev in team.stream({"messages": [HumanMessage("Find the latest agentic-AI papers and write a 200-word summary.")]},
stream_mode="updates", config=config):
for node, update in ev.items():
print(node, "β", str(update)[:160])Use this to debug. With LangSmith on, every node + LLM call is logged.
4. State design tips for multi-agent
- Use a single
messagesfield with a reducer (add_messages) so workers append rather than overwrite. - Tag each message with
name=agent_nameso the supervisor can attribute work. - Keep "scratchpad" or "intermediate artifacts" in named keys (e.g.
notes,code_outputs). - Add a
step_countand cap it.
5. Common pitfalls
- Supervisor never picks
FINISH. Add an explicit "FINISH when the user task is complete" instruction and bound steps. - Workers ignore each other's outputs. Pass full message history; do not over-trim.
- Workers create circular hand-offs. Add a step counter and a "no immediate hand-back" rule.
- Tools assigned to the supervisor. In CrewAI hierarchical, the manager must NOT have tools β let it delegate. Same hygiene helps in LangGraph.
- No persistence. Compile with a checkpointer if multi-turn; otherwise the team forgets between turns.
Hands-on lab (5 hours)
Build a research-and-write team:
researcheragent with Tavily web search.analystagent with a Python tool (execsandbox or a code-runner) for crunching numbers.writeragent that drafts the final report.- Supervisor selects the next worker.
Requirements:
- Use the prebuilt
create_supervisor. - Persist with
SqliteSaver. - Stream events to a Streamlit UI ("Currently: researching...").
- Build a 10-question eval set; measure end-to-end success rate.
Stretch: refactor to a swarm with explicit hand-off tools and compare cost / success rate.
Self-check
- Why must workers always loop back to the supervisor in the canonical pattern?
- What does
create_handoff_toolactually do under the hood? - When would you skip prebuilt helpers and build the graph by hand?
- Why is
add_messagesimportant in a multi-agent state? - How do you stop a swarm from infinite hand-offs?
References
Sign in to save your progress and earn badges.