Module 2: The supervisor/router pattern

Module 2: The supervisor/router pattern

Description

Module 1 left you with a criterion, not any built mechanism: you defined what a multi-agent system is, measured the real cost of coordinating (model calls, hops), and confirmed — with numbers, not intuition — when that cost is justified. The previous module's central measurement (lesson 05) was a supervisor that only had one possible specialist to delegate to — booking_agent — so its only real decision was "do I delegate, or not?". This module builds what that supervisor still didn't know how to do: choose among several specialists, reading the request to decide which of the three — booking_agent, policy_agent, pricing_agent — should solve it.

This is the first of the five patterns lesson 07 of Module 1 previewed. Supervisor/router: a central coordinator that receives the request, decides — by rules or by the model — which specialist to delegate to, dispatches the task, and aggregates the result before answering. You'll build both ways of making that decision — deterministic, with keyword rules that actually run, and model-decided, concept with a realistic example — and measure when each is enough. The module's centerpiece (lesson 06) is a complete dispatcher, run end to end, routing three different requests to each one's correct agent.

Hard rule of this module (read it before continuing)

We keep exactly the same line as Module 1: each agent's decision — which tool to call, what to answer — is not executed. It's a hand-written script, labeled as concept (claude-sonnet-5), never a real API call. What is actually executed, for real, with Python 3.14 and its standard library, is the complete orchestration: each agent's runner (reused unchanged from agent-fundamentals M4/M5), the deterministic router with its rules, the message passing between supervisor and specialist, and the end-to-end dispatcher. The only decision that IS actually executed for real in this module is, precisely, the deterministic router's — it isn't a model decision, it's a Python function comparing keywords, so it doesn't violate the rule: there's no LLM behind those lines.


Where we are in the ecosystem

agent-fundamentals-and-tool-calling-guide (already complete)
  -> built ONE agent: tools, protocol, the loop, multi-tool, memory, robustness

multi-agent-orchestration-guide (this guide)
├── Module 1: Why multi-agent (and when not to)  (complete)
│   → The decision criterion, measured with real numbers
├── Module 2: The supervisor/router pattern  ← YOU ARE HERE
│   → The first pattern: deterministic routing vs. model-decided routing
├── Module 3: Sequential pipelines
├── Module 4: Parallel fan-out and aggregation
├── Module 5: Handoff and delegation
├── Module 6: Shared state and the blackboard pattern
├── Module 7: Orchestrating Reservo's full system
└── Module 8: Project — Reservo's multi-agent system

This module doesn't re-explain the definition of a multi-agent system, or the cost of coordinating, or Module 1 lesson 06's three signals — those are assumed mastered. What's new here is exclusively the decision mechanism: how the supervisor knows, for a request it's never seen before, which of the three specialists to send it to.


Analogy: the front desk of an office building

Picture the front desk of a building with three departments: bookings, policy support, and rate comparison. A visitor arrives and needs someone to point them to the right floor before they can get done what they came to do.

There are two ways for the front desk to work. The first is a sign with fixed rules: "if your question includes the word 'booking,' third floor; if it includes 'policy,' fifth floor; if it includes 'compare,' seventh floor." It's fast, free, and works perfectly for the 80% of visitors who arrive with a clear question. But a visitor who says "I need to cancel because I won't be able to come tomorrow, will I be charged anything?" confuses the sign: they mention "cancel," which sounds like bookings, but they actually want to know about a charge — a policy question. The sign, which only reads loose words, sends them to the wrong floor with total confidence.

The second way is a human receptionist who listens to the whole sentence, understands the real intent — not just the words that appear — and directs people to the right floor even in cases the sign never anticipated. It's slower and, in a real system, costs more (you pay them a salary; here, it consumes a model call). The question that opens this module is exactly that: when is the sign enough, and when is the receptionist needed? You'll build both, and measure the difference with the same ambiguous request that confuses the sign.


The case running through the module: Reservo's supervisor

agent-fundamentals built Reservo's four canonical tools. Module 1 of this guide split them across three specialists — reused with no substantive change:

  • booking_agent — the four canonical tools (list_rooms, get_quote, book_room, cancel_booking). Quotes, books, cancels.
  • policy_agentsearch_docs(query) -> str, the minimal keyword stub you already built in Module 1, lesson 06 (no-show-policy, cancellation-policy).
  • pricing_agent — Module 1 named it and used get_quote without wrapping it in a complete agent. This module builds it, executed, as a real agent — with its own runner running over its own script, not just a loose call to get_quote — in lesson 05.

