supervisor recipe

The Supervisor pattern: routing to specialist sub-agents

A supervisor agent that reads the user message and routes to specialist sub-agents (docs, billing, escalation). The default pattern for multi-domain customer-facing agents.

5 min read · Published May 2, 2026 · Languages: python, typescript, rust, go, java

The pattern

A Supervisor reads the user message, picks a specialist sub-agent to handle it, and forwards the call. The supervisor doesn’t do the work — it routes.

Supervisor in one line: One LLM call decides which sub-agent runs, then the sub-agent runs to completion. The supervisor doesn’t think about tools — only about which specialist owns this conversation turn.

Prebuilt

from agentmatic.prebuilt import create_supervisor, create_react_agent, create_rag_agent

docs = create_rag_agent(llm=OpenAI(), vectorstore=Qdrant.from_env())
billing = create_react_agent(llm=OpenAI(), tools=[get_invoice, refund])
escalation = create_react_agent(llm=OpenAI(), tools=[send_to_human])

supervisor = create_supervisor(
    llm=OpenAI("gpt-4o"),
    agents={"docs": docs, "billing": billing, "escalation": escalation},
    system_prompt="""
      Route to docs for product questions.
      Route to billing for invoice/refund questions.
      Route to escalation for anything off-policy.
    """,
)

What makes it work

  • Hard separation — each sub-agent has its own tool set, system prompt, and memory.
  • Clear routing prompt — supervisor sees the message and a one-line summary of each agent.
  • Bounded recursion — supervisors don’t supervise other supervisors (we have a max_depth guard).

Compared to handing all tools to one ReAct agent

ReAct with all toolsSupervisor
Prompt sizeGrows with toolsetConstant
Cost per turnHigher (more tokens)Lower
Tool confusionMore commonRare
Add a new specialtyAdd tools, retest allAdd a sub-agent, no retest

For 3+ distinct domains, Supervisor wins on quality, cost, and maintainability.

Custom routing (hand-rolled)

If you don’t want the LLM doing routing — e.g., you want deterministic rule-based routing — use a StateGraph with add_conditional_edges:

def route(state):
    intent = classify(state.messages[-1].content)
    return {"product": "docs", "billing": "billing", "other": "escalation"}[intent]

graph.add_conditional_edges(START, route, {"docs": "docs", "billing": "billing", "escalation": "escalation"})

Ship your next agent in minutes, not weeks.

MIT licensed. Drop-in for LangGraph. Native SDKs in 5 languages. Battle-tested resilience primitives in the box.