In this lesson, you'll learn how to create sophisticated multi-agent systems where specialized agents work together. We'll build the complete PC Builder Pro system with three agents: Alex (triage), Morgan (sales), and Sam (support).
Multi-agent systems allow you to create specialized agents that handle different aspects of customer interaction. This provides several benefits:
Advantages:
Our System Architecture:
Customer → Triage Agent (Alex) → ┬→ Sales Agent (Morgan)
└→ Support Agent (Sam)
Key Components:
The AgentServer
class allows you to host multiple agents on a single port, each with its own route.
from signalwire_agents import AgentServer, AgentBase
# Create the server
server = AgentServer(
host="0.0.0.0",
port=3001,
log_level="info"
)
# Create and register agents
triage_agent = TriageAgent()
server.register(triage_agent, "/")
sales_agent = SalesAgent()
server.register(sales_agent, "/sales")
# Run the server
server.run()
Automatic Features:
/health
Configuration Options:
server = AgentServer(
host="0.0.0.0", # Network interface
port=3001, # TCP port
log_level="debug", # Logging verbosity
auth_user="custom", # Override auto-generated auth
auth_pass="secret" # Override auto-generated password
)
Dynamic configuration allows agents to adapt their behavior based on request parameters. This is crucial for:
def configure_transfer_tools(self, query_params, body_params, headers, agent):
"""
Called for every request before processing
Args:
query_params: URL query parameters (dict)
body_params: POST body parameters (dict)
headers: HTTP headers (dict)
agent: The agent instance to configure
"""
# Access proxy-aware URL building
base_url = agent.get_full_url(include_auth=True)
# Configure agent based on request
if query_params.get('transfer') == 'true':
# This is a transfer call
agent.prompt_add_section(...)
This method intelligently builds URLs that work with:
# Returns the correct URL for the current environment
url = agent.get_full_url(include_auth=True)
# Examples:
# Direct: https://user:pass@yourdomain.com:3001
# Proxy: https://user:pass@proxy.signalwire.com/agent-id
The swml_transfer
skill enables seamless handoffs between agents while preserving context.
agent.add_skill("swml_transfer", {
"tool_name": "transfer_to_specialist",
"description": "Transfer to sales or support specialist",
"parameter_name": "specialist_type",
"parameter_description": "The type of specialist (sales or support)",
"required_fields": {
"user_name": "The customer's name",
"summary": "Summary of the conversation"
},
"transfers": {
"/sales/i": { # Regex pattern
"url": sales_url,
"message": "Transferring to sales...",
"return_message": "Call complete."
}
}
})
Required Fields:
${call_data.field_name}
Transfer Configuration:
In the receiving agent:
"The customer's name is ${call_data.user_name}"
"They were transferred because: ${call_data.summary}"
Let's examine the complete PC Builder Pro system in pc_builder.py
:
class TriageAgent(AgentBase):
def __init__(self):
super().__init__(
name="PC Builder Triage Agent",
route="/", # Root route
host="0.0.0.0",
port=3001
)
# Configure prompt
self._configure_prompt()
# Set voice
self.add_language(
name="English",
code="en-US",
voice="rime.spore" # Energetic voice
)
# Dynamic configuration for transfers
self.set_dynamic_config_callback(self.configure_transfer_tools)
def configure_transfer_tools(self, query_params, body_params, headers, agent):
# Build URLs with proxy detection
sales_url = agent.get_full_url(include_auth=True).rstrip('/') + "/sales?transfer=true"
support_url = agent.get_full_url(include_auth=True).rstrip('/') + "/support?transfer=true"
# Configure transfers
agent.add_skill("swml_transfer", {
"tool_name": "transfer_to_specialist",
"required_fields": {
"user_name": "The customer's name",
"summary": "A comprehensive summary of the conversation"
},
"transfers": {
"/sales/i": {
"url": sales_url,
"message": "Perfect! Let me transfer you to our sales specialist.",
"post_process": True
},
"/support/i": {
"url": support_url,
"message": "I'll connect you with technical support.",
"post_process": True
}
}
})
class SalesAgent(AgentBase):
def __init__(self):
# ... initialization ...
# Dynamic prompt configuration
self.set_dynamic_config_callback(self.configure_dynamic_prompt)
def configure_dynamic_prompt(self, query_params, body_params, headers, agent):
if query_params.get('transfer') == 'true':
# This is a transfer - add context
agent.prompt_add_section(
"Call Transfer Information",
body="This call has been transferred from triage.",
bullets=[
"Customer name: ${call_data.user_name}",
"Transfer reason: ${call_data.summary}",
"Greet them by name and acknowledge the transfer"
]
)
else:
# Direct call - standard greeting
agent.prompt_add_section(
"Initial Greeting",
body="This is a direct call to sales.",
bullets=["Greet warmly", "Ask for name", "Understand needs"]
)
def create_pc_builder_app(host="0.0.0.0", port=3001):
# Create server
server = AgentServer(host=host, port=port)
# Create and register agents
triage = TriageAgent()
server.register(triage, "/")
sales = SalesAgent()
server.register(sales, "/sales")
support = SupportAgent()
server.register(support, "/support")
# Add info endpoint
@server.app.get("/info")
async def info():
return {
"agents": {
"triage": {"endpoint": "/"},
"sales": {"endpoint": "/sales"},
"support": {"endpoint": "/support"}
}
}
return server
# Run the complete system
python tutorial/pc_builder.py
# You'll see:
# Triage Agent (Alex): http://localhost:3001/
# Sales Agent (Morgan): http://localhost:3001/sales
# Support Agent (Sam): http://localhost:3001/support
# Test triage agent
curl http://localhost:3001/
# Test sales agent directly
curl http://localhost:3001/sales
# Test with transfer flag
curl "http://localhost:3001/sales?transfer=true"
Look for these in the logs:
Authentication:
# Set custom credentials
export SWML_AUTH_USER=myuser
export SWML_AUTH_PASS=mypassword
# Or let the system generate them (check logs)
SSL/HTTPS:
SWML_SSL_ENABLED=true \
SWML_SSL_CERT_PATH=/path/to/cert.pem \
SWML_SSL_KEY_PATH=/path/to/key.pem \
python tutorial/pc_builder.py
1. Single Server:
2. Distributed Agents:
3. Lambda/Serverless:
def lambda_handler(event, context):
server = create_pc_builder_app()
return server.run(event, context)
Health Checks:
# Built-in health endpoint
curl http://localhost:3001/health
Logging:
# Set appropriate log level
server = AgentServer(log_level="info") # or "debug" for more detail
Metrics to Track:
You've built a complete multi-agent system! You've mastered:
Core Concepts:
Architecture Patterns:
What's Next?
In the next lesson, you'll learn advanced features including custom SWAIG functions, error handling, and production deployment strategies.
Common Issues:
← Lesson 2: Adding Intelligence with Knowledge Bases | Tutorial Overview | Lesson 4: Advanced Features →