Intermediate
Escalation & Handoff
Detect when the AI cannot adequately help a customer and seamlessly hand off to a human agent with full conversation context, customer information, and suggested resolution.
Escalation Detector
# app/escalation/detector.py
class EscalationDetector:
ESCALATION_PHRASES = [
"speak to a human", "talk to agent", "real person",
"manager", "supervisor", "complaint", "not helpful",
"this is urgent", "cancel my account", "legal",
]
def should_escalate(self, message: str, confidence: float,
turn_count: int, sentiment: float = 0) -> dict:
reasons = []
# Low confidence from retrieval
if confidence < 0.3:
reasons.append(f"Low retrieval confidence: {confidence:.2f}")
# Explicit escalation request
msg_lower = message.lower()
for phrase in self.ESCALATION_PHRASES:
if phrase in msg_lower:
reasons.append(f"Escalation phrase: '{phrase}'")
break
# Too many turns without resolution
if turn_count > 5:
reasons.append(f"Extended conversation: {turn_count} turns")
# Negative sentiment
if sentiment < -0.5:
reasons.append(f"Negative sentiment: {sentiment:.2f}")
return {
"should_escalate": len(reasons) > 0,
"reasons": reasons,
"priority": "high" if len(reasons) > 1 else "normal",
}
Agent Router
# app/escalation/router.py
import uuid
from datetime import datetime
from dataclasses import dataclass, field
@dataclass
class Ticket:
id: str = field(default_factory=lambda: str(uuid.uuid4()))
session_id: str = ""
customer_context: dict = field(default_factory=dict)
conversation_history: list = field(default_factory=list)
escalation_reasons: list = field(default_factory=list)
priority: str = "normal"
assigned_agent: str = ""
status: str = "open"
created_at: str = field(default_factory=lambda: datetime.utcnow().isoformat())
class AgentRouter:
def __init__(self):
self.tickets: dict[str, Ticket] = {}
self.available_agents: list[dict] = []
def create_ticket(self, session_id, context, history, reasons, priority):
ticket = Ticket(
session_id=session_id, customer_context=context,
conversation_history=history, escalation_reasons=reasons,
priority=priority,
)
# Auto-assign based on priority and availability
if self.available_agents:
ticket.assigned_agent = self.available_agents[0]["id"]
ticket.status = "assigned"
self.tickets[ticket.id] = ticket
return ticket
def get_open_tickets(self):
return [t for t in self.tickets.values() if t.status in ("open", "assigned")]
Smooth handoff: Always pass the full conversation history to the human agent. Nothing frustrates customers more than repeating themselves. Include a summary of what the bot understood and what it could not resolve.
Key Takeaways
- Multiple escalation signals (low confidence, explicit request, sentiment, turn count) catch different failure modes.
- Priority scoring ensures urgent issues get human attention faster.
- Full context transfer prevents customers from repeating information to the agent.
Lilly Tech Systems