scrpt .ru
· 7 min
Оператор колл-центра работает с данными клиента и звонком в едином интерфейсе

Входящий звонок требует от оператора нескольких решений за первые секунды: понять, кто обращается, увидеть последний заказ, оценить историю сообщений и выбрать действие. Если для этого приходится открывать 1С, админку и отдельный телефонный клиент, разговор начинается с поиска. В описанном кейсе именно такая связка стала причиной отдельного операторского интерфейса поверх Asterisk.

Полезность такого проекта определяется не числом интегрированных сервисов. Рабочее место должно показывать контекст в момент события, давать предсказуемое управление и оставаться поддерживаемым при изменении сценариев. Разберём, как для этого разделить роли телефонии, прикладной логики и интерфейса.

Начинать стоит с пути одного звонка

В исходном процессе звонок приходил в отдельный сервис 1С. Оператору приходилось искать пользователя, затем его заказ и связанные сведения в разных разделах. Пока он переключался между окнами, клиент уже ждал ответа.

Поэтому требования стоит формулировать как конкретный набор состояний на одном экране. При входящем вызове оператор видит уведомление и кнопки «принять» или «отклонить», карточку клиента, последний заказ, связанные SMS и обращения. В том же месте нужны завершение, удержание, перевод, новый звонок и рабочие статусы: онлайн, офлайн, готовность, пауза.

Такое описание помогает отделить действительно необходимый контекст от желания собрать в интерфейсе всю базу компании. Для сервисной команды важны одни поля — адрес и история выездов, для интернет-магазина другие — заказ, доставка и возвраты. Список лучше проверять вместе с операторами: они видят, какие переключения реально тормозят разговор.

Телефония остаётся источником событий

Asterisk в этой архитектуре сохраняет роль ядра телефонии. Он сообщает, что произошло со звонком или оператором: входящий и исходящий вызов, ответ, удержание, возврат с удержания. Через REST-интерфейс можно получить текущие звонки, ответить, запустить IVR и выполнить другие операции.

Это разделение полезно для надёжности. Интерфейс не должен самостоятельно угадывать, принят ли вызов или завершён: он получает обновление из события. А действие оператора превращается в явную команду, которую сервис передаёт в Asterisk. Появляются два понятных направления потока: событие меняет экран, команда меняет состояние телефонии.

В кейсе соединение с Asterisk слушает события Stasis по WebSocket. Сервис отбирает типы вроде StasisStart, ChannelHold, ChannelUnhold, Dial и ChannelDestroyed, после чего направляет каждый тип в отдельный обработчик. Для входящего звонка создаётся объект Call с идентификатором канала; затем роутер распределяет вызов среди свободных операторов.

BFF-слой берёт на себя прикладные правила

Прямое соединение браузера с Asterisk быстро создаёт проблемы: в клиентском коде оказываются детали телефонии, а правила доступа и распределения расползаются по экранам. Между ними нужен отдельный слой для интерфейса — BFF, backend for frontend.

В рассмотренной реализации эту роль выполняют Python, Django и Django Channels. Сервис принимает события Asterisk, сопоставляет номер с данными клиента, применяет правила и отправляет нужное обновление во Vue-интерфейс. В обратную сторону он получает действие оператора и вызывает REST-операцию Asterisk. Здесь же удобно держать авторизацию, аудит действий, доступ к данным заказа и ограничения по ролям.

Отдельный слой позволяет менять сценарий без переделки ядра телефонии. Например, при отсутствии ответа звонок можно передать следующему свободному сотруднику. В источнике это делает периодическая задача Celery с учётом приоритета; для другой команды можно добавить очередь по навыкам, VIP-статус или отдельные правила для ночной смены. Правило существует в прикладном сервисе и проверяется как часть процесса, а не прячется в компоненте кнопки.

При выборе библиотек стоит проверить не только удобство API. В кейсе готовый клиент ari-py оказался давно неподдерживаемым и рассчитанным на Python 2, поэтому команда написала собственную обвязку над ARI. Такой факт важнее красивого списка технологий: зависимость, которую трудно обновить, превращает дальнейшую поддержку в отдельный риск.

Реальное время требует аккуратной модели состояний

WebSocket избавляет интерфейс от постоянного опроса сервера, однако сам по себе не делает экран корректным. Одно действие может породить несколько событий, соединение иногда обрывается, а оператор может нажать кнопку в момент, когда статус уже изменился.

Для каждого вызова полезно хранить идентификатор канала, текущий статус, время последнего события и версию обновления. После восстановления соединения интерфейс запрашивает актуальное состояние, а не пытается собрать его только из пропущенных уведомлений. Команды «удержать», «перевести» и «завершить» стоит блокировать на время выполнения и показывать результат только после подтверждения от сервиса.

В исходном проекте обработчики событий вынесены в отдельные модули и имеют единый контракт handle(data). Это упрощает добавление логики: новый тип события получает свой обработчик, а тесты можно писать на конкретный маршрут. Аналогичный принцип полезен и в бизнес-правилах — распределение, поиск клиента, журналирование и уведомления легче менять независимо друг от друга.

Аудиоканал не обязан переезжать в браузер

Единый экран не всегда означает единую технологию для всех частей звонка. Команда проверяла WebRTC и SIP over WebSocket, но на длинных разговорах заметила задержку звука. В рабочем варианте аудио осталось в SIP-клиенте: оператор снимает трубку там, а контекст и управление получает в веб-интерфейсе.

Это разумное ограничение, если качество связи важнее визуальной цельности решения. Браузерный сценарий можно повторно проверить после обновления инфраструктуры и клиентов, но сначала нужна измеримая проверка: задержка на длинном звонке, стабильность при нагрузке, работа гарнитур и поведение при потере сети. Перенос аудио имеет смысл только после такого теста.

Архитектура операторского места складывается вокруг простой границы: Asterisk отвечает за телефонию, прикладной сервис — за правила и данные, интерфейс — за быстрые действия человека. Когда эти роли разделены, карточка клиента появляется вовремя, сценарии распределения меняются без опасных правок ядра, а ограничения аудиоканала остаются видимым инженерным решением, а не скрытой проблемой операторов.

Обсудить проект

Остались вопросы? Напишите нам

Оставьте имя и телефон — перезвоним в течение часа, разберём задачу и предложим решение.

Также читайте нас в Telegram.