Как стать автором
Обновить

Asterisk распознавание речи через Google + умный IVR

Время на прочтение6 мин
Количество просмотров9.9K


Доброго времени суток, уважаемые хабра-пользователи.
В одном проекте необходимо было сделать умный IVR на базе IP-АТС Asterisk. Что подразумевается под словом «умный»: при звонке на определенный номер станция просит назвать имя абонента, человек на другом конце провода называет имя и станция связывает его с нужным абонентом.

В моем случае я использовал уже готовую сборку AsteriskNow с уже предустановленным FreePBX, хотя в данном случае это особой роли не играет, т.к. отличия будут только в редактировании dial-плана.


Шаг 1:


Работа с Google

Первое, что необходимо – это каким-то образом распознать речь позвонившего. На хабре было достаточно (раз, два) статей, как это делать с помощью Google Translate. Я же решил взять готовые скрипты, найденные на просторах github: googletts.agi – для того, что бы научить Asterisk говорить и speech-recog.agi – для того, чтобы Asterisk умел распознавать речь.

Файлы googletts.agi и speech-recog.agi закидываем в папку /var/lib/asterisk/agi-bin.
Для успешной работы скриптов необходимо иметь следующие пакеты: Perl, perl-libwww, IO-Socket-SSL, flac, sox, mpg123. Все пакеты у меня успешно скачались и установились с репозиториев (через yum install), за исключением mpg123, его пришлось качать отдельно.

В googletts.agi меняем значение переменной $lang с en на ru, мы ведь хотим, что бы Asterisk заговорил по-русски.
В speech-recog.agi меняем значение переменной $language с en-US на ru-RU для того, чтобы Google возвращал результат на русском языке.
Все, больше в скриптах я не трогал ничего.

Шаг 2:


Написание dial-плана

Как я говорил выше, у меня установлен FreePBX, поэтому все изменения я буду вносить в файл extensions_custom.conf.
Для начала неплохо поприветствовать позвонившего и дать ему комментарий, что делать дальше.

exten => 100,1,Answer()
exten => 100,n,agi(googletts.agi,”Здравствуйте! После звукового сигнала произнесите имя абонента.”,ru)


Дальше с помощью speech-recog.agi, слушаем, что говорит пользователь, записываем, конвертируем, отправляем в Google и получаем от него результаты.

exten => 100,n(record),agi(speech-recog.agi,ru-RU)

Далее с помощью функции GotoIf делаем проверку того, как отработал скрипт.
Скрипт возвращает следующие значения:

status: возвращает статус выполнения. 0 означает успех
utterance: строка, возвращаемая Google
confidence: значение от 0 до 1, показывающее вероятность правильного распознавания

exten => 100,n,GotoIf($[$["${status}" = "0"] & $["${confidence}" > "0.8"]]?if1:retry)

В случае успеха проверки переходим к событию if1, если неудача, переходим к событию retry, в котором попросим пользователя повторить.

exten => 100,n(retry),agi(googletts.agi,”Пожалуйста, повторите”,ru)

Дальше переходим к работе непосредственно с самой строкой, которую мы получили от Google. Необходимо сравнить полученную строку ${utterance} с каким-то шаблоном и решить, что делать дальше. Воспользуемся функцией GotoIf

exten => 100,n(if1),GotoIf($[“${utterance}” = “вася”]?vasya:retry)

Если строка, полученная от Google, совпадает с «вася», переходим к событию vasya, если не совпадает, просим пользователя повторить.

И осталось только позвонить Васе

exten => 100,n(vasya),Dial(SIP/101)

Dial-план полностью:
exten => 100,1,Answer()
exten => 100,n,agi(googletts.agi,”Здравствуйте! После звукового сигнала произнесите имя абонента.”,ru)
exten => 100,n(record),agi(speech-recog.agi,ru-RU)
exten => 100,n,GotoIf($[$["${status}" = «0»] & $["${confidence}" > «0.8»]]?if1:retry)
exten => 100,n(if1),GotoIf($[“${utterance}” = “вася”]?vasya:retry)
exten => 100,n(retry),agi(googletts.agi,”Пожалуйста повторите”,ru)
exten => 100,n(vasya),Dial(SIP/101)


Вариации на тему

  • Для простоты примера я привел всего одно условие сравнения того, что услышали с шаблоном, по факту их может и должно быть больше.
  • Не очень красиво звучат фразы, которые говорит «железная леди» от Google. В рабочем варианте, конечно, неплохо записать фразы «живым» голосом и воспроизводить их при помощи функции Playback.


Тонкости

При подобном виде работы с Google Translate стоит учитывать, что работает он хорошо, но не совершенно и это нужно учитывать при создании тех шаблонов, с которыми будем сравнивать полученный от Google результат.

Вот пример грабель, на которые я наступил:
Меня зовут Кирилл (две «Л» на конце). Google по каким-то только ему известным причинам через раз возвращал либо “кирил”, либо “кирилл”.

Послесловие

Есть подозрение, что сравнение можно было реализовать каким-нибудь более технологичным способом, с радостью выслушаю ваше мнение и предложения в комментариях.
И еще остается открытый вопрос в масштабе: что будет, если абонентов много, сколько займет время на прохождение всех сравнений, если, конечно, их реализовывать предложенным мной способом. Но для небольшой АТС примерно на 20 абонентов данный способ является приемлемым.

Спасибо за внимание.

UPD

Примеры

В качестве примера я использовал чуть-чуть другой dial-план, но суть от этого не меняется.

