Appearance
5.6 — Subgraphs and Multi-Agent Systems
1. What is it?
A subgraph is a compiled LangGraph graph used as a node within a larger, parent graph — composability applied to the graph abstraction itself. Multi-agent systems are architectures where multiple distinct agents (each potentially with their own tools, prompts, and even model choice) collaborate on a task, coordinated through some combination of subgraphs, conditional routing (Part 5.2), and shared or partitioned state.
2. Why does it exist?
Part 3.12's decomposition principle (don't build one monolithic system when the problem naturally splits into distinct concerns) applies at the graph level too: a complex workflow often has genuinely separable sub-processes (a document-intake sub-process, a review sub-process, a notification sub-process) that are each independently useful, testable, and reusable. Subgraphs exist to let you build and test these sub-processes independently, then compose them into a larger graph — directly extending Part 5.1's node/edge model with the ability for a "node" to itself be an entire, complex graph rather than a single function.
Multi-agent systems extend this further: some problems genuinely benefit from multiple distinct agents with different specializations (a research agent, a writing agent, a fact-checking agent) collaborating, rather than one agent trying to hold all of that specialization and context simultaneously — directly analogous to how a human team divides complex work among specialists rather than one generalist doing everything.
4. How does it work internally?
(Sections 3 and 4 combined for this chapter, given their tight coupling — subgraphs and multi-agent patterns solve overlapping composability problems.)
Subgraphs — a compiled graph as a node
python
document_intake_graph = build_document_intake_graph().compile()
parent_graph = StateGraph(ParentState)
parent_graph.add_node("intake", document_intake_graph) # a compiled graph used directly as a node
parent_graph.add_node("review", review_node)
parent_graph.add_edge("intake", "review")When a compiled graph is used as a node, LangGraph invokes it with (a mapped view of) the parent's state as input and merges its output back into the parent's state — the subgraph runs its own internal sequence of nodes/edges/supersteps (Part 5.1) as a self-contained unit from the parent's perspective. This means a subgraph can be developed, tested, and even checkpointed independently (Part 1.9's testing discipline applies naturally at this granularity — you can write focused tests for document_intake_graph alone, without needing to run the entire parent workflow), then composed into one or more larger parent graphs, potentially reused across multiple different parent workflows the same way a well-designed function is reused across call sites.
State schema mapping between parent and subgraph
A subgraph often has its own, more specific state schema than its parent graph's broader state — LangGraph requires (or provides mechanisms for) mapping between the parent's state shape and the subgraph's expected input/output shape at the boundary. Getting this mapping right and explicit is important: fields the subgraph doesn't need shouldn't need to be threaded through it, and fields the subgraph produces need clear rules for how they merge back into the parent's larger state (the same reducer considerations from Part 5.1/5.2 apply at this composition boundary too).
Multi-agent patterns
Several recurring architectural patterns exist for coordinating multiple distinct agents, most implementable as combinations of subgraphs and conditional routing:
- Supervisor pattern: a central "supervisor" agent/node decides, at each step, which specialized sub-agent should act next (structurally, this is Part 5.2's conditional routing, but routing to entire agent subgraphs rather than simple fixed handlers) — the supervisor's decision can itself be dynamic/LLM-driven (making the overall system agent-like per Part 3.7) even though each individual sub-agent might internally be a more constrained workflow.
- Sequential/pipeline pattern: agents run in a fixed sequence, each handling one stage (a research agent's output feeds a writing agent, whose output feeds a fact-checking agent) — this is simply Part 5.1's fixed-edge graph structure, with each node being a full agent subgraph rather than a single function.
- Peer-to-peer/network pattern: agents can hand off to each other more freely based on their own judgment about who should act next, rather than a central supervisor deciding — genuinely closer to Part 3.7's open-ended agent category at the system level, with correspondingly higher unpredictability and harder-to-audit behavior, and warranting the same "is this genuinely justified by the task's unpredictability" scrutiny Part 3.7 argued for at the single-agent level.
Supervisorroutes to next agent
Research agentsubgraph
Writing agentsubgraph
Fact-check agentsubgraph
The Command mechanism — handoff with a simultaneous state update
Peer-to-peer handoff (and, below, swarm) needs a concrete mechanism for one agent node to both (a) update shared state and (b) decide which node runs next, in a single atomic step — rather than returning a state update and relying on a separate conditional-edge function (Part 5.2) to inspect that state and compute the next destination. LangGraph provides this via Command, returned directly from a node function:
python
from langgraph.types import Command
from typing import Literal
def research_agent_node(state: "SwarmState") -> Command[Literal["writing_agent", "fact_check_agent"]]:
"""
Runs the research agent's turn, then hands off directly to whichever peer
agent it judges should act next, updating shared state in the same step.
"""
findings = run_research(state["task_description"])
next_agent = "writing_agent" if findings.get("ready_for_draft") else "fact_check_agent"
return Command(
goto=next_agent,
update={"research_findings": findings, "last_agent": "research_agent"},
)Verify the exact current import path and Command/goto/update signature against current LangGraph documentation — this is an actively developed part of the multi-agent API surface. The key property Command provides: the node that decides "who acts next" is the same node that produces the state update that decision was based on, which is exactly what a genuine agent-to-agent handoff (as opposed to a fixed edge, or a separate supervisor's routing function) requires.
Swarm — a fourth pattern: decentralized handoff with no central router
A swarm is a multi-agent architecture built entirely from Command-based handoffs between peer agents, with no central supervisor node at all — each agent, at the end of its own turn, decides (via Command(goto=...)) which other agent should act next, directly, the same way the peer-to-peer pattern above is described conceptually, but now with Command as the concrete mechanism that makes it buildable rather than merely a name for "agents talk to each other somehow."
Research agent
Writing agent
Fact-check agent
Swarm vs. supervisor, concretely: a supervisor pattern routes every handoff decision through one central node, which means that node is a single, auditable place to see (and log, and constrain) every routing decision the whole system makes — at the cost of that node being both a coordination bottleneck (an extra LLM call layered on every handoff, section 11) and a single point of design complexity as the number of possible destinations grows. A swarm has no such bottleneck — any agent can hand off directly to any other agent it judges relevant, with no extra routing hop — but this means there is no single place to see or constrain the system's overall routing behavior; auditing "why did the system do X" requires reconstructing the handoff chain after the fact from each agent's individual decision, rather than reading one supervisor's decision log. Swarm trades away the supervisor's central auditability specifically in exchange for removing its coordination bottleneck — a real tradeoff, not a strict improvement, and one that should be weighed with the same "is this genuinely justified" scrutiny section 4's peer-to-peer discussion already argues for.
Hierarchical — a fifth pattern: supervisors of supervisors
A hierarchical multi-agent system composes this chapter's own primitives one level higher: a top-level supervisor doesn't route directly to individual specialized agents, but to sub-teams, each of which is itself a supervisor-coordinated group of agents (built exactly as section 4's supervisor pattern describes, then wrapped as a subgraph per this chapter's subgraph mechanism and used as a single node in the top-level supervisor's graph). Nothing new is required to build this beyond composing the chapter's existing subgraph-as-node and supervisor primitives at an additional level of nesting:
Top-level supervisor
Research teamsubgraph: own supervisor + agents
Delivery teamsubgraph: own supervisor + agents
Tradeoff: hierarchical composition is what makes a large number of specialized agents genuinely manageable — a single flat supervisor routing across, say, fifteen individual agents becomes an unwieldy routing function and an unwieldy prompt for the supervisor to reason over, whereas grouping those fifteen into three or four sub-team supervisors under one top-level supervisor keeps each individual routing decision (top-level: which team; team-level: which agent within the team) small and well-scoped — the same decomposition benefit Part 3.12 argues for generally, applied to the multi-agent routing problem itself. The cost is deeper latency stacking (a request now passes through the top-level supervisor's routing call, then the sub-team supervisor's routing call, then the actual specialized agent — three sequential LLM calls of coordination overhead where a flat supervisor pattern would have only one) and harder end-to-end tracing (a trace, Part 6.1, now has an additional level of nesting to walk through to find where a specific decision was actually made, and a problem inside a sub-team can be harder to distinguish from a problem in the top-level routing without deliberately structured trace metadata per level). Reach for hierarchical composition specifically when the number of distinct specialized agents has grown large enough that a flat supervisor is itself becoming the unwieldy, hard-to-reason-about component — not as a default structure for a handful of agents that a flat supervisor handles perfectly well.
Why multi-agent isn't automatically better
Directly extending Part 3.12's core discipline: splitting a task across multiple agents adds real overhead (more LLM calls for coordination/handoff decisions, Part 3.11's cost multiplication concern applies with more force here since a supervisor's routing decision is itself an LLM call on top of every specialized agent's own calls) and real complexity (more moving pieces to debug, per-agent context to manage, Part 3.9's memory concerns multiplied across agents). A single well-prompted agent, or even a fixed workflow with no "agent" framing at all, frequently outperforms a multi-agent architecture on tasks that don't genuinely require distinct specializations — the decision to go multi-agent should follow the same measured, evidence-based process Part 3.12 argued for single-vs-multi-component architecture generally, not be adopted by default because it sounds more sophisticated.
5. Simple mental model
A subgraph is like a sealed sub-assembly in manufacturing (a car's engine, built and tested independently on its own assembly line, then installed as one unit into the larger car assembly) — it has its own internal complexity, fully worked out and verified in isolation, and the larger assembly line only needs to know its inputs and outputs, not its internal workings. A multi-agent supervisor system is like a project manager assigning tasks to specialists on a team — effective when the specialists' distinct expertise genuinely adds value beyond what one generalist could do, but adding real coordination overhead (meetings, handoffs, context-sharing between specialists) that a single capable generalist wouldn't need at all for a task that doesn't actually require that division of labor.
6. Real-world example
A legal-document review platform initially built one large agent trying to simultaneously extract clauses, assess risk, and draft a summary — in practice, this single agent's prompt became unwieldy (trying to hold three distinct tasks' worth of instructions and context at once) and its outputs became inconsistent across the three sub-tasks. Restructuring as three separate, focused subgraphs (a clause-extraction subgraph, a risk-assessment subgraph consuming the extraction's output, and a summary-drafting subgraph consuming both) — composed as a fixed sequential pipeline (no supervisor needed, since the sequence is actually fixed and known, Part 3.7's workflow-over-agent principle applied at the multi-agent level too) — let each subgraph be independently prompted, tested, and evaluated (Part 8) for its specific narrow task, producing more consistent results than the single overloaded agent, without needing the added complexity and cost of a dynamic supervisor pattern the task's actual (fixed) structure didn't require.
7. Architecture diagram
Clause extractionsubgraph, own nodes/edges
Risk assessmentsubgraph, consumes prior output
Summary draftsubgraph, consumes both prior outputs
8. Production considerations
- Test subgraphs independently before composing them — this is the concrete testing benefit of the sealed-sub-assembly model from section 5; a subgraph with its own passing test suite gives you confidence at composition time that isn't re-litigated from scratch in every parent graph that uses it.
- Default to a fixed pipeline/sequential multi-agent structure (Part 3.7's workflow preference, applied here) unless the task's genuine unpredictability justifies a dynamic supervisor pattern — exactly the section 4 warning against defaulting to multi-agent sophistication for its own sake.
- Track cost and latency per sub-agent/subgraph explicitly (Part 3.11, Part 7.10) — a multi-agent pipeline's total cost is the sum of every sub-agent's calls plus any coordination overhead, and without per-component tracking, it's hard to identify which specific sub-agent is the actual cost or latency bottleneck.
- Design state-mapping boundaries between parent and subgraph explicitly and minimally — pass only what each subgraph actually needs, not the parent's entire state indiscriminately, for the same data-minimization and clarity reasons Part 5.1's node-granularity discussion argued for.
- Prefer supervisor over swarm when auditability matters more than removing the coordination bottleneck — a regulated or high-stakes workflow (loan review, this Part's running example) generally wants one central, loggable place where every routing decision can be reviewed, which swarm's decentralized handoff structurally doesn't provide.
- Reach for hierarchical composition only once a flat supervisor is genuinely becoming unwieldy (a large, growing number of distinct specialized agents) — applied prematurely to a handful of agents, it adds latency-stacking and tracing overhead a flat supervisor wouldn't have incurred for the same task.
9. Common mistakes
- Defaulting to a supervisor/dynamic multi-agent architecture for a task whose actual structure is a fixed, knowable sequence — reproducing Part 3.7's core anti-pattern at the multi-agent scale, with correspondingly larger cost and unpredictability overhead.
- Not testing subgraphs independently, losing the composability/testability benefit that's the primary reason to use them in the first place.
- Passing a parent graph's entire state into every subgraph indiscriminately, rather than mapping only the specific fields each subgraph actually needs — increasing coupling and making each subgraph harder to reuse in a different parent context.
- Not tracking cost/latency per sub-agent, making it hard to diagnose which specific part of a multi-agent pipeline is responsible for a performance or cost problem.
- Choosing swarm for a high-stakes workflow specifically because it removes the supervisor's bottleneck, without weighing that the same decentralization removes the one place routing behavior could otherwise be centrally audited.
- Building a hierarchical structure for a small, fixed number of agents that a flat supervisor would have handled fine — adding latency-stacking and tracing complexity the actual agent count didn't yet justify.
10. Security considerations
- Every sub-agent/subgraph that has tool access carries its own excessive-agency risk profile (Part 3.3, Part 9.2) — a multi-agent system's overall risk is not simply "the same as a single agent," it's the combined risk surface of every sub-agent's tool access, and each sub-agent's tools should be scoped as narrowly as that specific sub-agent's actual job requires, not given broad access "just in case" it's useful across the whole system.
- If different sub-agents operate on data with different sensitivity levels (e.g., one sub-agent handles public product information, another handles confidential financial data), state-mapping boundaries between subgraphs are exactly where to enforce that a sub-agent only receives the specific data its own task requires, not the full combined context of everything the parent graph has accumulated (Part 9.6's data-minimization principle, applied at the multi-agent architecture level).
11. Performance considerations
- A supervisor pattern's routing decision is an additional LLM call layered on top of whichever specialized agent it routes to — for a high-frequency system, this coordination overhead is a real, additive latency cost worth weighing against a simpler fixed-pipeline alternative where no such per-step routing decision is needed.
- Independent subgraphs in a genuinely parallel structure (not a sequential pipeline) can benefit from the same superstep-level parallelism (Part 5.1) as independent nodes — a real performance opportunity when sub-agents' work is genuinely independent rather than dependent on each other's output.
- Swarm removes the supervisor's per-handoff routing-call latency entirely (a
Command-based handoff is decided by the acting agent itself, not a separate routing call) — a genuine latency advantage over supervisor when that extra hop matters, at the auditability cost described in section 4. - Hierarchical composition adds latency multiplicatively with depth — each additional level of supervisor-of-supervisors nesting adds one more sequential coordination call to the critical path of every request, which compounds with the per-agent LLM call latency already present at the leaf level.
12. Cost considerations
- Multi-agent cost is additive and compounding: every sub-agent's own LLM calls, plus any supervisor/coordination overhead — Part 3.11's routing/model-selection discipline should be applied per sub-agent independently (a simple sub-agent's task might warrant a smaller, cheaper model even within an otherwise sophisticated multi-agent system), rather than uniformly using the same model tier across every sub-agent regardless of that sub-agent's actual task complexity.
13. When to use it
Subgraphs: whenever a workflow has a genuinely separable, independently-testable, potentially-reusable sub-process — a strong default for any moderately complex graph. Multi-agent (specifically, multiple distinct specialized agents, as opposed to a single agent or a fixed pipeline of narrow tasks): when the task genuinely benefits from distinct specializations that a single agent's context/prompt can't hold effectively together (section 6's legal-document example), or when the task's coordination between specialists is itself genuinely unpredictable enough to justify a dynamic supervisor (Part 3.7's agent-justification test, applied at this scale). Swarm specifically: when supervisor's central routing call is a genuine, measured bottleneck and the task doesn't carry the regulatory/auditability weight that would make losing central routing visibility a real problem. Hierarchical specifically: once the number of distinct specialized agents has grown large enough that a single flat supervisor's routing function and prompt have themselves become the unwieldy part of the system.
14. When NOT to use it
A task with a fixed, known sequence of specialized steps doesn't need a dynamic supervisor — a fixed sequential pipeline of subgraphs (section 6's redesigned example) captures the specialization benefit without the coordination overhead and unpredictability of a supervisor deciding routing dynamically. A task simple enough for one well-scoped agent or workflow doesn't benefit from splitting into multiple agents at all — Part 3.12's "simplest architecture that could plausibly work" principle applies at full force here.
15. Alternatives and trade-offs
| Approach | Good for | Weak point |
|---|---|---|
| Single agent/workflow (Parts 3.7, 5.1-5.2) | Simple tasks, lowest coordination overhead | Can become unwieldy for genuinely multi-specialization tasks |
| Fixed sequential subgraph pipeline | Known, fixed multi-stage tasks with distinct specializations | No flexibility for genuinely unpredictable coordination needs |
| Supervisor multi-agent pattern | Genuinely unpredictable coordination between specialists; central auditability of routing decisions | Supervisor is a coordination bottleneck and an added LLM call on every handoff |
Swarm (Command-based, decentralized) | Removing the supervisor's coordination bottleneck; agents that genuinely need to hand off to each other directly | No central place to see or constrain overall routing behavior — harder to audit |
| Hierarchical (supervisors of supervisors) | Scaling to a large number of specialized agents while keeping each routing decision small | Deeper latency stacking (multiple sequential coordination hops); harder end-to-end tracing |
16. Practical Python/code example
python
from langgraph.graph import StateGraph, END
def build_clause_extraction_subgraph():
"""Builds and compiles an independently-testable clause-extraction subgraph."""
sub = StateGraph(ClauseExtractionState)
sub.add_node("extract", extract_clauses_node)
sub.set_entry_point("extract")
sub.add_edge("extract", END)
return sub.compile()
def build_parent_review_graph():
"""Composes independently-built subgraphs into a fixed sequential pipeline."""
parent = StateGraph(DocumentReviewState)
parent.add_node("clause_extraction", build_clause_extraction_subgraph())
parent.add_node("risk_assessment", build_risk_assessment_subgraph())
parent.add_node("summary_draft", build_summary_draft_subgraph())
parent.set_entry_point("clause_extraction")
parent.add_edge("clause_extraction", "risk_assessment")
parent.add_edge("risk_assessment", "summary_draft")
parent.add_edge("summary_draft", END)
return parent.compile()Each build_*_subgraph() function can be tested completely independently (invoking it directly with a test state and asserting on its output) before ever being composed into build_parent_review_graph() — the concrete realization of section 8's testing-independence benefit.
17. Production-quality example
A supervisor pattern with explicit per-agent cost tracking, addressing section 8/12's cost-visibility recommendation:
python
import logging
logger = logging.getLogger("multi_agent_supervisor")
def route_to_next_agent(state: "ResearchTaskState") -> str:
"""
Supervisor routing function — decides which specialized agent acts next,
or whether the task is complete, based on accumulated progress in state.
"""
if not state.get("research_complete"):
return "research_agent"
elif not state.get("draft_complete"):
return "writing_agent"
elif not state.get("fact_check_complete"):
return "fact_check_agent"
return "done"
async def research_agent_node(state: "ResearchTaskState") -> dict:
"""Runs the research sub-agent, logging its own cost contribution explicitly."""
result, usage = await run_research_subagent(state["task_description"])
logger.info("research_agent tokens: input=%d output=%d", usage.input_tokens, usage.output_tokens)
return {"research_findings": result, "research_complete": True}
# Analogous writing_agent_node and fact_check_agent_node, each logging their own
# usage independently — giving per-agent cost visibility across the whole
# multi-agent pipeline, addressing the "hard to diagnose which sub-agent is the
# bottleneck" common mistake from section 9.
graph = StateGraph(ResearchTaskState)
graph.add_node("research_agent", research_agent_node)
graph.add_node("writing_agent", writing_agent_node)
graph.add_node("fact_check_agent", fact_check_agent_node)
graph.set_entry_point("research_agent")
for node in ["research_agent", "writing_agent", "fact_check_agent"]:
graph.add_conditional_edges(
node, route_to_next_agent,
{"research_agent": "research_agent", "writing_agent": "writing_agent",
"fact_check_agent": "fact_check_agent", "done": END},
)18. Short exercise
A customer proposes a five-agent supervisor system for what turns out, on closer inspection, to be a task with exactly three fixed, always-sequential steps (never varying in order, never skipped). Using this chapter's section 9/14 guidance, write a short justification for recommending a fixed sequential subgraph pipeline instead, including what specific cost/complexity/unpredictability the customer would avoid.
19. Interview questions
- What does composing a subgraph as a node actually provide over just writing a larger single graph with more nodes?
- Why might a fixed sequential pipeline of specialized subgraphs outperform a dynamic supervisor pattern for a task with genuinely fixed structure?
- What additional security/data-minimization consideration does a multi-agent architecture introduce that a single-agent system doesn't have to the same degree?
- What does
Command(goto=..., update=...)provide that returning a plain state update and using a separate conditional-edge function doesn't? - Contrast swarm and supervisor on auditability versus coordination overhead — when would you choose each?
- Why does hierarchical composition help at large agent counts, and what two specific costs does it introduce?
20. FDE/customer scenario
Customer: "We've seen impressive multi-agent demos — shouldn't our document processing pipeline use a team of specialized agents too?"
This is directly the section 6/9 scenario: walking through the customer's actual task structure (as the legal-document example did) usually reveals whether it's genuinely a candidate for a dynamic supervisor pattern (real, unpredictable coordination needs) or better served by a simpler, fixed sequential pipeline of specialized subgraphs — capturing the same "specialization" benefit that made the demo impressive, without the added cost, latency, and unpredictability of a supervisor architecture the actual task doesn't need. This is Part 3.12's architecture-decision discipline, applied specifically to the "should this be multi-agent" question that recurs constantly in real FDE engagements.
Key takeaways
- Subgraphs let you build, test, and reuse independently-verified sub-processes, composed into larger graphs — extending Part 5.1's node/edge model with composability.
- Multi-agent patterns (supervisor, sequential pipeline, peer-to-peer/swarm, hierarchical) trade coordination overhead, auditability, and latency for specialization benefit — the trade-off must be justified by the task's actual structure, not adopted for its own sake.
- A fixed sequential pipeline of specialized subgraphs often outperforms a dynamic supervisor for tasks with genuinely known, fixed structure.
Command(goto=..., update=...)is the concrete mechanism behind agent-to-agent handoff (peer-to-peer and swarm) — one node atomically both updates state and decides the next destination.- Swarm removes supervisor's coordination bottleneck at the cost of central auditability; hierarchical composition scales to large agent counts at the cost of deeper latency stacking and harder end-to-end tracing.
Things you should be able to explain
- What subgraph composition provides over one larger flat graph.
- Why a supervisor pattern's routing decision is itself an additive cost/latency factor.
- What
Commandprovides over a plain state update plus a conditional edge, and how it enables swarm's decentralized handoffs. - The auditability-vs-bottleneck tradeoff between supervisor and swarm, and the scalability-vs-latency/tracing tradeoff of hierarchical composition.
Things you should be able to build
- An independently-tested subgraph composed into a fixed sequential pipeline, and a supervisor-routed multi-agent system with per-agent cost logging.
- A
Command-based handoff between two peer agent nodes.
Common mistakes
- Defaulting to a dynamic supervisor pattern for tasks with fixed, known structure.
- Not testing subgraphs independently before composition.
- No per-sub-agent cost/latency visibility in a multi-agent pipeline.
- Choosing swarm for a high-stakes workflow where central auditability of routing decisions actually matters.
- Building a hierarchical structure before the agent count actually justifies its added latency and tracing overhead.
Recommended next chapter
07-durable-execution-retries-recovery.md