Multi-Agent Orchestration

Hard 35 min read

Orchestration Patterns

Why Multi-Agent Orchestration Matters

The Problem: Complex tasks often exceed a single agent's capabilities. You need multiple specialized agents working together, but coordinating them introduces challenges around communication, state sharing, and conflict resolution.

The Solution: Orchestration patterns provide proven architectures for coordinating multiple agents -- from simple supervisor hierarchies to dynamic swarm topologies with peer-to-peer communication.

Real Impact: Companies like Salesforce and Microsoft use multi-agent orchestration to power enterprise automation workflows that handle thousands of concurrent tasks.

Real-World Analogy

Think of multi-agent orchestration like an orchestra performance:

  • Supervisor Pattern = Conductor directing each section when to play
  • Peer-to-Peer = Jazz ensemble where musicians listen and respond to each other
  • Shared State = The musical score that all musicians reference
  • Message Passing = Musical cues passed between sections
  • Conflict Resolution = The conductor resolving when two sections clash

Key Orchestration Patterns

Supervisor Pattern

A central manager agent delegates tasks, reviews outputs, and coordinates the workflow. Best for structured, hierarchical processes.

Swarm Architecture

Agents self-organize and dynamically hand off tasks based on capability matching. Best for flexible, emergent workflows.

Pipeline Pattern

Agents are arranged in a linear chain where each agent transforms and passes data to the next. Best for sequential processing.

Broadcast Pattern

One message is sent to all agents simultaneously, and results are aggregated. Best for parallel analysis from multiple perspectives.

Supervisor Pattern

Multi-Agent Orchestration Patterns
Supervisor Supervisor Agent A Agent B Agent C Swarm Agent 1 Agent 2 Agent 3 Pipeline Stage 1 Stage 2 Stage 3 Broadcast Router A B C
supervisor_pattern.py
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated, Literal
import operator

class OrchestratorState(TypedDict):
    messages: Annotated[list, operator.add]
    next_agent: str
    results: dict

def supervisor(state: OrchestratorState):
    """Central supervisor that routes tasks to agents."""
    llm = ChatOpenAI(model="gpt-4")
    response = llm.invoke(
        f"""You are a supervisor managing agents: researcher, writer, reviewer.
        Based on the current state, decide which agent should act next.
        Respond with just the agent name or 'FINISH' if done.
        Current results: {state['results']}"""
    )
    return {"next_agent": response.content.strip()}

def researcher_agent(state):
    # Research logic here
    return {"results": {"research": "findings..."}}

def writer_agent(state):
    # Writing logic here
    return {"results": {"draft": "content..."}}

# Build orchestration graph
graph = StateGraph(OrchestratorState)
graph.add_node("supervisor", supervisor)
graph.add_node("researcher", researcher_agent)
graph.add_node("writer", writer_agent)

graph.set_entry_point("supervisor")
graph.add_conditional_edges("supervisor", lambda s: s["next_agent"], {
    "researcher": "researcher",
    "writer": "writer",
    "FINISH": END,
})
graph.add_edge("researcher", "supervisor")
graph.add_edge("writer", "supervisor")

Swarm Architecture

Swarm vs Supervisor

  • Swarm: Agents hand off to each other based on capability. No central coordinator. More flexible but harder to debug.
  • Supervisor: One agent controls the flow. Predictable but can become a bottleneck.
  • Hybrid: Supervisor for high-level routing, swarm within sub-teams for flexibility.

Communication Protocols

message_passing.py
# Shared state approach
class SharedState(TypedDict):
    task_queue: list[str]
    completed: list[dict]
    agent_status: dict[str, str]

# Message passing approach
class AgentMessage:
    sender: str
    receiver: str
    content: str
    message_type: str  # "request", "response", "handoff"

# Task routing based on capability
def route_task(task: str, agents: list) -> str:
    """Route task to the most capable agent."""
    capabilities = {
        "researcher": ["search", "analyze", "summarize"],
        "coder": ["code", "debug", "test"],
        "writer": ["write", "edit", "format"],
    }
    for agent, skills in capabilities.items():
        if any(s in task.lower() for s in skills):
            return agent
    return "researcher"  # default

Conflict Resolution

Common Pitfall

Problem: Agents produce contradictory outputs or get stuck in delegation loops.

Solution: Implement a maximum delegation depth, use a voting mechanism for conflicting outputs, and add a timeout that escalates to the supervisor when agents cannot reach agreement.

Quick Reference

PatternBest ForTrade-offs
SupervisorStructured workflowsPredictable but single point of failure
SwarmDynamic tasksFlexible but harder to debug
PipelineSequential processingSimple but no parallelism
BroadcastParallel analysisFast but expensive (N agents)
HybridComplex systemsBest of both but more complexity
Shared StateTight coordinationEasy data access but race conditions
Message PassingLoose couplingClean interfaces but more overhead