Multi-Agent System Example¶
Learn how to build sophisticated systems by coordinating specialized agents - each with different capabilities, tools, and expertise working together in workflows.
What This Example Demonstrates¶
- Agent Specialization: Define agents with different tools and instructions
- Sequential Workflows: Chain agents together (agent1 → agent2 → agent3)
- Context Passing: Agents build on previous agents' work
- Real-World Architectures: Patterns for production multi-agent systems
Time to Complete: 30 minutes
Complexity: ⭐⭐⭐ Advanced
Running the Example¶
# Clone the repository
git clone https://github.com/microsoft/amplifier-foundation
cd amplifier-foundation
# Set your API key
export ANTHROPIC_API_KEY='your-key-here'
# Run the example (shows architect → implementer → reviewer workflow)
uv run python examples/09_multi_agent_system.py
How It Works¶
Multi-agent systems solve complex problems by dividing work among specialized agents.
Agent Specialization¶
Each agent has: - Different tools - Architect reads docs, Implementer writes code - Different instructions - Each agent has specific expertise - Different configuration - Can use different models or settings
def create_architect_agent(provider: Bundle) -> Bundle:
"""Designs system architecture and specifications"""
return Bundle(
name="architect",
tools=[{"module": "tool-filesystem", "source": "..."}],
instruction="""You are a Software Architect expert.
Your role:
- Design system architectures and specifications
- Break down complex problems into modules
- Define clear interfaces and contracts
- Consider scalability, maintainability, and best practices
When given a task:
1. Analyze the requirements
2. Design the architecture (components, interfaces, data flow)
3. Create a specification document
4. Return clear instructions for implementation
Focus on design, not implementation. Be thorough but concise."""
).compose(provider)
def create_implementer_agent(provider: Bundle) -> Bundle:
"""Writes code based on specifications"""
return Bundle(
name="implementer",
tools=[
{"module": "tool-filesystem", "source": "..."},
{"module": "tool-bash", "source": "..."}
],
instruction="""You are a Software Implementation expert.
Your role:
- Implement code based on specifications
- Write clean, tested, documented code
- Follow the architecture and contracts provided
- Run tests to verify correctness
When given a specification:
1. Understand the requirements
2. Implement the code
3. Write tests
4. Verify it works
5. Document the implementation
Focus on clean, working code that matches the spec exactly."""
).compose(provider)
def create_reviewer_agent(provider: Bundle) -> Bundle:
"""Reviews code for quality, security, and best practices"""
return Bundle(
name="reviewer",
tools=[{"module": "tool-filesystem", "source": "..."}],
instruction="""You are a Code Review expert.
Your role:
- Review code for correctness, security, and best practices
- Identify bugs, vulnerabilities, and improvement opportunities
- Provide actionable feedback
When reviewing code:
1. Check correctness (does it match the spec?)
2. Check security (any vulnerabilities?)
3. Check quality (readable, maintainable?)
4. Check tests (adequate coverage?)
5. Provide specific, actionable feedback
Be thorough but constructive. Focus on important issues."""
).compose(provider)
Sequential Workflow¶
Agents execute in sequence, each building on the previous agent's output:
workflow = [
(
"architect",
"""Design a rate limiting module for API requests.
Requirements:
- Support different rate limit strategies (token bucket, sliding window)
- Thread-safe for concurrent requests
- Configurable limits per endpoint
- Include usage tracking
Provide:
1. Module structure
2. Class definitions and interfaces
3. Key algorithms
4. Example usage""",
architect,
),
(
"implementer",
"""Implement the rate limiting module based on the architect's design.
Create:
1. rate_limiter.py with all classes
2. A simple test to verify it works
3. Brief usage example
Keep it simple and focused.""",
implementer,
),
(
"reviewer",
"""Review the rate limiter implementation.
Check:
1. Does it match the architecture?
2. Are there any bugs or edge cases?
3. Is it thread-safe?
4. Is the code readable and maintainable?
Provide specific feedback for improvement.""",
reviewer,
),
]
# Execute workflow
results = await system.execute_workflow(task, workflow)
Context Passing¶
Each agent receives: 1. The original task description 2. All previous agents' outputs 3. Its specific instruction
def _format_context(self, context: dict[str, Any], previous_results: dict[str, str]) -> str:
"""Format context from previous agents for the current agent."""
if not previous_results:
return f"Task: {context['task']}"
sections = [f"Task: {context['task']}", "\nPrevious work:"]
for agent_name, result in previous_results.items():
sections.append(f"\n{agent_name.upper()} OUTPUT:")
sections.append(result[:500] + "..." if len(result) > 500 else result)
return "\n".join(sections)
Real-World Use Cases¶
Software Development¶
Each specialist focuses on their domain expertise.
Data Analysis¶
Separation of concerns: gathering, processing, presentation.
Content Creation¶
Research facts, write content, polish for quality.
Customer Support¶
Route by type, attempt resolution, escalate if needed.
Architecture Patterns¶
The Orchestrator Pattern¶
A coordinator manages multiple specialized agents:
class MultiAgentSystem:
"""Orchestrates multiple specialized agents to complete complex tasks."""
def __init__(self, foundation: Bundle, provider: Bundle):
self.foundation = foundation
self.provider = provider
async def execute_workflow(
self,
task: str,
workflow: list[tuple[str, str, Bundle]]
) -> dict[str, Any]:
"""Execute a multi-agent workflow.
Args:
task: The overall task description
workflow: List of (agent_name, instruction, agent_bundle) tuples
Returns:
Dict with results from each agent
"""
results = {}
context = {"task": task}
for agent_name, instruction, agent_bundle in workflow:
# Compose foundation + agent
composed = self.foundation.compose(agent_bundle)
# Prepare and execute
prepared = await composed.prepare()
session = await prepared.create_session()
# Add context from previous agents
context_str = self._format_context(context, results)
full_instruction = f"{context_str}\n\n{instruction}"
# Execute
async with session:
response = await session.execute(full_instruction)
results[agent_name] = response
return results
Parallel Execution¶
For independent tasks, execute agents in parallel:
async def parallel_workflow():
"""Execute multiple independent agents in parallel."""
# Create tasks
tasks = [
analyze_code(code_agent),
check_security(security_agent),
verify_tests(test_agent),
]
# Run in parallel
results = await asyncio.gather(*tasks)
return results
Key Design Principles¶
1. Single Responsibility¶
Each agent should have ONE clear job:
- ✅ Good: "Architect" designs, "Implementer" codes, "Reviewer" reviews
- ❌ Bad: "Developer" does everything
2. Clear Interfaces¶
Define what each agent receives and produces:
# Clear contract
architect_output: Specification
implementer_input: Specification
implementer_output: Implementation
reviewer_input: Implementation
3. Stateless Agents¶
Agents should not maintain state between invocations:
- All context passed explicitly
- Results returned, not stored
- Enables parallel execution
4. Composable¶
Agents should be reusable in different workflows:
# Same agents, different workflows
workflow_1 = [architect, implementer, reviewer]
workflow_2 = [architect, implementer, tester, reviewer]
workflow_3 = [researcher, implementer, documenter]
Example Output¶
============================================================
WORKFLOW: Build a Feature (Design → Implement → Review)
============================================================
============================================================
🤖 Agent: ARCHITECT
============================================================
Instruction: Design a rate limiting module for API requests...
⏳ Preparing agent...
🔄 Executing...
✓ architect completed
Response length: 1847 chars
============================================================
🤖 Agent: IMPLEMENTER
============================================================
Instruction: Implement the rate limiting module based on the architect's design...
⏳ Preparing agent...
🔄 Executing...
✓ implementer completed
Response length: 2341 chars
============================================================
🤖 Agent: REVIEWER
============================================================
Instruction: Review the rate limiter implementation...
⏳ Preparing agent...
🔄 Executing...
✓ reviewer completed
Response length: 982 chars
============================================================
WORKFLOW COMPLETE - SUMMARY
============================================================
ARCHITECT:
Output: 1847 characters
Preview: I'll design a rate limiting module that supports multiple strategies...
IMPLEMENTER:
Output: 2341 characters
Preview: I'll implement the rate limiting module based on the architecture...
REVIEWER:
Output: 982 characters
Preview: The implementation looks solid overall. Here's my detailed review...
Next Steps¶
- Custom Orchestration: Build your own workflow patterns
- Error Handling: Add retry logic and fallbacks
- Monitoring: Track agent performance and costs
- Production Deployment: Scale multi-agent systems
Learn More¶
- Bundle System Deep Dive - How composition works
- Common Patterns - Agent spawning patterns
- Application Guide - Building production apps