Lab L1.7: Build a Lookup Function
🎯 Assignment: Accept this lab on GitHub Classroom
You’ll get your own repository with starter code, instructions, and automatic grading.
| Â | Â |
|---|---|
| Duration | 60 minutes |
| Prerequisites | Previous module completed |
Objectives
- Create SWAIG functions with parameters
- Test functions using swaig-test
- Make live calls that use functions
- Handle function results properly
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.
Scenario
You’re building an Order Status Agent that helps customers check their order status. The agent needs a function to look up orders.
Exercise 1: Create Order Agent
Create order_agent.py:
#!/usr/bin/env python3
"""Order status agent with lookup function."""
import os
from signalwire_agents import AgentBase, SwaigFunctionResult
agent = AgentBase(
name="order-agent",
route="/orders"
)
# Authentication
agent.set_params({
"swml_basic_auth_user": os.getenv("AUTH_USER", "signalwire"),
"swml_basic_auth_password": os.getenv("AUTH_PASS", "training123"),
})
# Prompt
agent.prompt_add_section(
"Role",
"You are an order status assistant. Help customers check "
"the status of their orders. Ask for their order number "
"to look it up."
)
agent.prompt_add_section(
"Process",
bullets=[
"Greet the customer warmly",
"Ask for their order number",
"Use the lookup function to find the order",
"Tell them the status clearly",
"Ask if there's anything else"
]
)
# Voice
agent.add_language(
"English", "en-US", "rime.spore",
speech_fillers=["Um", "Uh", "Well"],
function_fillers=["Looking that up now...", "One moment please..."]
)
if __name__ == "__main__":
agent.run()
Test:
swaig-test order_agent.py --list-tools
Expected: No tools listed yet.
Exercise 2: Add Lookup Function
Add a function to look up orders:
Add to order_agent.py before if __name__:
# Mock order database
ORDERS = {
"12345": {"status": "shipped", "date": "Monday", "carrier": "FedEx"},
"67890": {"status": "processing", "date": "tomorrow", "carrier": "UPS"},
"11111": {"status": "delivered", "date": "last Friday", "carrier": "USPS"},
}
@agent.tool(
description="Look up an order by order number",
parameters={
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "The order number to look up"
}
},
"required": ["order_number"]
}
)
def get_order_status(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
"""Look up order status."""
# Clean the input
order_number = args.get("order_number", "").strip()
# Look up in mock database
if order_number in ORDERS:
order = ORDERS[order_number]
return SwaigFunctionResult(
f"Order {order_number} status is {order['status']}. "
f"It was shipped via {order['carrier']} and "
f"{'was delivered' if order['status'] == 'delivered' else 'is expected'} "
f"{order['date']}."
)
else:
return SwaigFunctionResult(
f"I couldn't find order number {order_number}. "
"Please check the number and try again."
)
Exercise 3: Test Function Listing
swaig-test order_agent.py --list-tools
Expected Output:
Available tools:
get_order_status - Look up an order by order number
Parameters:
order_number (string, required)
Exercise 4: Test Function Execution
Test with valid order:
swaig-test order_agent.py --exec get_order_status --order_number "12345"
Expected Output:
Executing function: get_order_status
Arguments: {"order_number": "12345"}
Result: Order 12345 status is shipped. It was shipped via FedEx and is expected Monday.
Test with invalid order:
swaig-test order_agent.py --exec get_order_status --order_number "99999"
Expected: Error message about order not found.
Exercise 5: Examine SWML
swaig-test order_agent.py --dump-swml | grep -A 30 '"SWAIG"'
Find:
- Function name
- Description
- Parameters schema
- Webhook URL
Exercise 6: Add Second Function
Add a function to list recent orders by email:
# Mock customer database
CUSTOMERS = {
"john@example.com": ["12345", "67890"],
"jane@example.com": ["11111"],
}
@agent.tool(
description="Find orders for a customer by their email address",
fillers=["Searching for your orders...", "Let me find those..."],
parameters={
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "Customer email address"
}
},
"required": ["email"]
}
)
def find_orders_by_email(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
"""Find orders by customer email."""
email = args.get("email", "").lower().strip()
if email in CUSTOMERS:
orders = CUSTOMERS[email]
if len(orders) == 1:
return SwaigFunctionResult(
f"I found 1 order for {email}: order number {orders[0]}."
)
else:
order_list = ", ".join(orders[:-1]) + f" and {orders[-1]}"
return SwaigFunctionResult(
f"I found {len(orders)} orders for {email}: {order_list}."
)
else:
return SwaigFunctionResult(
f"I couldn't find any orders for {email}. "
"Please check the email address."
)
Update the prompt to mention email lookup:
agent.prompt_add_section(
"Process",
bullets=[
"Greet the customer warmly",
"Ask if they have an order number, or ask for their email",
"Use the appropriate function to find their order",
"Tell them the status clearly",
"Ask if there's anything else"
]
)
Exercise 7: Test Both Functions
# List all tools
swaig-test order_agent.py --list-tools
# Test order lookup
swaig-test order_agent.py --exec get_order_status --order_number "12345"
# Test email lookup
swaig-test order_agent.py --exec find_orders_by_email --email "john@example.com"
Exercise 8: Live Call Test
-
Start the agent:
python order_agent.py -
Start ngrok:
ngrok http 3000 -
Update SignalWire URL (change
/agentto/orders) -
Call your number and try:
- “What’s the status of order 12345?”
- “Can you look up orders for john@example.com?”
- “Check order 99999” (invalid)
Exercise 9: Add Parameter Validation
Improve the order lookup with validation:
@agent.tool(
description="Look up an order by order number (5 digits)",
parameters={
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "The 5-digit order number",
"pattern": "^[0-9]{5}$"
}
},
"required": ["order_number"]
}
)
def get_order_status(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
order_number = args.get("order_number", "").strip()
# ... existing code ...
Challenge Exercise
Add a third function to cancel an order:
Requirements:
- Only allow cancellation if status is “processing”
- Return appropriate message for other statuses
- Add confirmation in the response
@agent.tool(
description="Cancel an order if it hasn't shipped yet",
parameters={
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "The order number to cancel"
}
},
"required": ["order_number"]
}
)
def cancel_order(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
order_number = args.get("order_number", "").strip()
# Your implementation here
pass
Complete Agent Code
Click to reveal complete solution
#!/usr/bin/env python3
"""Complete order status agent."""
import os
from signalwire_agents import AgentBase, SwaigFunctionResult
agent = AgentBase(name="order-agent", route="/orders")
agent.set_params({
"swml_basic_auth_user": os.getenv("AUTH_USER", "signalwire"),
"swml_basic_auth_password": os.getenv("AUTH_PASS", "training123"),
})
agent.prompt_add_section(
"Role",
"You are an order status assistant. Help customers check "
"and manage their orders."
)
agent.prompt_add_section(
"Process",
bullets=[
"Greet the customer warmly",
"Ask if they have an order number, or ask for their email",
"Use the appropriate function to find or check their order",
"Tell them the status clearly",
"Ask if there's anything else"
]
)
agent.add_language(
"English", "en-US", "rime.spore",
speech_fillers=["Um", "Uh", "Well"],
function_fillers=["Looking that up now...", "One moment please..."]
)
# Mock databases
ORDERS = {
"12345": {"status": "shipped", "date": "Monday", "carrier": "FedEx"},
"67890": {"status": "processing", "date": "tomorrow", "carrier": "UPS"},
"11111": {"status": "delivered", "date": "last Friday", "carrier": "USPS"},
}
CUSTOMERS = {
"john@example.com": ["12345", "67890"],
"jane@example.com": ["11111"],
}
@agent.tool(
description="Look up an order by order number",
parameters={
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "The order number to look up"
}
},
"required": ["order_number"]
}
)
def get_order_status(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
order_number = args.get("order_number", "").strip()
if order_number in ORDERS:
order = ORDERS[order_number]
return SwaigFunctionResult(
f"Order {order_number} status is {order['status']}. "
f"Shipped via {order['carrier']}, "
f"{'delivered' if order['status'] == 'delivered' else 'expected'} "
f"{order['date']}."
)
return SwaigFunctionResult(f"Order {order_number} not found.")
@agent.tool(
description="Find orders for a customer by email",
fillers=["Searching for your orders..."],
parameters={
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "Customer email address"
}
},
"required": ["email"]
}
)
def find_orders_by_email(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
email = args.get("email", "").lower().strip()
if email in CUSTOMERS:
orders = CUSTOMERS[email]
return SwaigFunctionResult(
f"Found {len(orders)} order(s) for {email}: {', '.join(orders)}."
)
return SwaigFunctionResult(f"No orders found for {email}.")
@agent.tool(
description="Cancel an order if it hasn't shipped",
parameters={
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "The order number to cancel"
}
},
"required": ["order_number"]
}
)
def cancel_order(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
order_number = args.get("order_number", "").strip()
if order_number not in ORDERS:
return SwaigFunctionResult(f"Order {order_number} not found.")
order = ORDERS[order_number]
if order["status"] == "processing":
return SwaigFunctionResult(
f"Order {order_number} has been cancelled. "
"You'll receive a confirmation email."
)
elif order["status"] == "shipped":
return SwaigFunctionResult(
f"Order {order_number} has already shipped and cannot be cancelled. "
"Would you like information about returns instead?"
)
else:
return SwaigFunctionResult(
f"Order {order_number} has already been delivered."
)
if __name__ == "__main__":
agent.run()
Deliverables
order_agent.pywith all three functions- Screenshot of swaig-test –list-tools output
- Notes from live call testing
Review Questions
- What does the @tool decorator do?
- How does the AI know when to call a function?
- What must every function return?
- How do you test functions without making calls?
- What are fillers used for?
Summary
You have successfully:
- Created SWAIG functions with parameters
- Tested functions using swaig-test CLI
- Made live calls that invoke functions
- Handled various response scenarios
Next: Module 1.8 - Certification Assessment