Multi-agent in LangGraph: supervisor and swarm

Implement the two most useful topologies with LangGraph's supervisor prebuilts and shared state.

πŸ•ΈοΈ Module 4 5 min read Not started

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

  1. Build a supervisor team that delegates to specialists.
  2. Build a swarm that hands off control between peers.
  3. Use the prebuilt langgraph-supervisor and langgraph-swarm.
  4. 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)

python
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

python
# 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:

python
# 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

python
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 messages field with a reducer (add_messages) so workers append rather than overwrite.
  • Tag each message with name=agent_name so the supervisor can attribute work.
  • Keep "scratchpad" or "intermediate artifacts" in named keys (e.g. notes, code_outputs).
  • Add a step_count and cap it.

5. Common pitfalls

  1. Supervisor never picks FINISH. Add an explicit "FINISH when the user task is complete" instruction and bound steps.
  2. Workers ignore each other's outputs. Pass full message history; do not over-trim.
  3. Workers create circular hand-offs. Add a step counter and a "no immediate hand-back" rule.
  4. Tools assigned to the supervisor. In CrewAI hierarchical, the manager must NOT have tools β€” let it delegate. Same hygiene helps in LangGraph.
  5. 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:

  • researcher agent with Tavily web search.
  • analyst agent with a Python tool (exec sandbox or a code-runner) for crunching numbers.
  • writer agent 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

  1. Why must workers always loop back to the supervisor in the canonical pattern?
  2. What does create_handoff_tool actually do under the hood?
  3. When would you skip prebuilt helpers and build the graph by hand?
  4. Why is add_messages important in a multi-agent state?
  5. How do you stop a swarm from infinite hand-offs?

References

Sign in to save your progress and earn badges.