Unit 09.00: A supervisor that routes rather than reasons
The supervisor pattern is one agent deciding which other agent works next. It works well when the supervisor does exactly that and nothing else.
A router with a job title
A supervisor node reads the state and routes. It does not summarise, draft, call a tool or accumulate context. Structurally it is the conditional edge from Module 2 with a more impressive name.
The code routes three ticket kinds to three workers.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
kind: str
handled_by: str
def supervisor(state: State):
"""Decides who works next. It does no work itself."""
return {}
def route(state: State):
return {"refund": "billing", "bug": "engineering"}.get(state["kind"], "general")
g = StateGraph(State)
g.add_node("supervisor", supervisor)
for name in ("billing", "engineering", "general"):
g.add_node(name, (lambda n: (lambda s: {"handled_by": n}))(name))
g.add_edge(name, END)
g.add_edge(START, "supervisor")
g.add_conditional_edges("supervisor", route,
{n: n for n in ("billing", "engineering", "general")})
app = g.compile()
for kind in ("refund", "bug", "something else"):
print(f"{kind:16} -> {app.invoke({'kind': kind, 'handled_by': ''})['handled_by']}")
# A supervisor that also summarises, or drafts, or calls a tool is a worker
# with routing bolted on -- and when the routing is wrong you cannot tell
# whether the decision or the work was at fault.
supervisor returns an empty update - it changes nothing. All the decision lives in route, which is a pure function of state and testable without building a graph.
That separation is what makes supervisor failures diagnosable. When a ticket reaches the wrong worker you know the fault is in one small function, and you can reproduce it with a dictionary.
The mistake this prevents
The mistake is letting the supervisor also do work - summarising the ticket before routing, or enriching the state. When routing goes wrong you then cannot tell whether the decision was bad or the summary it decided from was. Keep the router pure and put enrichment in a node before it.
Takeaway
A supervisor routes and does nothing else. Enrichment goes in a separate node upstream, so a bad route and a bad summary remain distinguishable.
