| Duration | 2 hours |
| Day | 4 of 5 |
Learning Objectives
By the end of this module, students will be able to:
- Implement different transfer methods
- Configure warm and cold transfers
- Handle transfer failures gracefully
- Build department routing systems
Topics
1. Transfer Types (20 min)
Cold Transfer (Blind)
Immediately connect caller to destination:
Caller ──> AI Agent ──> Destination
(immediate)
@agent.tool(description="Transfer to sales")
def transfer_sales(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
return (
SwaigFunctionResult("Connecting you to sales now.")
.connect("+15551234567", final=True)
)
Warm Transfer (Announced)
AI stays while destination answers:
Caller ──> AI Agent ──> Destination
│ │
└── bridges ─┘
@agent.tool(description="Warm transfer with context")
def transfer_with_context(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
return (
SwaigFunctionResult("Let me connect you. One moment.")
.connect(
"to": "+15551234567",
"ringback": "https://example.com/hold-music.mp3"
)
)
2. Transfer Actions (30 min)
Simple Transfer
.connect("+15551234567" # Phone number
, final=True)
SIP Transfer
.connect("sip:sales@pbx.example.com"
, final=True)
Transfer with Caller ID
.connect("+15551234567",
"from": "+15559876543" # Show this number
, final=True)
Transfer with Ringback
.connect("+15551234567", final=True)
# Note: ringback audio configured in SignalWire dashboard
3. Department Routing (25 min)
Basic Router
DEPARTMENTS = {
"sales": "+15551111111",
"support": "+15552222222",
"billing": "+15553333333",
"returns": "+15554444444"
}
@agent.tool(
description="Transfer to a department",
parameters={
"type": "object",
"properties": {
"department": {
"type": "string",
"enum": list(DEPARTMENTS.keys()),
"description": "Target department"
}
},
"required": ["department"]
}
)
def transfer_department(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
department = args.get("department", "")
if department not in DEPARTMENTS:
return SwaigFunctionResult(
f"I can transfer you to: {', '.join(DEPARTMENTS.keys())}"
)
return (
SwaigFunctionResult(f"Connecting you to {department}.")
.connect(DEPARTMENTS[department], final=True)
)
Time-Based Routing
from datetime import datetime
def get_support_number():
hour = datetime.now().hour
if 9 <= hour < 17: # Business hours
return "+15551111111" # Main support
else:
return "+15552222222" # After-hours
@agent.tool(description="Transfer to support")
def transfer_support(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
number = get_support_number()
hour = datetime.now().hour
if 9 <= hour < 17:
msg = "Connecting you to our support team."
else:
msg = "Connecting you to our after-hours support."
return (
SwaigFunctionResult(msg)
.connect(number, final=True)
)
Skill-Based Routing
SPECIALISTS = {
"billing": {
"number": "+15551111111",
"skills": ["payments", "invoices", "refunds"]
},
"technical": {
"number": "+15552222222",
"skills": ["troubleshooting", "setup", "integration"]
},
"account": {
"number": "+15553333333",
"skills": ["password", "profile", "subscription"]
}
}
@agent.tool(description="Route to appropriate specialist")
def route_to_specialist(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
issue_type = args.get("issue_type", "")
issue_lower = issue_type.lower()
for dept, info in SPECIALISTS.items():
if any(skill in issue_lower for skill in info["skills"]):
return (
SwaigFunctionResult(f"Connecting you to our {dept} specialist.")
.connect(info["number"], final=True)
)
# Default
return (
SwaigFunctionResult("Connecting you to general support.")
.connect("+15559999999", final=True)
)
4. Post-Process Transfers (20 min)
Transfer After Summary
Let AI finish before transferring:
@agent.tool(description="Escalate to supervisor")
def escalate_call(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
return (
SwaigFunctionResult(
"I'll connect you with a supervisor who can help further. "
"Thank you for your patience."
)
.connect("+15551234567"
, final=True) # Happens after AI finishes
)
Transfer with Data Pass
@agent.tool(description="Transfer with context")
def transfer_with_notes(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
department = args.get("department", "")
issue_summary = args.get("issue_summary", "")
raw_data = raw_data or {}
global_data = raw_data.get("global_data", {})
# Build context for receiving agent
context = {
"caller": global_data.get("customer_name", "Unknown"),
"issue": issue_summary,
"call_id": raw_data.get("call_id")
}
# In reality, store this for the receiving agent to access
store_transfer_context(context)
return (
SwaigFunctionResult(f"Transferring to {department} with your information.")
.connect(DEPARTMENTS[department], final=True)
)
5. Transfer with SMS (15 min)
Send Info Before Transfer
@agent.tool(description="Transfer and send ticket number")
def transfer_with_ticket(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
issue = args.get("issue", "")
caller_phone = args.get("caller_phone", "")
# Create ticket
ticket_id = create_support_ticket(issue)
return (
SwaigFunctionResult(
f"I've created ticket {ticket_id} and sent it to your phone. "
"Connecting you to support now."
)
.send_sms(
to_number=caller_phone,
from_number="+15559876543",
body=f"Your support ticket: {ticket_id}. Reference this when speaking with our team."
)
.connect("+15551234567", final=True)
)
Complete Example
Click to reveal complete solution
#!/usr/bin/env python3
"""Department routing agent."""
from datetime import datetime
from signalwire_agents import AgentBase, SwaigFunctionResult
class RouterAgent(AgentBase):
DEPARTMENTS = {
"sales": {
"number": "+15551111111",
"hours": (9, 18),
"after_hours": "+15551111112"
},
"support": {
"number": "+15552222222",
"hours": (8, 20),
"after_hours": "+15552222223"
},
"billing": {
"number": "+15553333333",
"hours": (9, 17),
"after_hours": None # No after hours
}
}
def __init__(self):
super().__init__(name="router")
self.prompt_add_section(
"Role",
"You are the main receptionist. Help callers reach the right department."
)
self.prompt_add_section(
"Available Departments",
bullets=[
"Sales - for purchases and pricing",
"Support - for technical help",
"Billing - for payments and invoices"
]
)
self.add_language("English", "en-US", "rime.spore")
self._setup_functions()
def _get_department_number(self, dept: str) -> tuple:
"""Get appropriate number based on time."""
info = self.DEPARTMENTS.get(dept)
if not info:
return None, "Department not found"
hour = datetime.now().hour
start, end = info["hours"]
if start <= hour < end:
return info["number"], None
elif info["after_hours"]:
return info["after_hours"], "after-hours"
else:
return None, f"Billing is only available {start}AM to {end}PM"
def _setup_functions(self):
@self.tool(
description="Transfer to a department",
parameters={
"type": "object",
"properties": {
"department": {
"type": "string",
"enum": ["sales", "support", "billing"]
}
},
"required": ["department"]
}
)
def transfer(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
department = args.get("department", "")
number, status = self._get_department_number(department)
if not number:
return SwaigFunctionResult(status)
if status == "after-hours":
msg = f"Connecting you to {department} after-hours support."
else:
msg = f"Connecting you to {department}."
return (
SwaigFunctionResult(msg)
.connect(number, final=True)
)
@self.tool(description="Check department availability")
def check_availability(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
department = args.get("department", "")
info = self.DEPARTMENTS.get(department)
if not info:
return SwaigFunctionResult(
f"Unknown department. Available: {', '.join(self.DEPARTMENTS.keys())}"
)
hour = datetime.now().hour
start, end = info["hours"]
if start <= hour < end:
return SwaigFunctionResult(
f"{department.title()} is open now until {end}:00."
)
elif info["after_hours"]:
return SwaigFunctionResult(
f"{department.title()} regular hours are {start}:00 to {end}:00, "
"but after-hours support is available."
)
else:
return SwaigFunctionResult(
f"{department.title()} is closed. Hours: {start}:00 to {end}:00."
)
if __name__ == "__main__":
agent = RouterAgent()
agent.run()
Key Takeaways
- Multiple transfer methods - Cold, warm, SIP
- Department routing - Map names to numbers
- Time-based logic - Route by business hours
- Post-process for sequencing - Transfer after AI finishes
- Combine with SMS - Send context before transfer
Preparation for Lab 2.8
- Get test phone numbers for transfers
- Plan department structure
- Consider business hours
Lab Preview
In Lab 2.8, you will:
- Build department router
- Add time-based routing
- Implement transfer with SMS
- Test transfer scenarios