Lab L2.1: Function Results and Actions
🎯 Assignment: Accept this lab on GitHub Classroom
You’ll get your own repository with starter code, instructions, and automatic grading.
| Â | Â |
|---|---|
| Duration | 45 minutes |
| Prerequisites | Previous module completed |
Objectives
- Chain multiple actions in a single response
- Implement post-process actions
- Control call flow with actions
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: Action Chaining (15 min)
Task
Build an agent that confirms appointments and performs multiple actions:
- Says a confirmation message
- Sends an SMS confirmation
- Updates metadata with appointment details
Starter Code
#!/usr/bin/env python3
"""Lab 2.1: Action chaining."""
from signalwire_agents import AgentBase, SwaigFunctionResult
class AppointmentAgent(AgentBase):
def __init__(self):
super().__init__(name="appointment-agent")
self.prompt_add_section(
"Role",
"You help customers schedule and confirm appointments."
)
self.add_language("English", "en-US", "rime.spore")
# TODO: Add the confirm_appointment function
if __name__ == "__main__":
agent = AppointmentAgent()
agent.run()
Your Task
Add a confirm_appointment function that:
- Takes
date,time, andphoneparameters - Returns a spoken confirmation
- Sends an SMS with appointment details
- Stores appointment info in metadata
Expected Result
@AgentBase.tool(
description="Confirm an appointment",
parameters={
"type": "object",
"properties": {
"date": {"type": "string", "description": "Appointment date"},
"time": {"type": "string", "description": "Appointment time"},
"phone": {"type": "string", "description": "Customer phone"}
},
"required": ["date", "time", "phone"]
}
)
def confirm_appointment(
self,
args: dict,
raw_data: dict = None
) -> SwaigFunctionResult:
date = args.get("date", "")
time = args.get("time", "")
phone = args.get("phone", "")
return (
SwaigFunctionResult(
f"Your appointment is confirmed for {date} at {time}. "
"I've sent a confirmation to your phone."
)
.send_sms(
to_number=phone,
from_number="+15559876543",
body=f"Appointment confirmed: {date} at {time}"
)
.update_global_data({
"appointment_date": date,
"appointment_time": time,
"confirmed": True
})
)
Part 2: Post-Process Actions (15 min)
Task
Create a function that schedules a callback after the conversation ends.
Your Task
Add a schedule_callback function that:
- Takes
phoneandreasonparameters - Tells the user a callback will be scheduled
- Uses post-process to actually schedule it (simulated with metadata)
Expected Result
@AgentBase.tool(
description="Schedule a callback for the customer",
parameters={
"type": "object",
"properties": {
"phone": {"type": "string"},
"reason": {"type": "string"}
},
"required": ["phone", "reason"]
}
)
def schedule_callback(
self,
args: dict,
raw_data: dict = None
) -> SwaigFunctionResult:
phone = args.get("phone", "")
reason = args.get("reason", "")
return (
SwaigFunctionResult(
"I've scheduled a callback for you. "
"One of our team members will reach out soon.",
post_process=True # AI can respond to follow-up before action executes
)
.update_global_data({
"callback_phone": phone,
"callback_reason": reason,
"callback_scheduled": True
})
)
Part 3: Call Flow Control (15 min)
Task
Build an escalation function that transfers to a supervisor.
Your Task
Add an escalate_call function that:
- Takes a
reasonparameter - Explains the transfer to the caller
- Performs a post-process transfer
Expected Result
@AgentBase.tool(
description="Escalate call to supervisor",
parameters={
"type": "object",
"properties": {
"reason": {"type": "string", "description": "Escalation reason"}
},
"required": ["reason"]
}
)
def escalate_call(self, args: dict, raw_data: dict = None) -> SwaigFunctionResult:
reason = args.get("reason", "")
return (
SwaigFunctionResult(
"I understand this needs supervisor attention. "
"Let me transfer you now.",
post_process=True # AI finishes speaking before transfer
)
.update_global_data({"escalation_reason": reason})
.connect("+15551234567", final=True)
)
Testing
# Test the agent
swaig-test lab2_1_actions.py --dump-swml
# Verify functions are registered
swaig-test lab2_1_actions.py --list-tools
# Test specific function
swaig-test lab2_1_actions.py --exec confirm_appointment \
--date "2024-01-15" \
--time "2:00 PM" \
--phone "+15559876543"
Validation Checklist
confirm_appointmentreturns 3 chained actionsschedule_callbackuses post_process=Trueescalate_callperforms transfer after speaking- All functions appear in SWML output
- SMS actions include required fields
Challenge Extension
Add a cancel_appointment function that:
- Confirms cancellation verbally
- Sends cancellation SMS
- Updates metadata to mark as cancelled
- Offers to reschedule (context switch if you’ve learned contexts)
Submission
Upload your completed lab2_1_actions.py file.
Complete Agent Code
Click to reveal complete solution
#!/usr/bin/env python3
"""Appointment agent with action chaining.
Lab 2.1 Deliverable: Demonstrates SwaigFunctionResult action chaining
including post-process actions for transfers and callbacks.
"""
from signalwire_agents import AgentBase, SwaigFunctionResult
class AppointmentAgent(AgentBase):
"""Agent for scheduling appointments with action chaining."""
def __init__(self):
super().__init__(name="appointment-agent")
self.prompt_add_section(
"Role",
"You help customers schedule and confirm appointments."
)
self.prompt_add_section(
"Capabilities",
bullets=[
"Confirm appointments with SMS notifications",
"Schedule callbacks for follow-up",
"Escalate to supervisor when needed"
]
)
self.add_language("English", "en-US", "rime.spore")
self._setup_functions()
def _setup_functions(self):
@self.tool(
description="Confirm an appointment",
parameters={
"type": "object",
"properties": {
"date": {"type": "string", "description": "Appointment date"},
"time": {"type": "string", "description": "Appointment time"},
"phone": {"type": "string", "description": "Customer phone"}
},
"required": ["date", "time", "phone"]
}
)
def confirm_appointment(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
"""Confirm appointment with multiple chained actions."""
date = args.get("date", "")
time = args.get("time", "")
phone = args.get("phone", "")
return (
SwaigFunctionResult(
f"Your appointment is confirmed for {date} at {time}. "
"I've sent a confirmation to your phone."
)
.send_sms(
to_number=phone,
from_number="+15559999999",
body=f"Appointment confirmed: {date} at {time}"
)
.update_global_data({
"appointment_date": date,
"appointment_time": time,
"confirmed": True
})
)
@self.tool(
description="Schedule a callback for the customer",
parameters={
"type": "object",
"properties": {
"phone": {"type": "string"},
"reason": {"type": "string"}
},
"required": ["phone", "reason"]
}
)
def schedule_callback(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
"""Schedule callback using post-process action."""
phone = args.get("phone", "")
reason = args.get("reason", "")
return (
SwaigFunctionResult(
"I've scheduled a callback for you. "
"One of our team members will reach out soon.",
post_process=True
)
.update_global_data({
"callback_phone": phone,
"callback_reason": reason,
"callback_scheduled": True
})
)
@self.tool(
description="Escalate call to supervisor",
parameters={
"type": "object",
"properties": {
"reason": {"type": "string", "description": "Escalation reason"}
},
"required": ["reason"]
}
)
def escalate_call(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
"""Escalate to supervisor with post-process transfer."""
reason = args.get("reason", "")
return (
SwaigFunctionResult(
"I understand this needs supervisor attention. "
"Let me transfer you now.",
post_process=True
)
.update_global_data({"escalation_reason": reason})
.connect("+15551234567", final=True)
)
@self.tool(
description="Cancel an appointment",
parameters={
"type": "object",
"properties": {
"appointment_id": {"type": "string"},
"phone": {"type": "string"}
},
"required": ["appointment_id", "phone"]
}
)
def cancel_appointment(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
"""Cancel appointment with SMS confirmation."""
appointment_id = args.get("appointment_id", "")
phone = args.get("phone", "")
return (
SwaigFunctionResult(
f"Appointment {appointment_id} has been cancelled. "
"I've sent a confirmation to your phone. "
"Would you like to reschedule?"
)
.send_sms(
to_number=phone,
from_number="+15559999999",
body=f"Appointment {appointment_id} cancelled."
)
.update_global_data({
"cancelled_appointment": appointment_id,
"cancellation_confirmed": True
})
)
if __name__ == "__main__":
agent = AppointmentAgent()
agent.run()