
Services
How to architect a multi-agent system where specialized agents collaborate without going off the rails. The supervisor-worker pattern, explained with a real e-commerce example, working code, and production guardrails.

The first multi-agent system most engineers ship is a free-for-all - four agents, all allowed to call each other, all allowed to call tools. It demos beautifully. In production it either loops forever, contradicts itself, or burns through your token budget by week two. The fix is the supervisor pattern, and once you internalize it you will reach for it on almost every agent project.
One supervisor agent owns the plan and delegates. Worker agents are specialists that do one thing and return. No peer-to-peer communication. No shared-state mutations without supervisor approval. This is how you get determinism, observability, and safety in a multi-agent system.
When every agent can call every other agent, three predictable failure modes emerge:
The supervisor pattern solves all three by design - not by adding more guardrails on top of a chaotic system.
The pattern mirrors how good human teams work. The project manager (supervisor) reads the brief, decides which specialist (worker) to call, waits for the result, then decides the next move. Specialists do not freelance - they execute a narrow task and hand back.
Three properties this gives you that a peer-to-peer swarm does not:
Here is a support system I built using this pattern in late 2024. The supervisor receives a customer ticket and picks between three workers:
The supervisor loops: plan, delegate, observe, re-plan. It is the only agent that can call the Action Worker. The two read-only workers cannot call anyone - they just return data.
from langgraph.graph import StateGraph, END
from typing import TypedDict, Literal
class State(TypedDict):
ticket: str
plan: list
observations: list
proposed_action: dict | None
action_approved: bool
def supervisor(state: State) -> dict:
next_step = llm_plan_next(state)
# next_step is one of: "lookup", "policy", "action", "done"
return {"plan": state["plan"] + [next_step]}
def route(state: State) -> Literal["lookup", "policy", "action", "done"]:
return state["plan"][-1]
graph = StateGraph(State)
graph.add_node("supervisor", supervisor)
graph.add_node("lookup", order_lookup_worker)
graph.add_node("policy", policy_worker)
graph.add_node("action", action_worker)
graph.set_entry_point("supervisor")
graph.add_conditional_edges("supervisor", route, {
"lookup": "lookup",
"policy": "policy",
"action": "action",
"done": END
})
# Workers always return to the supervisor
graph.add_edge("lookup", "supervisor")
graph.add_edge("policy", "supervisor")
graph.add_edge("action", "supervisor")
app = graph.compile(checkpointer=postgres_saver)
Two non-obvious choices in this implementation:
The same e-commerce system, 90 days post-launch:
If your task is genuinely collaborative and creative - say, a research crew brainstorming angles on a topic - a peer-to-peer or role-playing pattern (CrewAI style) can be worth the debuggability cost. The brainstorm is the point, and the lack of determinism is a feature.
But for anything that touches customer data, money, or external write actions, start with a supervisor. Always. The "but my use case is creative" argument almost always loses to "your customers expect deterministic outcomes."
Yes. LangGraph supports parallel branches via fan-out edges. The supervisor delegates to multiple workers simultaneously and waits for all to return before re-planning. This is useful when the order lookup and policy lookup can run independently.
CrewAI's hierarchical process is a similar idea - one manager agent delegates to workers. The difference is in control: LangGraph gives you explicit edge logic and typed state. CrewAI abstracts both away. For production write actions, the LangGraph version is easier to debug and audit.
A small, fast model (GPT-4o-mini, Claude 3.5 Haiku, Gemini 1.5 Flash) is usually right. The supervisor is making routing decisions from a fixed enum, not generating prose. Reserve the heavy models for workers that need to reason or write.
Add the new node, add an edge from the supervisor to the new node and back, extend the routing enum, and add a few-shot example to the supervisor prompt that demonstrates when to use it. Ship behind a feature flag and roll to a small percentage of traffic first.
It scales, but past 8-10 workers you should consider grouping workers under sub-supervisors (a hierarchical pattern). Otherwise the supervisor's prompt grows unwieldy and routing accuracy drops.
The supervisor pattern is the boring, durable architecture that ships and stays shipped. Every multi-agent system I have built in production over the last 18 months has used some variant of it. Free-for-all swarms make better demos; supervisor-worker systems make better businesses.
If you want a second opinion on the architecture of your specific multi-agent project, happy to whiteboard it with you in a free 30-minute call.
Free 30-minute scoping call. I will help you sketch the supervisor and worker boundaries on a whiteboard.
Book a Scoping Call