Lab L2.11: Serverless Deployment
🎯 Assignment: Accept this lab on GitHub Classroom
You’ll get your own repository with starter code, instructions, and automatic grading.
| Â | Â |
|---|---|
| Duration | 75 minutes |
| Prerequisites | Previous module completed |
Objectives
- Deploy agent to AWS Lambda
- Configure API Gateway
- Handle serverless constraints
How to Complete This Lab
- Accept the Assignment — Click the GitHub Classroom link above
- Clone Your Repo —
git clone <your-repo-url> - Read the README — Your repo has detailed requirements and grading criteria
- Write Your Code — Implement the solution in
solution/agent.py - Test Locally — Use
swaig-testto verify your agent works - Push to Submit —
git pushtriggers auto-grading
Key Concepts
The following exercises walk through the concepts you’ll need. Your GitHub Classroom repo README has the specific requirements for grading.
Part 1: Lambda Handler (20 min)
handler.py
#!/usr/bin/env python3
"""AWS Lambda handler for SignalWire agent."""
import os
from signalwire_agents import AgentBase, SwaigFunctionResult
class LambdaAgent(AgentBase):
def __init__(self):
super().__init__(name="lambda-agent")
self.prompt_add_section(
"Role",
"You are a serverless assistant running on AWS Lambda."
)
self.prompt_add_section(
"Capabilities",
bullets=[
"Answer questions about the service",
"Provide status information",
"Help with basic inquiries"
]
)
self.add_language("English", "en-US", "rime.spore")
self._setup_functions()
def _setup_functions(self):
@self.tool(description="Get service status")
def get_status(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
return SwaigFunctionResult(
"The service is running on AWS Lambda. All systems operational."
)
@self.tool(description="Get deployment info")
def get_deployment_info(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
region = os.getenv("AWS_REGION", "unknown")
function = os.getenv("AWS_LAMBDA_FUNCTION_NAME", "unknown")
return SwaigFunctionResult(
f"Running in {region} as {function}."
)
@self.tool(
description="Echo a message back",
parameters={
"type": "object",
"properties": {
"message": {"type": "string"}
},
"required": ["message"]
}
)
def echo(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
message = args.get("message", "")
return SwaigFunctionResult(f"You said: {message}")
# Create agent instance (outside handler for warm starts)
agent = LambdaAgent()
def lambda_handler(event, context):
"""AWS Lambda entry point."""
return agent.handle_lambda(event, context)
requirements.txt
signalwire-agents>=1.0.7
Part 2: Serverless Framework Config (20 min)
serverless.yml
service: voice-agent
frameworkVersion: '3'
provider:
name: aws
runtime: python3.11
region: us-east-1
stage: ${opt:stage, 'dev'}
environment:
SIGNALWIRE_SPACE_NAME: ${env:SIGNALWIRE_SPACE_NAME}
SIGNALWIRE_PROJECT_ID: ${env:SIGNALWIRE_PROJECT_ID}
SIGNALWIRE_TOKEN: ${env:SIGNALWIRE_TOKEN}
functions:
agent:
handler: handler.lambda_handler
events:
- http:
path: /agent
method: post
cors: true
- http:
path: /agent/swaig
method: post
cors: true
timeout: 30
memorySize: 512
plugins:
- serverless-python-requirements
custom:
pythonRequirements:
dockerizePip: true
slim: true
strip: false
package.json
{
"name": "voice-agent",
"devDependencies": {
"serverless": "^3.38.0",
"serverless-python-requirements": "^6.0.0"
},
"scripts": {
"deploy": "serverless deploy",
"remove": "serverless remove",
"logs": "serverless logs -f agent -t",
"info": "serverless info"
}
}
Part 3: Deploy (15 min)
Task
Deploy the agent to AWS Lambda.
Prerequisites
- AWS CLI configured with credentials
- Node.js installed
- Serverless Framework installed
Commands
# Install dependencies
npm install
# Set environment variables
export SIGNALWIRE_SPACE_NAME=your-space
export SIGNALWIRE_PROJECT_ID=your-project-id
export SIGNALWIRE_TOKEN=your-token
# Deploy
npm run deploy
# Get endpoint URL
npm run info
Expected Output
Service Information
service: voice-agent
stage: dev
region: us-east-1
stack: voice-agent-dev
endpoints:
POST - https://abc123.execute-api.us-east-1.amazonaws.com/dev/agent
POST - https://abc123.execute-api.us-east-1.amazonaws.com/dev/agent/swaig
functions:
agent: voice-agent-dev-agent
Part 4: Testing (5 min)
Task
Test the deployed Lambda function.
Commands
# Test the endpoint
curl -X POST https://your-api-endpoint/dev/agent \
-H "Content-Type: application/json" \
-d '{}'
# Test function execution
curl -X POST https://your-api-endpoint/dev/agent/swaig \
-H "Content-Type: application/json" \
-d '{
"function": "get_status",
"args": {}
}'
# View logs
npm run logs
Cold Start Testing
Measure Cold Start
# Force cold start (wait 15+ minutes or redeploy)
serverless deploy
# Time the first request
time curl -X POST https://your-api-endpoint/dev/agent
# Time subsequent requests (warm)
time curl -X POST https://your-api-endpoint/dev/agent
Expected Results
| Request Type | Typical Time |
|---|---|
| Cold start | 2-5 seconds |
| Warm request | 100-300ms |
Validation Checklist
- Lambda function deploys successfully
- API Gateway endpoints created
- Agent responds to POST requests
- SWAIG functions execute correctly
- Logs show in CloudWatch
- Cold start time acceptable
Optimization Tips
Minimize Cold Starts
-
Keep dependencies minimal
# Import only what you need from signalwire_agents import AgentBase, SwaigFunctionResult -
Initialize outside handler
# Good - reused across invocations agent = LambdaAgent() def lambda_handler(event, context): return agent.handle_lambda(event, context) -
Use Provisioned Concurrency (adds cost)
functions: agent: provisionedConcurrency: 1
Cleanup
# Remove all AWS resources
npm run remove
Alternative: Google Cloud Functions
main.py
from signalwire_agents import AgentBase, SwaigFunctionResult
class GCFAgent(AgentBase):
def __init__(self):
super().__init__(name="gcf-agent")
self.prompt_add_section("Role", "GCF assistant")
self.add_language("English", "en-US", "rime.spore")
agent = GCFAgent()
def handle_request(request):
return agent.handle_cloud_function(request)
Deploy to GCF
gcloud functions deploy voice-agent \
--runtime python311 \
--trigger-http \
--allow-unauthenticated \
--entry-point handle_request
Submission
Upload your project directory containing:
handler.pyrequirements.txtserverless.ymlpackage.json- Screenshot of successful deployment
Complete Agent Code
Click to reveal complete solution
#!/usr/bin/env python3
"""AWS Lambda handler for SignalWire agent.
Lab 2.11 Deliverable: Demonstrates serverless deployment patterns
for AWS Lambda with proper initialization and warm start handling.
"""
import os
from signalwire_agents import AgentBase, SwaigFunctionResult
class LambdaAgent(AgentBase):
"""Agent optimized for serverless deployment."""
def __init__(self):
super().__init__(name="lambda-agent")
self.prompt_add_section(
"Role",
"You are a serverless assistant running on AWS Lambda."
)
self.prompt_add_section(
"Capabilities",
bullets=[
"Answer questions about the service",
"Provide status information",
"Help with basic inquiries"
]
)
self.add_language("English", "en-US", "rime.spore")
self._setup_functions()
def _setup_functions(self):
@self.tool(description="Get service status")
def get_status() -> SwaigFunctionResult:
return SwaigFunctionResult(
"The service is running on AWS Lambda. All systems operational."
)
@self.tool(description="Get deployment info")
def get_deployment_info() -> SwaigFunctionResult:
region = os.getenv("AWS_REGION", "unknown")
function = os.getenv("AWS_LAMBDA_FUNCTION_NAME", "unknown")
memory = os.getenv("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "unknown")
return SwaigFunctionResult(
f"Running in {region} as {function} with {memory}MB memory."
)
@self.tool(
description="Echo a message back",
parameters={
"type": "object",
"properties": {
"message": {
"type": "string",
"description": "Message to echo"
}
},
"required": ["message"]
}
)
def echo(message: str) -> SwaigFunctionResult:
return SwaigFunctionResult(f"You said: {message}")
@self.tool(description="Get help information")
def get_help() -> SwaigFunctionResult:
return SwaigFunctionResult(
"I can provide status info, deployment details, or echo messages. "
"How can I help you today?"
)
# Create agent instance outside handler for warm starts
# This allows the agent to be reused across invocations
agent = LambdaAgent()
def lambda_handler(event, context):
"""AWS Lambda entry point.
Args:
event: Lambda event (API Gateway request)
context: Lambda context with runtime info
Returns:
API Gateway response dict
"""
return agent.handle_lambda(event, context)