What's new in this module is the coordinator: a supervisor that receives a raw request from the member, decides which of the three specialists should solve it, dispatches it, and composes the final answer. You'll build that decision two ways — deterministic (lesson 03) and model-decided (lesson 04) — and see them work together, over the three complete specialists, in lesson 06's dispatcher.


Boundary with what's coming (and with what you've already seen)

One request at a time, one specialist at a time — that's this module's scope. Two things this module deliberately does not cover, because they have their own dedicated module:

  • When the task ALWAYS needs the same steps, in the same order, with no routing decision — for example, quote, then validate the cancellation policy, then confirm the booking, always in that order, no matter what the member asked for — that's a pipeline, and you'll see it in Module 3. The underlying difference: a supervisor decides who to delegate to, by reading the request; a pipeline decides nothing, the order is fixed in advance.
  • When two independent sub-tasks get dispatched at the same time, in real parallel, and their results get aggregated — like "quote Focus pro 3h and tell me the cancellation policy" from Module 1, lesson 06 — that's fan-out, and Module 4 develops it in depth. This module sticks to the case of one decision, one specialist chosen at a time.

With that clear, you can now tell the three patterns apart without confusing them: supervisor decides who works (this module); pipeline fixes the order without deciding anything (M3); fan-out splits simultaneous work among several you already know you need (M4).


Prerequisites

Required knowledge:

  • ✅ This guide's Module 1, complete: the definition of a multi-agent system, the measured cost of coordinating, the three signals that justify multi-agent, and the map of the five patterns.
  • agent-fundamentals-and-tool-calling-guide: a tool's contract, the tool_use/tool_result protocol, run_agent_parallel, and dispatch_parallel.

Recommended:

  • ✅ Having run Module 1 lesson 05's executed comparison yourself — that single-specialist supervisor's number of calls and hops is the baseline this module compares the deterministic routing cost against.

NOT required:

  • ❌ You don't need an API key or an internet connection: each agent's decision is still concept, hand written.
  • ❌ You don't need LangChain, LangGraph, CrewAI, or any orchestration framework.

Environment:

  • Python 3.14.0 with its standard library. Nothing to install.

Module roadmap

Lesson 01 — Module introduction (this one)

The supervisor/router pattern, the front-desk analogy, and the boundary with pipeline (M3) and fan-out (M4).

Lesson 02 — What a supervisor does

The complete anatomy: receive, decide, dispatch, aggregate. The SPECIALISTS registry with the three agents and the run_specialist wrapper that wraps the runner without modifying it.

Lesson 03 — Deterministic routing with rules

A keyword router, executed, over the three single-intent requests. Where the deterministic router gets it right and where it reaches its limit.

Lesson 04 — Routing by model decision

The same ambiguous request the deterministic router failed on, solved by model decision (concept), with the decision extraction actually executed.

Lesson 05 — Building pricing_agent

The third specialist, run for the first time as a complete agent: three quotes in the same turn, compared.

Lesson 06 — The dispatcher executed end to end

The module's centerpiece: the three requests, routed and dispatched to the three specialists, with each path's cost measured.

Lesson 07 — Choosing your router

Deterministic vs. model, compared with numbers; a hybrid router combining the two, with its real limits, not idealized ones.

Lesson 08 — Mini-project: Reservo's supervisor

Five new scenarios, routed and dispatched with the complete hybrid router.

Progression map

Lesson 01 (this one) → The pattern, the analogy, the boundary with M3/M4
Lesson 02             → The supervisor's anatomy: receive, decide, dispatch, aggregate
Lesson 03             → Deterministic routing, executed
Lesson 04             → Model-decided routing, concept
Lesson 05             → pricing_agent, built and run
Lesson 06             → The complete dispatcher, run (the dense one)
Lesson 07             → Deterministic vs. model, the hybrid router
Lesson 08             → Project: Reservo's complete supervisor

Difficulty: ⭐⭐ ──────────────────▶ ⭐⭐⭐

What you'll achieve in this module

By completing the 8 lessons, you'll be able to:

  1. Build a complete supervisor that receives a request, decides who to delegate to, dispatches, and aggregates — Module 1's formal definition's four pieces, this time implemented.
  2. Write a deterministic keyword router, and recognize exactly where it reaches its limit: incomplete coverage and confident wrongness.
  3. Recognize when the model's decision is needed to route, and extract that decision from a turn in an executed way, without violating the rule that the decision itself is concept.
  4. Build a new specialist (pricing_agent) from scratch, reusing the same runner without modifying it.
  5. Run a complete dispatcher across three specialists and cite, with real numbers, the cost of coordinating each path.
  6. Design a hybrid router that combines rules and model decision, and explain why that hybrid doesn't solve every case — only the ones the rules recognize they can't solve.

