Реализация передачи диалога от AI-бота к живому оператору (Handoff)
Handoff — критически важная функция любого production-бота. Плохая передача разрушает клиентский опыт: клиент заново объясняет ситуацию, оператор не понимает контекста. Хороший handoff — бесшовный, с полным контекстом.
Триггеры эскалации
Явные триггеры:
- Клиент пишет «оператор», «живой человек», «позвоните мне», «не помогаете»
- Клиент нажал кнопку «Соединить с оператором»
Автоматические триггеры:
- Низкая уверенность бота (confidence < 0.6) по 3 подряд сообщениям
- Отрицательный sentiment нарастает — frustrated customer detection
- Тема попала в список «эскалировать всегда» (юридические претензии, угрозы, VIP)
- Цикличный диалог — пользователь повторяет один вопрос разными словами
class EscalationDetector:
def should_escalate(self, dialog: Dialog) -> EscalationReason | None:
if self.explicit_request_detected(dialog.last_message):
return EscalationReason.EXPLICIT_REQUEST
if dialog.bot_confidence_history[-3:] == [low, low, low]:
return EscalationReason.LOW_CONFIDENCE
sentiment = self.sentiment_analyzer.analyze(dialog.last_5_messages)
if sentiment.score < -0.7 and sentiment.trend == "worsening":
return EscalationReason.FRUSTRATED_CUSTOMER
return None
Контекст для оператора
Передача контекста — ключевое. Оператор получает:
- Полная история диалога с timestamps
- Профиль клиента из CRM (история заказов, открытые тикеты, сегмент)
- Причина эскалации (почему бот передал)
- Предложение бота по решению (которое не сработало)
- Детектированная тема и sentiment
Визуализация в интерфейсе оператора: дашборд с цветовой кодировкой эмоций, карточка клиента, история диалога.
Очередь и маршрутизация
При эскалации бот ставит клиента в очередь к нужному оператору. Пока клиент ждёт:
- Сообщает примерное время ожидания
- Предлагает оставить контакты для callback
- Продолжает отвечать на простые вопросы пока ждёт оператора
async def initiate_handoff(dialog: Dialog, reason: EscalationReason):
# Находим доступного оператора
available_agent = await agent_queue.find_available(
skills=classify_required_skills(dialog),
priority=get_customer_priority(dialog.user_id)
)
# Уведомляем клиента
wait_time = await agent_queue.estimate_wait(available_agent)
await bot.send(dialog.channel, f"Соединяю с оператором. Ожидайте ~{wait_time} мин.")
# Передаём контекст оператору
await agent_dashboard.notify(available_agent, {
"dialog": dialog,
"reason": reason,
"customer_profile": await crm.get_profile(dialog.user_id),
"summary": await ai.summarize_dialog(dialog)
})
После завершения разговора с оператором
Обратная передача боту (если нужна): оператор закончил, клиент снова пишет → бот подхватывает с обновлённым контекстом (что решил оператор).
Обучение на эскалациях: регулярный анализ причин эскалации → обновление базы знаний, дополнение промптов, выявление пробелов в функционале бота.
Интеграция с helpdesk
- Zendesk: Handoff API, создание тикета при эскалации
- Freshdesk: Agent SDK для передачи контекста
- Bitrix24: Live Chat API, очереди операторов
- Кастомные платформы: WebSocket-соединение между ботом и оператором







