Multi-Agent Systems in Practice: How Multiple AI Agents Collaborate
Contents
When Multi-Agent Is Needed
Not every scenario needs multiple Agents.
Single Agent handles:
- task boundaries clear
- needs centralized decision
- tool call chain not long
Consider Multi-Agent:
- task needs different expertise
- needs parallel processing of independent subtasks
- decisions need multi-party verification
- system scale needs responsibility separationCommon Architectures
1. Supervisor + Workers
Supervisor Agent
├── Worker Agent A (handles domain)
├── Worker Agent B (handles another domain)
└── Worker Agent C (handles third domain)# Supervisor handles task decomposition and coordination
class Supervisor:
def handle(self, task):
subtasks = self.decompose(task)
results = []
for subtask in subtasks:
worker = self.select_worker(subtask)
result = worker.execute(subtask)
results.append(result)
return self.synthesize(results)Good for: tasks decomposable, subtasks relatively independent.
2. Sequential Pipeline
Agent A → Agent B → Agent C → Agent D# Pipeline style, each step depends on previous output
pipeline = [
("research", ResearchAgent()),
("analyze", AnalyzeAgent()),
("write", WriteAgent()),
("review", ReviewAgent()),
]
def run_pipeline(initial_input):
output = initial_input
for name, agent in pipeline:
output = agent.process(output)
return outputGood for: ordered steps, each with clear input/output.
3. Parallel + Merge
→ Agent A (parallel)
Task → → Agent B (parallel) → Merge Agent → Result
→ Agent C (parallel)# Same task, multiple perspectives, merge results
async def parallel_analyze(task):
agents = [ResearchAgent(), AnalyzeAgent(), CritiqueAgent()]
results = await asyncio.gather(*[
agent.process(task) for agent in agents
])
return merge_agent.synthesize(results)Good for: multi-perspective analysis, synthesizing different viewpoints.
Communication Protocols
Multi-Agent needs communication. Two approaches:
1. Shared Message Queue
# Communicate via shared queue
class Agent:
def __init__(self, name, inbox, outbox):
self.name = name
self.inbox = inbox
self.outbox = outbox
async def run(self):
while True:
msg = await self.inbox.get()
result = await self.process(msg)
await self.outbox.put(Message(
from_agent=self.name,
to="merge",
content=result
))2. Explicit Message Passing
# Agents send messages directly
class Agent:
async def send_to(self, target, message):
await self.messenger.send(
to=target,
message=message
)
async def receive(self):
return await self.messenger.receive()Pitfalls Encountered
Pit 1: Information Loss Between Agents
# Problem: Agent A's output, Agent B can't understand
# Solution: standardize output format
class ResearchAgent:
def output_format(self):
return {
"findings": [...],
"sources": [...],
"confidence": 0.8,
"raw_data": {...}
}Pit 2: Circular Dependencies
Agent A → Agent B → Agent C
↑ ↓
└─────────────────────┘
# Agent C depends on A
# Agent A also indirectly depends on C# Solution: organize with DAG
# Or introduce arbitration mechanism
class Orchestrator:
def resolve_conflict(self, result_a, result_c):
if result_a.confidence > result_c.confidence:
return result_a
return result_cPit 3: Debugging Difficulty
# Problem: when Multi-Agent breaks, hard to locate which Agent
# Solution: each Agent has complete logs, message queue replayable
class Agent:
def process(self, msg):
logger.info(f"{self.name} received: {msg}")
result = self._process_impl(msg)
logger.info(f"{self.name} output: {result}")
return resultFramework Selection
| Framework | Use Case | Characteristics |
|---|---|---|
| LangGraph | complex flow orchestration | strong state management |
| AutoGen | multi-Agent dialogue | rich collaboration modes |
| CrewAI | clear role separation | easy to start |
| Custom | highly customized | flexible but high effort |
Conclusion
Multi-Agent’s value: let specialists do specialized work.
Multiple Agents each have strengths, combined they handle more complex tasks than single Agent.
But Multi-Agent adds complexity: communication, debugging, consistency all cost extra.
Recommendation: start with single Agent, split when confirmed insufficient. Don’t overdesign.