Multi-Agent Coordination Patterns: Beyond the God Agent
In our previous posts, we mastered the single agent. We gave it tools (see our tool calling guide), memory, and logic (see our agent architecture guide). But as your use cases grow in complexity—like building a software feature from scratch or automating a legal discovery process—a single brain isn't enough.
You wouldn't hire one person to be your Researcher, your Writer, and your Editor. You would hire a team.
This post is for engineers ready to move from "God Prompts" (one prompt doing everything) to Multi-Agent Systems. We will explore the three fundamental patterns of coordination that allow specialized agents to solve complex problems together: The Chain, The Router, and The Supervisor.
The problem: The "God Agent" bottleneck
Let's say you want to build an agent that "Writes a data-driven market analysis report."
If you try to shove this all into one prompt, you run into the God Agent Fallacy.
- Context Clutter: Instructions for "maintaining a professional tone" (Writing) distract the model while it is trying to "calculate year-over-year growth" (Analysis).
- Tool Confusion: The agent has access to 20 tools. It frequently tries to use the
Calculatortool to write the blog post title. - Debugging Nightmares: When the output is bad, is it because the research was bad, or the writing was bad? You can't isolate the failure.
The Solution: Split the brain. Create specialized agents. But once you split them, you have a new engineering problem: Coordination.
Pattern 1: The Chain (Sequential handoffs)
This is the simplest pattern. It is a "bucket brigade." Agent A does work and passes the entire result to Agent B.
Use Case: Competitor Analysis Pipeline
- Researcher Agent: Scrapes raw data.
- Analyst Agent: Summarizes trends from that data.
- Writer Agent: Formats those trends into a PDF.
graph LR
A[Input: Apple vs Microsoft] --> B(Agent 1: Researcher)
B --> C(Agent 2: Analyst)
C --> D(Agent 3: Writer)
D --> E[Final Report PDF]
style B fill:#e3f2fd,stroke:#0d47a1
style C fill:#e8f5e9,stroke:#388e3c
style D fill:#fff3e0,stroke:#e65100
The engineering challenge: The interface
The "Chain" seems easy, but the failure point is the Interface between agents. If the Researcher dumps 10 pages of unstructured text, the Analyst (with a limited context window) will choke.
Best Practice: Enforce Structured Output between links in the chain.
# Define the Interface (Pydantic)
class ResearchOutput(BaseModel):
company_name: str
revenue_data: dict
recent_news: List[str]
# Agent 1 (Researcher) MUST output this schema
research_result = researcher_agent.invoke({"company": "Apple"})
# research_result is now a clean, validated object
# Agent 2 (Analyst) consumes that object
analysis_result = analyst_agent.invoke({"data": research_result.model_dump()})
Observation: By forcing a schema between agents, you decouple them. You can upgrade the Researcher agent completely, and as long as it outputs the same JSON schema, the Analyst agent won't break.
Pattern 2: The Router (Specialist routing)
Here, a "Front Desk" agent decides who handles the request. This is not a chain; it's a fork in the road.
Use Case: Medical Triage System
A user asks a question. The Triage Agent analyzes the intent and routes it to a specialist.
graph TD
A[User Query] --> B(Triage Agent: The Router)
B -- "My tooth hurts" --> C[Dentist Agent]
B -- "I have a rash" --> D[Dermatologist Agent]
B -- "What is my copay?" --> E[Billing Agent]
C --> F[Final Advice]
D --> F
E --> F
style B fill:#f3e5f5,stroke:#7b1fa2
The engineering challenge: Classification accuracy
The Router doesn't do work; it classifies. If the router fails, the wrong expert gets the job.
Best Practice: Use constrained decoding (or Tool Calling) for the router. Don't let it output text like "I think this should go to the dentist." Force it to output a classification ID.
class TriageDecision(BaseModel):
department: Literal["dentist", "dermatologist", "billing"]
urgency: int
# The Router only sees this tool
def route_query(query):
classification = llm.with_structured_output(TriageDecision).invoke(query)
if classification.department == "dentist":
return dentist_agent.invoke(query)
elif classification.department == "dermatologist":
return dermatologist_agent.invoke(query)
Why this wins: It reduces Hallucination. The Dermatologist Agent doesn't even have the "Tooth Extraction" tool in its prompt. It literally cannot make a mistake about teeth because it doesn't know teeth exist.
Pattern 3: The Supervisor (Hierarchical state)
This is the most complex but powerful pattern. It models a team with a Manager.
The Manager Agent maintains the "Global State" (the plan) and assigns sub-tasks to workers. The workers report back, and the Manager decides what to do next. This allows for Loops and Replanning.
Use Case: Complex Travel Planner
- Manager -> Flight Agent: "Find flights to Tokyo."
- Flight Agent -> Manager: "Here are 3 flights. They arrive at 10 PM."
- Manager -> Logic Check: "10 PM is late. I need to check hotel check-in times."
- Manager -> Hotel Agent: "Find hotels near Narita Airport with 24hr check-in."
graph TD
A[Manager Agent: Holds the Plan] --> B{Decision Loop}
B -- "Task: Flights" --> C[Flight Agent]
B -- "Task: Hotels" --> D[Hotel Agent]
C --> A
D --> A
A -- "Plan Complete" --> E[Final Itinerary]
style A fill:#e8f5e9,stroke:#388e3c
The engineering challenge: The "Shared State"
In a chain, data flows A -> B. In a Supervisor pattern, data flows A -> Manager -> B -> Manager.
The Manager needs a "Clipboard" (State) to keep track of everything.
Best Practice (LangGraph Style):
class TravelState(TypedDict):
destination: str
flights: List[Flight]
hotels: List[Hotel]
# The Manager updates 'next_step' to control the loop
next_step: str
def manager_node(state: TravelState):
# The Manager looks at the state and decides what is missing
if not state['flights']:
return "call_flight_agent"
if not state['hotels']:
return "call_hotel_agent"
return "finish"
Observation: The worker agents are stateless. They don't know the whole plan. They just get a query ("Find hotels near Narita"), do the job, and return data. The Manager is the only one who sees the big picture.
Summary: Choosing your architecture
| Pattern | Complexity | Best For | Example |
|---|---|---|---|
| The Chain | Low | Pipelines: Data transformation, Content creation | Research -> Write -> Edit |
| The Router | Medium | Classification: Handling diverse types of user inputs | Support Bot Triage |
| The Supervisor | High | Complex Projects: Tasks where step 2 depends on the result of step 1 | Coding Agent, Travel Planner |
Challenge for you
Scenario: You are building a "Software Development Team" of agents.
- Agents:
Coder,Reviewer,Tester. - Flow:
- The
Coderwrites code. - The
Reviewerchecks style. - The
Testerruns unit tests.
- The
- The Twist: If the
Testerfinds a bug, the code must go back to theCoderto be fixed.
Your Task:
- Draw the coordination diagram.
- Is this a Chain? A Router? Or a Supervisor loop?
- Hint: Who decides when the code is "done"? Is it the Tester, or a Manager?
Key takeaways
- The Chain pattern is for pipelines: Sequential handoffs work best when each agent transforms data in a clear, linear flow
- The Router pattern is for classification: Use routing when you need to direct different types of requests to specialized agents
- The Supervisor pattern enables loops and replanning: The manager maintains global state and can send work back to agents for iteration
- Structured interfaces prevent coupling: Using Pydantic schemas between agents decouples them and makes upgrades easier
- Constrained routing reduces errors: Forcing classification decisions into structured outputs prevents hallucination and routing mistakes
- State management is critical for supervisors: The manager needs a shared state object to coordinate multiple agents and handle complex workflows
For more on building multi-agent systems, see our agent framework comparison and our agent architecture guide.
For more on building production AI systems, check out our AI Bootcamp for Software Engineers.