Before and after

BEFORE the module:
→ "Routing" is simply "call the model and let it decide"
→ Keyword rules are an unserious shortcut, not a real pattern
→ A router that works in tests always works
→ The supervisor is just an extra step with no logic of its own

AFTER the module:
→ Deterministic routing is FREE (0 model calls) and great for bounded vocabularies
→ Model-decided routing costs a real call -- and sometimes it's worth paying
→ A deterministic router can fail CONFIDENTLY, not just return "I don't know" -- a rule that matches wrong never triggers a simple fallback
→ The supervisor has a four-step anatomy: receive, decide, dispatch, aggregate

Traps to avoid while taking this module

1. "Deterministic routing is a beginner's trick, model decision is always better"

No. Lesson 03 measures 100% coverage over clearly-intentioned requests, at zero cost. Lesson 07 shows the real cost of routing EVERYTHING with the model, at different daily volumes — the savings from rules, when they're enough, aren't negligible.

2. "If the deterministic router doesn't fail in my tests, it never fails"

This is lesson 03's central trap: a rule can match with total confidence and be wrong — not just return None. Lesson 07 shows in detail why that means a naive fallback ("if it's None, ask the model") isn't enough for every case.

3. "This module already builds Reservo's complete system"

Not yet. This module sticks to one request, one specialist chosen at a time. Compound requests that need two specialists at once are fan-out (Module 4); combining everything into a real run is Module 7.

4. "pricing_agent was already complete since Module 1"

No. Module 1 used get_quote directly, as a loose call inside the decision criterion. This module's lesson 05 builds it for the first time as a real agent, with its own runner and its own script.


How to work through this module

  1. Run lesson 06 yourself. It's the piece that holds up the whole module — the complete dispatcher, with the three requests routed to the correct agent and their cost cited.
  2. Pay attention to the cases where the deterministic router fails. They're not rare cases set up to scare you: they're the exact kind of ambiguity a real vocabulary produces all the time.
  3. The mini-project is the synthesis. Lesson 08 gives you new scenarios with the complete hybrid router — practicing it before Module 3 makes the next pattern feel like a natural extension, not a new topic.

Estimated time:

Lesson 01 (this one) →  15 min reading
Lesson 02             →  20 min + running the demo
Lesson 03             →  25 min + running the demo
Lesson 04             →  25 min + running the demo
Lesson 05             →  20 min + running the demo
Lesson 06             →  35 min + running the demo (the densest one in the module)
Lesson 07             →  25 min + running the demo
Lesson 08             →  30 min + applying the complete router

Total: ~3.3 hours

Evidence of success

Before moving on to Module 3 (Sequential pipelines), you should be able to:

  • Explain a supervisor's four pieces — receive, decide, dispatch, aggregate — in your own words.
  • Write a deterministic keyword router and recognize, with your own example, where it fails confidently instead of returning None.
  • Cite from memory lesson 06's dispatcher result: how many model calls each of the three requests cost, and why deterministic routing saved a call compared to Module 1's single-specialist supervisor.
  • Distinguish supervisor/router from pipeline and from fan-out, without confusing them.
  • Design a hybrid router, and explain in which concrete cases its fallback isn't enough.

Summary

  • This module builds the first pattern of the five Module 1 previewed: supervisor/router — a coordinator that receives the request, decides who to delegate to, dispatches, and aggregates.
  • Hard rule: each agent's decision is still concept (claude-sonnet-5); the deterministic router, each specialist's runner, the message passing, and the complete dispatcher are actually executed with Python 3.14.
  • The case is Reservo's three specialists — booking_agent and policy_agent reused unchanged from Module 1, pricing_agent built for the first time as a complete agent in lesson 05.
  • Clear boundary with what follows: an ALWAYS-fixed order, with no routing decision, is pipeline (M3); independent sub-tasks dispatched in parallel is fan-out (M4). This module sticks to one decision, one specialist chosen at a time.

Next lesson: 02 — What a supervisor does. We build the complete anatomy — receive, decide, dispatch, aggregate — with the first example run end to end.


Additional resources

  1. Anthropic — Building effective agents — The "routing" pattern described there (classifying an input and directing it to a specialized flow) is, under different vocabulary, exactly this module's supervisor/router.
  2. Anthropic — Multi-agent research system — A real orchestrator deciding which sub-agent to delegate each part of a research task to.
  3. Anthropic — Tool use (function calling) overview — The protocol every specialist in this module keeps following, unchanged, inside its own loop.
  4. Python 3.14 — What's New — The version this module's entire orchestration runs on.