Lab L2.6: State Management
🎯 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
- Manage conversation state
- Persist data across turns
- Track user progress
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
Build a customer service agent that:
- Identifies customers by phone
- Tracks conversation topics
- Maintains a help ticket throughout the call
Part 1: Global Data (15 min)
Task
Set up global data for company information and service hours.
Starter Code
#!/usr/bin/env python3
"""Lab 2.6: State management."""
from datetime import datetime
from signalwire_agents import AgentBase, SwaigFunctionResult
class ServiceAgent(AgentBase):
# Simulated customer database
CUSTOMERS = {
"+15551234567": {"id": "C001", "name": "John Smith", "tier": "gold"},
"+15559876543": {"id": "C002", "name": "Jane Doe", "tier": "silver"},
}
def __init__(self):
super().__init__(name="service-agent")
self.prompt_add_section(
"Role",
"You are a customer service agent for TechCorp. "
"Help customers with inquiries and track issues."
)
self.add_language("English", "en-US", "rime.spore")
self._setup_global_data()
self._setup_functions()
def _setup_global_data(self):
# TODO: Set up global data
pass
def _setup_functions(self):
# TODO: Set up functions
pass
if __name__ == "__main__":
agent = ServiceAgent()
agent.run()
Expected Implementation
def _setup_global_data(self):
hour = datetime.now().hour
self.set_global_data({
"company_name": "TechCorp",
"support_email": "support@techcorp.com",
"support_phone": "1-800-TECH",
"business_hours": "9 AM to 6 PM EST",
"is_business_hours": 9 <= hour < 18,
"greeting": "Good morning" if hour < 12 else "Good afternoon"
})
Part 2: Customer Identification (15 min)
Task
Create a function that identifies customers and stores their info in metadata.
Expected Implementation
@self.tool(
description="Identify customer by phone number",
parameters={
"type": "object",
"properties": {
"phone": {
"type": "string",
"description": "Customer phone number"
}
},
"required": ["phone"]
}
)
def identify_customer(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
phone = args.get("phone", "")
customer = self.CUSTOMERS.get(phone)
if customer:
global_data = self.get_global_data()
greeting = global_data.get("greeting", "Hello")
return (
SwaigFunctionResult(
f"{greeting}, {customer['name']}! "
f"I see you're a {customer['tier']} member. How can I help?"
)
.update_global_data( {
"customer_id": customer["id"],
"customer_name": customer["name"],
"customer_tier": customer["tier"],
"identified": True
})
)
return SwaigFunctionResult(
"I don't recognize that number. Could you provide your account ID?"
)
Part 3: Ticket Tracking (15 min)
Task
Create functions to create and update support tickets using metadata.
Expected Implementation
@self.tool(
description="Create a support ticket",
parameters={
"type": "object",
"properties": {
"issue": {
"type": "string",
"description": "Description of the issue"
}
},
"required": ["issue"]
}
)
def create_ticket(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
issue = args.get("issue", "")
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
customer_id = global_data.get("customer_id", "UNKNOWN")
customer_name = global_data.get("customer_name", "Customer")
# Generate ticket ID
ticket_id = f"TKT-{datetime.now().strftime('%Y%m%d%H%M%S')}"
return (
SwaigFunctionResult(
f"I've created ticket {ticket_id} for you, {customer_name}. "
"Is there anything else about this issue?"
)
.update_global_data( {
"ticket_id": ticket_id,
"ticket_issue": issue,
"ticket_notes": []
})
)
@self.tool(
description="Add a note to the current ticket",
parameters={
"type": "object",
"properties": {
"note": {
"type": "string",
"description": "Additional information"
}
},
"required": ["note"]
}
)
def add_ticket_note(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
note = args.get("note", "")
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
ticket_id = global_data.get("ticket_id")
notes = global_data.get("ticket_notes", [])
if not ticket_id:
return SwaigFunctionResult(
"No ticket found. Would you like me to create one?"
)
notes.append({
"time": datetime.now().isoformat(),
"content": note
})
return (
SwaigFunctionResult(f"Added note to ticket {ticket_id}.")
.update_global_data( {"ticket_notes": notes})
)
@self.tool(description="Get ticket summary")
def get_ticket_summary(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
ticket_id = global_data.get("ticket_id")
issue = global_data.get("ticket_issue", "No issue recorded")
notes = global_data.get("ticket_notes", [])
if not ticket_id:
return SwaigFunctionResult("No active ticket.")
summary = f"Ticket {ticket_id}: {issue}. {len(notes)} notes added."
return SwaigFunctionResult(summary)
Testing
# Test agent
swaig-test lab2_6_state.py --dump-swml
# Verify global data in SWML
swaig-test lab2_6_state.py --dump-swml | grep -A5 "global_data"
# Test identify customer
swaig-test lab2_6_state.py --exec identify_customer \
--phone "+15551234567"
# Test create ticket
swaig-test lab2_6_state.py --exec create_ticket \
--issue "Cannot login to account"
Validation Checklist
- Global data contains company info
- Customer identification stores metadata
- Ticket creation uses customer metadata
- Notes accumulate in metadata
- Summary reads from metadata correctly
Submission
Upload your completed lab2_6_state.py file.
Complete Agent Code
Click to reveal complete solution
#!/usr/bin/env python3
"""State management agent with global data and metadata.
Lab 2.6 Deliverable: Demonstrates global data for shared configuration
and call metadata for per-call state tracking.
"""
from datetime import datetime
from signalwire_agents import AgentBase, SwaigFunctionResult
class ServiceAgent(AgentBase):
"""Customer service agent with comprehensive state management."""
# Simulated customer database
CUSTOMERS = {
"+15551234567": {"id": "C001", "name": "John Smith", "tier": "gold"},
"+15559876543": {"id": "C002", "name": "Jane Doe", "tier": "silver"},
"+15551112222": {"id": "C003", "name": "Bob Wilson", "tier": "bronze"},
}
def __init__(self):
super().__init__(name="service-agent")
self.prompt_add_section(
"Role",
"You are a customer service agent for TechCorp. "
"Help customers with inquiries and track issues."
)
self.prompt_add_section(
"Process",
bullets=[
"Identify the customer by phone number",
"Create support tickets for issues",
"Add notes to track conversation details",
"Provide ticket summary when requested"
]
)
self.add_language("English", "en-US", "rime.spore")
self._setup_global_data()
self._setup_functions()
def _setup_global_data(self):
"""Set up global data available to all function calls."""
hour = datetime.now().hour
self.set_global_data({
"company_name": "TechCorp",
"support_email": "support@techcorp.com",
"support_phone": "1-800-TECH",
"business_hours": "9 AM to 6 PM EST",
"is_business_hours": 9 <= hour < 18,
"greeting": "Good morning" if hour < 12 else (
"Good afternoon" if hour < 17 else "Good evening"
)
})
def _setup_functions(self):
"""Define SWAIG functions for customer service."""
@self.tool(
description="Identify customer by phone number",
parameters={
"type": "object",
"properties": {
"phone": {
"type": "string",
"description": "Customer phone number"
}
},
"required": ["phone"]
}
)
def identify_customer(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
phone = args.get("phone", "")
customer = self.CUSTOMERS.get(phone)
if customer:
global_data = self.get_global_data()
greeting = global_data.get("greeting", "Hello")
return (
SwaigFunctionResult(
f"{greeting}, {customer['name']}! "
f"I see you're a {customer['tier']} member. How can I help?"
)
.update_global_data({
"customer_id": customer["id"],
"customer_name": customer["name"],
"customer_tier": customer["tier"],
"identified": True
})
)
return SwaigFunctionResult(
"I don't recognize that number. Could you provide your account ID?"
)
@self.tool(description="Get company information")
def get_company_info(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
global_data = self.get_global_data()
return SwaigFunctionResult(
f"You've reached {global_data['company_name']}. "
f"Our hours are {global_data['business_hours']}. "
f"Email us at {global_data['support_email']}."
)
@self.tool(
description="Create a support ticket",
parameters={
"type": "object",
"properties": {
"issue": {
"type": "string",
"description": "Description of the issue"
}
},
"required": ["issue"]
}
)
def create_ticket(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
issue = args.get("issue", "")
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
customer_id = global_data.get("customer_id", "UNKNOWN")
customer_name = global_data.get("customer_name", "Customer")
# Generate ticket ID
ticket_id = f"TKT-{datetime.now().strftime('%Y%m%d%H%M%S')}"
return (
SwaigFunctionResult(
f"I've created ticket {ticket_id} for you, {customer_name}. "
"Is there anything else about this issue?"
)
.update_global_data({
"ticket_id": ticket_id,
"ticket_issue": issue,
"ticket_notes": [],
"ticket_created": datetime.now().isoformat()
})
)
@self.tool(
description="Add a note to the current ticket",
parameters={
"type": "object",
"properties": {
"note": {
"type": "string",
"description": "Additional information"
}
},
"required": ["note"]
}
)
def add_ticket_note(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
note = args.get("note", "")
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
ticket_id = global_data.get("ticket_id")
notes = global_data.get("ticket_notes", [])
if not ticket_id:
return SwaigFunctionResult(
"No ticket found. Would you like me to create one?"
)
notes.append({
"time": datetime.now().isoformat(),
"content": note
})
return (
SwaigFunctionResult(
f"Added note to ticket {ticket_id}. "
f"Total notes: {len(notes)}."
)
.update_global_data({"ticket_notes": notes})
)
@self.tool(description="Get ticket summary")
def get_ticket_summary(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
ticket_id = global_data.get("ticket_id")
issue = global_data.get("ticket_issue", "No issue recorded")
notes = global_data.get("ticket_notes", [])
customer_name = global_data.get("customer_name", "Customer")
if not ticket_id:
return SwaigFunctionResult("No active ticket.")
return SwaigFunctionResult(
f"Ticket {ticket_id} for {customer_name}: {issue}. "
f"{len(notes)} note(s) added."
)
@self.tool(
description="Escalate ticket to supervisor",
parameters={
"type": "object",
"properties": {
"reason": {"type": "string"}
},
"required": ["reason"]
}
)
def escalate_ticket(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
reason = args.get("reason", "")
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
ticket_id = global_data.get("ticket_id")
if not ticket_id:
return SwaigFunctionResult(
"No ticket to escalate. Let me create one first."
)
return (
SwaigFunctionResult(
f"Ticket {ticket_id} has been escalated. "
"A supervisor will contact you within 2 hours."
)
.update_global_data({
"escalated": True,
"escalation_reason": reason,
"escalation_time": datetime.now().isoformat()
})
)
if __name__ == "__main__":
agent = ServiceAgent()
agent.run()