Building Multi-Agent Pipelines with LangGraph: A Practical Guide
LangChain now recommends against the supervisor library most tutorials teach, here's what to build instead, and the benchmarks for choosing
If you learned to build multi-agent systems in LangGraph from a tutorial, you were probably taught create_supervisor from the langgraph-supervisor library. Go and read that library's README today and you'll find this at the very top:
We now recommend using the supervisor pattern directly via tools rather than this library for most use cases.
That's not a random blog opinion. That's LangChain, on their own repo, steering you somewhere else. Here's what changed, what to build instead, and, the part almost nobody covers, the published numbers on which pattern actually costs less.
Why the supervisor library fell out of favour
The old approach handed you a black box. You passed in a list of agents, it generated handoff tools, and it wired the graph for you. Convenient, until you needed to control what each agent actually sees.
And that turns out to be the whole game. LangChain's guidance is blunt about it:
At the center of multi-agent design is context engineering, deciding what information each agent sees.
A generated handoff tool passes the full message history to the next agent by default. On a long conversation that's a lot of tokens, most of them irrelevant to the specialist you're calling. The tool-calling approach gives you that control back, and it's just Python, no framework magic to fight.
The five patterns
Multi-agent isn't one thing. LangChain now documents five distinct architectures:
| Pattern | How it works |
|---|---|
| Subagents | A main agent calls subagents as tools. All routing goes through it. This is the supervisor. |
| Handoffs | Agents transfer control to each other. The active agent persists across turns. |
| Skills | One agent stays in control, loading specialized prompts and knowledge on demand. |
| Router | A classification step dispatches to specialists, then synthesizes the results. |
| Custom workflow | Bespoke graphs mixing deterministic logic with agentic steps. |
Most people reach for a supervisor because it's the one they've seen. It's often not the cheapest.
Building the supervisor properly
The mechanism is almost disappointingly simple: a subagent is just an agent, wrapped in a tool.
from langchain.tools import tool
from langchain.agents import create_agent
# 1. A specialist agent
research_agent = create_agent(model="...", tools=[web_search])
# 2. Wrap it as a tool the main agent can call
@tool("research", description="Research a topic and return findings")
def call_research_agent(query: str):
result = research_agent.invoke({"messages": [{"role": "user", "content": query}]})
return result["messages"][-1].content
# 3. The supervisor is just an agent whose tools happen to be agents
main_agent = create_agent(model="...", tools=[call_research_agent])
That's it. No supervisor class, no handoff machinery. The main agent sees a tool called research, decides when it's relevant, calls it, and gets a string back.
Because the subagent runs in its own context window, the main conversation never fills up with the specialist's intermediate reasoning. That isolation is the real prize, and it's why this pattern wins on token cost when you have multiple domains.
Scaling past a handful of agents
One tool per agent gets unwieldy fast. The alternative is a single dispatch tool over a registry:
SUBAGENTS = {
"research": research_agent,
"writer": writer_agent,
}
@tool
def task(agent_name: str, description: str) -> str:
"""Launch an ephemeral subagent for a task.
Available agents:
- research: Research and fact-finding
- writer: Content creation and editing
"""
agent = SUBAGENTS[agent_name]
result = agent.invoke({"messages": [{"role": "user", "content": description}]})
return result["messages"][-1].content
main_agent = create_agent(
model="...",
tools=[task],
system_prompt=(
"You coordinate specialized sub-agents. "
"Available: research (fact-finding), writer (content creation). "
"Use the task tool to delegate work."
),
)
Now adding an agent means adding a dictionary entry. Different teams can ship agents independently without touching the coordinator.
The three decisions that actually determine whether it works
Everything above is plumbing. These three are where systems succeed or quietly fail.
1. Names and descriptions are your routing logic. The main agent has nothing else to go on. research_agent, "researches topics using web search; use for current events, not math" routes correctly. agent_2, "does stuff" does not. These are prompt engineering, not naming conventions.
2. Inputs decide what the specialist knows. By default your subagent gets only the query string. If it needs the conversation so far, pass it explicitly, and pass only what helps. This is the control the old library took away from you.
3. Outputs decide whether the supervisor can act.
Fix it in the prompt: tell the subagent explicitly that its final message is the only thing the supervisor receives, so the answer must be in it.
Which pattern is actually cheapest?
This is the part almost no tutorial gives you. LangChain publishes real comparisons:
| Pattern | One-shot | Repeat request | Multi-domain (3 topics) |
|---|---|---|---|
| Subagents | 4 calls | 8 calls | 5 calls · ~9K tokens |
| Handoffs | 3 calls | 5 calls | 7+ calls · ~14K tokens |
| Skills | 3 calls | 5 calls | 3 calls · ~15K tokens |
| Router | 3 calls | 6 calls | 5 calls · ~9K tokens |
Three things fall out of that table:
Subagents cost one extra call on simple tasks. Results flow back through the main agent. You're paying that call for centralized control, fine, as long as you know you're paying it.
Stateful patterns win on repetition. Handoffs and Skills save 40–50% of calls on a repeated request, because the right agent (or context) is already active. Subagents are stateless by design and re-run the whole flow every time.
But context isolation wins on breadth. Ask about three domains at once and Skills balloons to ~15K tokens, every subsequent call re-processes all that loaded documentation. Subagents handles it in ~9K, roughly 67% fewer tokens, because each specialist works in a clean window. Handoffs is worst here: it runs sequentially and can't fan out in parallel at all.
So: few domains, repeated interactions → Handoffs or Skills. Many domains, parallel work, large contexts → Subagents or Router.
app.invoke(inputs, config={"recursion_limit": 10})
Two lines. Do it now, not after the invoice.
Related on Everyday Data Science: The agent revolution is here, and most organizations are not ready, on what separates the teams that get agents into production.
Key takeaways
- Don't reach for
create_supervisor. LangChain now recommends the tool-based approach, a subagent is just an agent wrapped in a@tool. - Context engineering is the design. Names, inputs, and outputs decide whether the system works; the graph wiring rarely does.
- Tell subagents their final message is all the supervisor sees. This one line of prompt prevents the most common silent failure.
- Choose the pattern from your access shape, repeated single-domain work and broad multi-domain work want opposite architectures.
- Cap the recursion. Always.
Which multi-agent pattern are you running in production, and did you pick it deliberately, or because it was the one in the tutorial? Hit the comments 👇
Sources: LangChain multi-agent docs, Subagents pattern, langgraph-supervisor README. Benchmarks are LangChain's published figures; your numbers will vary by model and workload.
RAG vs. Fine-Tuning: A 2026 Decision Framework for Practitioners
Next →Agent Accuracy Went Up. Agent Reliability Barely Moved.
Found this useful? Passing it on to someone who builds is the best way to help the publication grow.