Как составить короткое голосовое меню для офиса на Asterisk
Для небольшого офиса я начинаю с одного короткого приветствия и трёх понятных пунктов. Неверный ввод или пауза запускают одну повторную попытку, затем звонок уходит сотруднику. Внутренний номер можно набирать во время приветствия, если он разрешён в контексте меню. Ниже — схема Asterisk, расписание и проверки перед запуском.
Почему длинное голосовое меню теряет звонящего
Звонящий в сервисную мастерскую хочет одного: узнать, готов ли его аппарат, или сдать новый. Он не хочет слушать историю компании, адрес сайта и девять пунктов, из которых ему нужен седьмой. Каждая лишняя секунда в меню — это шанс, что человек положит трубку и наберёт конкурента. Когда мы берём телефонию на ИТ-аутсорсинг для офиса, первым делом слушаем входящий сценарий глазами клиента, а не глазами того, кто его настраивал. Обычно хватает одного прослушивания, чтобы понять, где люди сходят с дистанции.
Типовая картина: приветствие на двадцать секунд, затем «для связи с отделом продаж нажмите один, для связи с бухгалтерией — два», и так до девятки. Потом второй уровень, где снова пять пунктов. Человек, который звонит второй раз за день, вынужден снова прослушать всё. Самое обидное, что половина пунктов ведёт к одним и тем же двум сотрудникам. Меню сложное не потому, что бизнес сложный, а потому, что каждый руководитель отдела в своё время попросил «свою кнопку».
Я исхожу из простого правила: меню должно отвечать на вопрос «зачем вы звоните», а не повторять оргструктуру. Оргструктура меняется, причины звонков обычно стабильнее. Если причин больше четырёх, сначала выясняю, кто реально отвечает на эти вопросы, а не добавляю кнопку каждому отделу. Качество голоса зависит и от сети; отдельно я разбирал основы диалплана Asterisk, но очередь не исправит меню, которое отправляет вызов не туда.
Сколько пунктов должно быть в IVR и как нарисовать дерево
Для офиса до пятидесяти мест я почти всегда делаю один уровень и три-четыре пункта. Больше одного уровня допустимо только там, где есть реально разные очереди с разными людьми, например приём техники и выдача готовой. Пункт «0» — всегда человек, администратор или дежурный. Его не прячут в конце длинной фразы, его называют сразу: «оператор — ноль». Человек, который ненавидит меню, должен получить выход с первой секунды, а не после того, как выслушает всё.
Дерево я рисую до того, как открываю extensions.conf. На листе: точка входа, проверка расписания, приветствие, пункты, что происходит при неверной клавише, что происходит при тишине, куда уходит звонок после второй ошибки. Если какую-то ветку нельзя нарисовать стрелкой до конкретного человека или очереди, значит, звонок в ней потеряется. Тупиков в правильном дереве нет: каждая стрелка заканчивается либо живым ответом, либо голосовой почтой с понятным владельцем, который её разбирает.
Отдельно — порядок пунктов. Самая частая причина звонка идёт под единицей и звучит первой. В мастерской это может быть статус ремонта, затем приём нового заказа. Пункты вроде «сотрудничество и реклама» я убираю из короткого меню: для них обычно достаточно перевода оператору. Утверждение «каждая кнопка стоит ровно четыре секунды» красиво выглядит в презентации, но без замеров это просто число с потолка.
- Один уровень меню, максимум два для разных очередей
- Три-четыре пункта по частоте причин звонка
- «0» — живой человек, озвучен в первой фразе
- Каждая ветка заканчивается человеком или почтой с владельцем
- Приветствие без рекламы и адреса сайта
Донабор внутреннего номера в Asterisk: Background и WaitExten
Постоянные клиенты и партнёры часто знают внутренний номер мастера. В Asterisk приложение Background() проигрывает приветствие и одновременно принимает DTMF; WaitExten() добавляет ожидание после окончания записи. Совпавший с введёнными цифрами экстеншен переводит вызов на соответствующую ветку. Диалплан разделён на контексты, и документация Asterisk прямо описывает их как способ отделять функциональность и задавать границы доступа. Поэтому входящий контекст меню не должен наследовать все возможности контекста сотрудников.
Есть и обычная грабля: если пункт меню «1», а добавочные тоже начинаются с единицы, после первой цифры станция может ждать продолжения номера до межсимвольного таймаута. Я развожу номерной план: пункты меню — 1, 2 и 0, добавочные — 3XX. Шаблон _3XX принимает ровно три цифры, начиная с тройки; X означает одну цифру от 0 до 9. А вот широкий include внутренних контекстов способен открыть из меню маршруты, которых внешнему звонящему видеть не следует. Контексты — граница доступа, а не косметическая группировка.
Ниже — минимальный пример. TIMEOUT(digit) задаёт паузу между цифрами уже набираемого экстеншена, TIMEOUT(response) — ожидание первой цифры. Поэтому WaitExten() оставлен без параметра: он использует response timeout, заданный выше. Имя custom/ts-menu условное; файл должен существовать в каталоге звуков вашей установки.
[ivr-main]
exten => s,1,Answer()
same => n,Set(TRIES=0)
same => n(menu),Set(TIMEOUT(digit)=3)
same => n,Set(TIMEOUT(response)=5)
same => n,Background(custom/ts-menu)
same => n,WaitExten()
exten => 1,1,Goto(q-status,s,1)
exten => 2,1,Goto(q-priemka,s,1)
exten => 0,1,Goto(q-admin,s,1)
exten => _3XX,1,Dial(PJSIP/${EXTEN},20)
same => n,Goto(q-admin,s,1)Если мастер не ответил за двадцать секунд, следующий приоритет переводит вызов администратору. В реальном плане проверьте, что имя PJSIP-устройства совпадает с добавочным, а целевая ветка существует.
Fallback при ошибке и тишине: как не оставить звонящего в пустоте
Две ситуации требуют отдельной обработки: человек нажал несуществующую клавишу или не нажал ничего. В Asterisk экстеншен i ловит некорректный ввод во время Background() или WaitExten(), а t — истечение времени ожидания. Без подходящего обработчика сценарий может закончиться отбоем. Но вывод «такой вызов не попадёт в CDR или отчёт» делать нельзя: это зависит от телефонии и отчётности вокруг диалплана. Ошибка меню и учёт вызова — разные вещи.
Моё правило: одна повторная попытка, потом человек. Не три круга меню, не «вы ввели неверный номер, попробуйте ещё раз» до бесконечности. Пожилой клиент с кнопочным телефоном, водитель за рулём, человек со старым аппаратом без тонального набора — все они должны попасть к живому сотруднику. Тишина в трубке тоже ответ: значит, меню человеку не подходит, и дальше его мучить бессмысленно.
exten => i,1,Playback(custom/ts-wrong-key)
same => n,Goto(retry,1)
exten => t,1,Goto(retry,1)
exten => retry,1,Set(TRIES=$[${TRIES}+1])
same => n,GotoIf($[${TRIES}>=2]?q-admin,s,1:s,menu)После переделки обязательно проверьте и экстеншен h, если вы что-то делаете по завершении звонка, например пишете отметку в CDR или шлёте уведомление. И ещё одно: не используйте в приветствии фразу «ваш звонок очень важен для нас». После неё звонящие почему-то начинают сомневаться именно в этом. Короткое «Здравствуйте, мастерская такая-то. Статус ремонта — один, сдать технику — два, администратор — ноль» работает лучше любой вежливой преамбулы.
Расписание, праздники и нерабочее время в диалплане
Меню, которое ночью предлагает «нажать два для связи с приёмкой», а потом молча гудит, хуже, чем отсутствие меню. Проверка времени должна стоять до приветствия. В Asterisk для этого есть GotoIfTime(): задаёте часы, дни недели, числа и месяцы, и звонок уходит в рабочую или нерабочую ветку. В нерабочей ветке — короткая фраза с часами работы и предложение оставить сообщение, которое утром кто-то конкретный разбирает.
[from-trunk]
exten => _X.,1,GotoIfTime(09:00-19:00,mon-fri,*,*?ivr-main,s,1)
same => n,GotoIfTime(10:00-16:00,sat,*,*?ivr-main,s,1)
same => n,Goto(ivr-closed,s,1)Праздники — отдельная история. Производственный календарь каждый год свой, и переносы выходных в него вшить заранее не получится. Я выношу праздничные даты в отдельные строки GotoIfTime перед рабочими или в переменную, которую администратор меняет без правки логики. Главное — чтобы правка праздников не требовала программиста и перезагрузки всего диалплана посреди рабочего дня. Обычно хватает dialplan reload, и это занимает секунды.
Проверяйте часовой пояс сервера. Если расписание 09:00–19:00 вычисляется по UTC, то для Москвы зимой оно сдвинется на три часа: вызовы будут считаться рабочими до 22:00 по Москве, а не до 16:00. В GotoIfTime() можно явно передать часовой пояс последним параметром; второй вариант — согласовать системную зону и расписание. Я проверяю границы вызовом до начала смены, сразу после открытия и около закрытия: именно на стыке времени ошибка заметна быстрее всего.
Кейс: голосовое меню сервисной мастерской «Точный штрих»
Сервисная мастерская «Точный штрих», 14 рабочих мест: приёмка, четыре мастера, выдача, администратор и офис. До нас входящий звонок встречало меню из семи пунктов с двумя уровнями, приветствие длилось около двадцати пяти секунд. Внутренние номера начинались с единицы, как и первый пункт меню, поэтому после нажатия «1» звонящий ждал ещё три секунды тишины. Экстеншенов i и t не было: неверная клавиша обрывала звонок. Руководитель жаловался, что «клиенты не дозваниваются», хотя линии были свободны.
Мы сократили меню до трёх пунктов: статус ремонта, сдать технику, администратор под нулём. Приветствие записали заново, уложились в восемь секунд. Внутренние номера мастеров перенесли в диапазон 3XX, донабор разрешили только по шаблону _3XX, без доступа к внешним направлениям. Добавили одну повторную попытку и выход на администратора после второй ошибки или тишины. Расписание вынесли в GotoIfTime перед меню, нерабочее время ведёт в голосовую почту, которую утром разбирает приёмщик. Работа заняла два дня, включая запись новых файлов и тестовые звонки со всех типов телефонов.
Через месяц картина по журналу звонков стала другой: обрывы в меню практически исчезли, большая часть клиентов выбирает пункт за первые секунды, а постоянные заказчики сразу набирают мастера. Администратор перестал слышать «я до вас полчаса не мог дозвониться». Никакого нового оборудования не покупали: та же станция, тот же провайдер, просто дерево маршрутов стало честным. Если честно, это самая дешёвая доработка телефонии, которую я знаю, и самая недооценённая.
Чек-лист перед запуском меню и что делать дальше
Перед тем как переключить входящую линию на новое меню, я прохожу один и тот же список. Звоню с мобильного и стационарного телефона, проверяю набор во время приветствия, неверную клавишу, молчание и выключенный внутренний номер. Отдельно проверяю нерабочее время и доставку голосового сообщения ответственному. Если для voicemail включено IMAP-хранение, отдельный ящик для голосовых сообщений не смешивает их с обычным INBOX и снижает риск удалить обычную переписку при очистке voicemail.
Отдельно проверяю доступ: из контекста меню нельзя набрать внешний номер или попасть в служебные экстеншены. Риск появляется не из-за самого IVR, а из-за слишком широких маршрутов и неверно заданных границ контекстов. Документирую также, кто отвечает за приветствие, расписание и праздники. Без владельца меню постепенно превращается в склад кнопок — каждая когда-то казалась срочной.
Меню — это первый контакт. Дальше вызов может уйти в очередь, и там начинаются другие решения: кому звонить первым, сколько ждать и что делать, если все заняты. Для этого есть отдельный разбор настройки очередей Asterisk. Меню должно передать туда вызов с понятными данными, а не пытаться заменить логику очереди.
- Звонок с мобильного, городского и аппарата без тонального набора
- Неверная клавиша, тишина, донабор посреди фразы
- Нерабочее время и праздничная дата
- Попытка набрать внешний номер из меню
- Назначен ответственный за приветствие и праздники
Частые вопросы
Сколько пунктов должно быть в голосовом меню офиса?
Для офиса до 50 мест — три-четыре пункта и один уровень. Пункт «0» всегда ведёт к живому человеку и называется в первой фразе.
Как сделать донабор внутреннего номера во время приветствия в Asterisk?
Проигрывать приветствие через Background(), после него ставить WaitExten(), а в контексте меню разрешить шаблон внутренних номеров, например _3XX, без include всего внутреннего контекста.
Почему после нажатия «1» в меню возникает пауза?
Если внутренние номера тоже начинаются с единицы, Asterisk ждёт межсимвольный таймаут. Разведите пункты меню и внутренние номера по разным первым цифрам.
Что делать, если звонящий нажал неверную клавишу или молчит?
Обработать экстеншены i и t: одна повторная попытка, затем перевод на администратора. Звонок не должен обрываться.
Как настроить нерабочее время в меню Asterisk?
Поставить GotoIfTime() до приветствия и вести нерабочее время в отдельную ветку с часами работы и голосовой почтой. Проверьте часовой пояс сервера.
Источники
- Asterisk Documentation: Background и WaitExten — Приём DTMF во время воспроизведения, ожидание добавочного и применение TIMEOUT(response): https://docs.asterisk.org/Deployment/Basic-PBX-Functionality/Auto-attendant-and-IVR-Menus/Background-and-WaitExten-Applications/
- Asterisk Documentation: TIMEOUT — Различие digit и response timeout, поведение при истечении времени: https://docs.asterisk.org/Latest_API/API_Documentation/Dialplan_Functions/TIMEOUT/
- Asterisk Documentation: GotoIfTime — Порядок аргументов расписания и передача часового пояса: https://docs.asterisk.org/Latest_API/API_Documentation/Dialplan_Applications/GotoIfTime/
- Asterisk Documentation: Special Dialplan Extensions — Назначение экстеншенов i, t и h при неверном вводе, таймауте и отбое: https://docs.asterisk.org/Configuration/Dialplan/Special-Dialplan-Extensions/
- Asterisk Documentation: Contexts, Extensions, and Priorities — Роль контекстов как границ функциональности и доступа, синтаксис Dial(PJSIP/...,20): https://docs.asterisk.org/Configuration/Dialplan/Contexts-Extensions-and-Priorities/
- Asterisk Documentation: IMAP Voicemail Storage — Различие отдельного и общего IMAP-ящика для голосовой и обычной почты: https://docs.asterisk.org/Configuration/Applications/Voicemail/IMAP-Voicemail-Storage/Separate-vs.-Shared-E-mail-Accounts