Dial-план примера:
exten => 8251,1,Answer()
exten => 8251,n,MixMonitor(/var/spool/asterisk/monitor/8251/${CDR(start)}-${DST-NUM}-${ID_CALL}-full.wav)
exten => 8251,n,agi(googletts.agi,«Пожалуйста скажите имя абонента с которым вас соеденить.»,ru)
exten => 8251,n(record),agi(speech-recog.agi,ru-RU)
exten => 8251,n,GotoIf($[$["${status}" = «0»] & $["${confidence}" > «0.5»]]?if1:retry)
exten => 8251,n(if1),GotoIf($["${utterance}" = «александр»]?al:retry)
exten => 8251,n(al),Dial(SIP/8201)
exten => 8251,n(retry),agi(googletts.agi,«Пожалуйста повторите?»,ru)
exten => 8251,n,goto(record)

Ссылка на запись с успешным распознаванием.
Вывод Astesterisk'а:
— Executing [8251@from-internal:1] Answer(«SIP/8211-00000000», "") in new stack
— Executing [8251@from-internal:2] MixMonitor(«SIP/8211-00000000», "/var/spool/asterisk/monitor/8251/2013-04-24 10:28:03---full.wav") in new stack
— Executing [8251@from-internal:3] AGI(«SIP/8211-00000000», «googletts.agi,»Пожалуйста скажите имя абонента с которым вас соеденить.",ru") in new stack
— Launched AGI Script /var/lib/asterisk/agi-bin/googletts.agi
== Begin MixMonitor Recording SIP/8211-00000000
— Playing '/tmp/16ae8d012843179807cfdabd9a34608f' (escape_digits=) (sample_offset 0)
— Playing '/tmp/ef3ccb070117857a8045932052f3fd7b' (escape_digits=) (sample_offset 0)
— <SIP/8211-00000000>AGI Script googletts.agi completed, returning 0
— Executing [8251@from-internal:4] AGI(«SIP/8211-00000000», «speech-recog.agi,ru-RU») in new stack
— Launched AGI Script /var/lib/asterisk/agi-bin/speech-recog.agi
— <SIP/8211-00000000> Playing 'beep.ulaw' (language 'en')
— <SIP/8211-00000000>AGI Script speech-recog.agi completed, returning 0
— Executing [8251@from-internal:5] GotoIf(«SIP/8211-00000000», «1?if1:retry») in new stack
— Goto (from-internal,8251,6)
— Executing [8251@from-internal:6] GotoIf(«SIP/8211-00000000», «1?al:retry») in new stack
— Goto (from-internal,8251,7)
— Executing [8251@from-internal:7] Dial(«SIP/8211-00000000», «SIP/8201») in new stack
== Using SIP RTP TOS bits 184
== Using SIP RTP CoS mark 5
— Called SIP/8201
— SIP/8201-00000001 is ringing
— SIP/8201-00000001 answered SIP/8211-00000000
— Executing [h@from-internal:1] Hangup(«SIP/8211-00000000», "") in new stack
== Spawn extension (from-internal, h, 1) exited non-zero on 'SIP/8211-00000000'
== Spawn extension (from-internal, 8251, 7) exited non-zero on 'SIP/8211-00000000'
== MixMonitor close filestream
== End MixMonitor Recording SIP/8211-00000000

Ссылка на НЕ удачное распознавание.
Вывод Astesterisk'а:
— Executing [8251@from-internal:1] Answer(«SIP/8211-00000002», "") in new stack
— Executing [8251@from-internal:2] MixMonitor(«SIP/8211-00000002», "/var/spool/asterisk/monitor/8251/2013-04-24 10:36:29---full.wav") in new stack
— Executing [8251@from-internal:3] AGI(«SIP/8211-00000002», «googletts.agi,»Пожалуйста скажите имя абонента с которым вас соеденить.",ru") in new stack
— Launched AGI Script /var/lib/asterisk/agi-bin/googletts.agi
== Begin MixMonitor Recording SIP/8211-00000002
— Playing '/tmp/16ae8d012843179807cfdabd9a34608f' (escape_digits=) (sample_offset 0)
— Playing '/tmp/ef3ccb070117857a8045932052f3fd7b' (escape_digits=) (sample_offset 0)
— <SIP/8211-00000002>AGI Script googletts.agi completed, returning 0
— Executing [8251@from-internal:4] AGI(«SIP/8211-00000002», «speech-recog.agi,ru-RU») in new stack
— Launched AGI Script /var/lib/asterisk/agi-bin/speech-recog.agi
— <SIP/8211-00000002> Playing 'beep.ulaw' (language 'en')
— <SIP/8211-00000002>AGI Script speech-recog.agi completed, returning 0
— Executing [8251@from-internal:5] GotoIf(«SIP/8211-00000002», «1?if1:retry») in new stack
— Goto (from-internal,8251,6)
— Executing [8251@from-internal:6] GotoIf(«SIP/8211-00000002», «0?al:retry») in new stack
— Goto (from-internal,8251,8)
— Executing [8251@from-internal:8] AGI(«SIP/8211-00000002», «googletts.agi,»Пожалуста повторите?",ru") in new stack
— Launched AGI Script /var/lib/asterisk/agi-bin/googletts.agi
— Playing '/tmp/0c5de11c17dda57dabeaebe335110036' (escape_digits=) (sample_offset 0)
— <SIP/8211-00000002>AGI Script googletts.agi completed, returning 0
Теги:
Хабы:
Всего голосов 16: ↑16 и ↓0+16
Комментарии11

Публикации

Истории

Ближайшие события