AI & ML / Agents & orchestration / LangGraph / 08_multi_agent_and_subgraphs.md

Multi-agent and subgraphs

Updated 5 interview angles 4 min read source
On this page8
  1. Start with the warning
  2. Subgraphs: a graph as a node
  3. The supervisor pattern
  4. Handoff versus call
  5. Context isolation, concretely
  6. Debugging composition
  7. Related
  8. Interview angle

Multi-agent and subgraphs

How to compose graphs, and — more usefully in an interview — when not to.

Start with the warning

Five agents at 90% reliability each is about 59% end to end. Multi-agent multiplies unreliability, cost and latency, and most tasks that look like they need several agents need one agent with several tools.

Reach for multi-agent for three reasons only:

Reason Example
Context isolation a reader that would fill the window
Genuine parallelism ten independent documents
Privilege separation one agent may write, others read

If none applies, you want tools, not agents. See Multi-agent patterns.

Subgraphs: a graph as a node

python
sub = sub_builder.compile()

parent = StateGraph(ParentState)
parent.add_node("research", sub)

A compiled graph is callable like any node, so composition is free. Two ways the state can line up:

Shared keys. If parent and subgraph state share key names, they flow through automatically. Simple, and it couples the two schemas.

Transformed. Wrap the subgraph in a function that maps parent state in and subgraph output back out:

python
def research(state: ParentState) -> dict:
    out = sub.invoke({"query": state["question"]})
    return {"findings": out["summary"]}

Prefer the wrapper. It keeps the subgraph’s schema independent, which means you can test and reuse it without knowing about the parent — the same argument as not sharing a database between services.

The supervisor pattern

One coordinator routes to specialists and decides when the work is done.

text
        ┌──────────────┐
        │  supervisor  │
        └──┬───┬────┬──┘
           │   │    │
       research writer reviewer
           │   │    │
        └──────┴────┘
           back to supervisor

Each specialist returns to the supervisor, which routes again or finishes. The supervisor’s routing is a conditional edge or a Command — and it is worth asking whether it needs a model at all. A supervisor that routes on a field in state is deterministic, testable and free.

python
Route = Literal["research", "writer", "done"]

def supervise(state: State) -> Command[Route]:
    if not state["findings"]:
        return Command(goto="research")
    if not state["draft"]:
        return Command(goto="writer")
    return Command(goto="done")

That is a supervisor with no model in it. Many production “multi-agent systems” are exactly this, and saying so is a stronger answer than describing a free-running crew.

Handoff versus call

Two different shapes, and the distinction matters:

Call Handoff
Control returns to caller transfers away
Shape a function a goto
Fits a sub-task a change of role

Calling a subgraph is a function call — do the research, come back. Handing off transfers the conversation, as when a triage agent passes to a billing agent that now owns the thread. Command(goto=...) expresses the second; a subgraph node expresses the first.

Most systems want calls. Handoff is right when the receiving agent should own the rest of the interaction, which is mainly a customer-support shape.

Context isolation, concretely

The strongest technical reason to split. A sub-agent reading forty documents would fill the parent’s window with material nobody needs afterwards. Running it as a subgraph means the parent sees only the summary that comes back.

python
def deep_research(state: ParentState) -> dict:
    # The subgraph reads 40 docs in its own state.
    out = researcher.invoke({"topic": state["topic"]})
    # The parent only ever sees the summary.
    return {"brief": out["summary"]}

This is a context engineering decision expressed as an architecture — see Context engineering.

Gotcha: a sub-agent does not see the parent’s conversation. Whatever it needs must be in the input you pass it. A brief that assumes shared context produces a confidently wrong result, and the symptom looks like a model failure rather than a plumbing one.

Debugging composition

Two things make a composed system inspectable, and both are worth naming:

  1. Stream with subgraphs=True so child steps appear with their namespace (Streaming).
  2. Give every agent a name, so traces attribute work correctly. An unnamed sub-agent shows up as an anonymous span and you lose the ability to ask which agent burned the tokens.

Cost attribution per agent is the thing people wish they had after the first month — see Cost attribution.

Interview angle 5

  • “When do you use multiple agents?” - for context isolation, genuine parallelism, or privilege separation. Otherwise one agent with several tools, because five agents at 90% each is about 59% end to end.
  • “How do you compose graphs?” - a compiled graph is callable as a node. Either share state keys, or wrap it in a function mapping parent state in and results out — prefer the wrapper, since it keeps the subgraph’s schema independent and testable.
  • “What is the supervisor pattern?” - a coordinator that routes to specialists and decides when work is done. Worth asking whether it needs a model: a supervisor routing on a state field is deterministic, testable and free, and that is what many production systems actually are.
  • “Call or handoff?” - a call returns to the caller and is a function; a handoff transfers ownership of the thread with Command(goto=...). Most systems want calls; handoff fits a change of role, like triage passing to billing.
  • “What breaks in a multi-agent system?” - the sub-agent cannot see the parent’s conversation, so an incomplete brief produces a confidently wrong answer that looks like a model failure. And unnamed agents make traces and cost attribution useless.