Agent Orchestration Patterns
- ▸Know the four core agent orchestration patterns
- ▸Choose the right pattern for your use case
- ▸Compose agents with plain Python — no framework required
One agent rarely covers a real production system. A customer support system might need one agent for billing, another for technical support, and a third for account management. A research pipeline might need a researcher agent feeding into a writer agent. These four patterns cover the vast majority of multi-agent architectures you will encounter.

Pattern 1 — Single Agent
The simplest pattern: one agent, many tools. The agent decides which tools to call based on the user's question. Handles most use cases that do not require specialist knowledge or parallel execution. Start here and only add complexity when you hit a real limitation.
agent = create_react_agent(
model=model,
tools=[search_web, query_database, send_email, read_file],
)
# The agent autonomously decides which tools to call
result = agent.invoke({'messages': [HumanMessage(content='Summarise today's sales and email the report to the team.')]})
Pattern 2 — Sequential
Two or more agents run in sequence: the output of the first becomes the input of the second. Classic use case: a researcher agent gathers information, then a writer agent turns it into a polished document. Each agent has a clear, focused role.
from langchain_core.messages import HumanMessage
researcher = create_react_agent(model=model, tools=[search_web, read_arxiv])
writer = create_react_agent(model=writer_model, tools=[check_grammar])
# Step 1: Researcher gathers facts
research_result = researcher.invoke({
'messages': [HumanMessage(content='Research the latest advances in quantum computing.')]
})
research_text = research_result['messages'][-1].content
# Step 2: Writer turns facts into an article
final_result = writer.invoke({
'messages': [HumanMessage(
content=f'Write a blog post based on this research:\n\n{research_text}'
)]
})
print(final_result['messages'][-1].content)
Pattern 3 — Parallel
Run multiple independent agents concurrently with asyncio.gather() and merge their outputs. Use this when tasks are independent and latency matters — three agents running in parallel take the same time as one, not three times longer.
import asyncio
from langchain_core.messages import HumanMessage
weather_agent = create_react_agent(model=model, tools=[get_weather])
news_agent = create_react_agent(model=model, tools=[search_news])
stocks_agent = create_react_agent(model=model, tools=[get_stock_price])
async def morning_briefing(city: str, topic: str, stock: str) -> str:
# All three run in parallel — total time = max(weather, news, stocks)
weather, news, stocks = await asyncio.gather(
weather_agent.ainvoke({'messages': [HumanMessage(content=f'Weather in {city}?')]}),
news_agent.ainvoke({'messages': [HumanMessage(content=f'Top news about {topic}?')]}),
stocks_agent.ainvoke({'messages': [HumanMessage(content=f'Price of {stock}?')]}),
)
return '\n\n'.join([
weather['messages'][-1].content,
news['messages'][-1].content,
stocks['messages'][-1].content,
])
result = asyncio.run(morning_briefing('London', 'AI', 'NVDA'))
print(result)
Pattern 4 — Router / Supervisor
A supervisor agent classifies the incoming request and routes it to the appropriate specialist. The specialist agents are wrapped as @tool functions — the supervisor calls them like any other tool. This pattern scales cleanly to many specialists without growing the supervisor's prompt.
from langchain_core.tools import tool
billing_agent = create_react_agent(model=model, tools=[get_invoice, process_refund])
tech_agent = create_react_agent(model=model, tools=[check_system_status, run_diagnostics])
@tool
def route_to_billing(query: str) -> str:
"""Handle billing, payment, and invoice questions."""
result = billing_agent.invoke({'messages': [HumanMessage(content=query)]})
return result['messages'][-1].content
@tool
def route_to_tech_support(query: str) -> str:
"""Handle technical issues, bugs, and system problems."""
result = tech_agent.invoke({'messages': [HumanMessage(content=query)]})
return result['messages'][-1].content
# Supervisor routes to the right specialist
supervisor = create_react_agent(
model=model,
tools=[route_to_billing, route_to_tech_support],
prompt='Route customer queries to the appropriate specialist.',
)Choosing the right pattern
- •Simple chatbots and assistants
- •Tasks that fit in one context window
- •Start here — add patterns only when needed
- •Research → Write → Review pipelines
- •Data transformation workflows
- •Output of A must feed input of B
- •Independent tasks with latency budget
- •Dashboard aggregation from multiple sources
- •Tasks must not depend on each other
- •Many specialist domains to handle
- •Customer support with multiple teams
- •Clean separation of specialist prompts
- ✓Four patterns cover almost every multi-agent architecture: Single, Sequential, Parallel, Router
- ✓Start with Single and add patterns only when you hit a real scaling or complexity limit
- ✓Parallel uses asyncio.gather() — tasks must be independent of each other
- ✓Router wraps specialist agents as @tool functions for a clean supervisor interface