| Duration | 2.5 hours |
| Day | 5 of 5 |
Learning Objectives
By the end of this module, students will be able to:
- Configure agents for production environments
- Deploy with Docker containers
- Set up proper logging and monitoring
- Implement health checks
Topics
1. Production Configuration (30 min)
Environment Variables
# .env.production
SIGNALWIRE_SPACE_NAME=your-space
SIGNALWIRE_PROJECT_ID=your-project-id
SIGNALWIRE_TOKEN=your-token
# Server
HOST=0.0.0.0
PORT=3000
WORKERS=4
# Security
AUTH_USER=signalwire
AUTH_PASSWORD=strong-random-password
# Logging
LOG_LEVEL=info
LOG_FORMAT=json
Loading Configuration
#!/usr/bin/env python3
import os
from dotenv import load_dotenv
from signalwire_agents import AgentBase
# Load environment
load_dotenv()
class ProductionAgent(AgentBase):
def __init__(self):
super().__init__(
name=os.getenv("AGENT_NAME", "production-agent"),
route=os.getenv("AGENT_ROUTE", "/")
)
self._configure_auth()
self._configure_prompts()
self._configure_voice()
def _configure_auth(self):
user = os.getenv("AUTH_USER")
password = os.getenv("AUTH_PASSWORD")
if user and password:
self.set_params({
"swml_basic_auth_user": user,
"swml_basic_auth_password": password
})
Production Checklist
| Item | Status |
|---|---|
| Environment variables set | [ ] |
| Secrets not in code | [ ] |
| Authentication enabled | [ ] |
| HTTPS configured | [ ] |
| Logging configured | [ ] |
| Error handling in place | [ ] |
| Health checks implemented | [ ] |
2. Docker Deployment (35 min)
Dockerfile
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Create non-root user
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Run with uvicorn
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "3000", "--workers", "4"]
requirements.txt
signalwire-agents>=1.0.7
python-dotenv>=1.0.0
uvicorn[standard]>=0.20.0
docker-compose.yml
version: '3.8'
services:
agent:
build: .
ports:
- "3000:3000"
environment:
- SIGNALWIRE_SPACE_NAME=${SIGNALWIRE_SPACE_NAME}
- SIGNALWIRE_PROJECT_ID=${SIGNALWIRE_PROJECT_ID}
- SIGNALWIRE_TOKEN=${SIGNALWIRE_TOKEN}
- AUTH_USER=${AUTH_USER}
- AUTH_PASSWORD=${AUTH_PASSWORD}
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
Building and Running
# Build image
docker build -t my-agent:latest .
# Run container
docker run -d \
--name my-agent \
-p 3000:3000 \
--env-file .env.production \
my-agent:latest
# Check logs
docker logs my-agent
# Check health
docker inspect --format='' my-agent
3. Kubernetes Deployment (30 min)
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: voice-agent
spec:
replicas: 3
selector:
matchLabels:
app: voice-agent
template:
metadata:
labels:
app: voice-agent
spec:
containers:
- name: agent
image: my-agent:latest
ports:
- containerPort: 3000
env:
- name: SIGNALWIRE_SPACE_NAME
valueFrom:
secretKeyRef:
name: signalwire-secrets
key: space-name
- name: SIGNALWIRE_PROJECT_ID
valueFrom:
secretKeyRef:
name: signalwire-secrets
key: project-id
- name: SIGNALWIRE_TOKEN
valueFrom:
secretKeyRef:
name: signalwire-secrets
key: token
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
service.yaml
apiVersion: v1
kind: Service
metadata:
name: voice-agent-service
spec:
selector:
app: voice-agent
ports:
- port: 80
targetPort: 3000
type: LoadBalancer
4. Logging Configuration (25 min)
Structured Logging
import logging
import json
from datetime import datetime
class JSONFormatter(logging.Formatter):
def format(self, record):
log_record = {
"timestamp": datetime.utcnow().isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
if record.exc_info:
log_record["exception"] = self.formatException(record.exc_info)
return json.dumps(log_record)
# Configure logging
def setup_logging():
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logging.basicConfig(
level=os.getenv("LOG_LEVEL", "INFO").upper(),
handlers=[handler]
)
Agent Logging
import logging
logger = logging.getLogger(__name__)
class LoggedAgent(AgentBase):
@AgentBase.tool(description="Process order")
def process_order(self, args: dict, raw_data: dict = None) -> SwaigFunctionResult:
order_id = args.get("order_id", "")
logger.info(f"Processing order", extra={"order_id": order_id})
try:
result = do_processing(order_id)
logger.info(f"Order processed", extra={
"order_id": order_id,
"status": "success"
})
return SwaigFunctionResult("Order processed.")
except Exception as e:
logger.error(f"Order processing failed", extra={
"order_id": order_id,
"error": str(e)
})
return SwaigFunctionResult("Error processing order.")
5. Health Checks (20 min)
Basic Health Endpoint
from signalwire_agents import AgentServer
server = AgentServer()
server.register(MyAgent())
@server.app.get("/health")
async def health():
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
Comprehensive Health Check
@server.app.get("/health")
async def health():
checks = {
"agent": check_agent_health(),
"database": check_database(),
"external_api": check_external_api()
}
all_healthy = all(c["status"] == "healthy" for c in checks.values())
return {
"status": "healthy" if all_healthy else "degraded",
"checks": checks,
"timestamp": datetime.utcnow().isoformat()
}
def check_agent_health():
try:
# Verify agent can generate SWML
agent.get_swml()
return {"status": "healthy"}
except Exception as e:
return {"status": "unhealthy", "error": str(e)}
def check_database():
try:
# Ping database
db.ping()
return {"status": "healthy"}
except Exception as e:
return {"status": "unhealthy", "error": str(e)}
Complete Production Example
#!/usr/bin/env python3
"""Production-ready agent."""
import os
import logging
from datetime import datetime
from dotenv import load_dotenv
from signalwire_agents import AgentServer, AgentBase, SwaigFunctionResult
# Load environment
load_dotenv()
# Configure logging
logging.basicConfig(
level=os.getenv("LOG_LEVEL", "INFO").upper(),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class ProductionAgent(AgentBase):
def __init__(self):
super().__init__(
name="production-agent",
route="/agent"
)
# Authentication
auth_user = os.getenv("AUTH_USER")
auth_pass = os.getenv("AUTH_PASSWORD")
if auth_user and auth_pass:
self.set_params({
"swml_basic_auth_user": auth_user,
"swml_basic_auth_password": auth_pass
})
# Configuration
self.prompt_add_section("Role", "You are a production support agent.")
self.add_language("English", "en-US", "rime.spore")
logger.info("ProductionAgent initialized")
@AgentBase.tool(description="Get system status")
def get_status(self, args: dict, raw_data: dict = None) -> SwaigFunctionResult:
logger.info("Status check requested")
return SwaigFunctionResult("All systems operational.")
def create_app():
"""Create the application."""
host = os.getenv("HOST", "0.0.0.0")
port = int(os.getenv("PORT", "3000"))
server = AgentServer(host=host, port=port)
server.register(ProductionAgent())
# Health endpoint
@server.app.get("/health")
async def health():
return {
"status": "healthy",
"timestamp": datetime.utcnow().isoformat(),
"version": os.getenv("APP_VERSION", "1.0.0")
}
# Ready endpoint
@server.app.get("/ready")
async def ready():
return {"ready": True}
return server
# For uvicorn
server = create_app()
app = server.app
if __name__ == "__main__":
server.run()
Key Takeaways
- Environment-based config - No secrets in code
- Docker for consistency - Same everywhere
- Health checks are essential - Know when things break
- Structured logging - Machine-parseable logs
- Multiple replicas - Scale horizontally
Preparation for Lab 2.10
- Install Docker
- Have cloud account ready (if deploying)
- Review your agent for production readiness
Lab Preview
In Lab 2.10, you will:
- Containerize your agent
- Set up Docker Compose
- Implement health checks
- Deploy to a server