В этой статье я расскажу как с помощью asterisk и yandex speechkit, можно организовать интерактивный диалог с помощью голоса. В качестве примера создадим систему для голосового сбора показаний счетчиков горячей и холодной воды, сразу скажу, что это просто пример, а не рабочая версия, просто здесь показана сама идея организации диалога.
Итак, настройка Asterisk, для простоты настраивать будем через ael:
На Астериске организован цикл while
1234567 => { Answer; Set(audio=/var/lib/asterisk/sounds/custom/enteraccount); flag=1; label=1; while (${flag} = 1){ Background(${audio}); Record(/tmp/${UNIQUEID}label${label}.wav,3,20); Agi(yandex.agi,/tmp/${UNIQUEID}, ${label}); label=${label}+1; }; };
В начале позвонившему проигрывается фудио файл «enteraccount» с просьбой назвать лицевой счет
На каждом шаге цикла, позвонившему человеку проигрывается аудио файл «audio», путь к которому получен из agi скрипта, далее записывается ответ, и путь к полученному записанному файлу отправляется agi скрипту, также в скрипт отправляется текущий шаг «label», после чего шаг увеличивается на 1, и цикл повторяется. Из agi скрипта астерис получает 2 параметра, это обязательный параметр «audio», путь к аудиофайлу который нужно проиграть, и не обязательный «label», метка с помощью которой происходит синхронизация астериска и agi. Выход из цикла происходит если человек положил трубку, а также если из agi прийдет параметр «flag» не равный 1.
Еще неплохо бы после завершения вызова удалить все временные файлы, для этого в макросе который срабатывает после завершения вызова пропишем
[macro-hangupcall] exten => s,1,System(find /tmp/ -name "${UNIQUEID}*" | xargs rm);
На этом настройка астериска завершена.
Далее сам скрипт yandex.agi, написанный на python, для работы необходимо установить pyst
# wget http://heanet.dl.sourceforge.net/project/pyst/pyst/0.6.50/pyst-0.6.50.zip # unzip pyst-0.6.50.zip # cd pyst-0.6.50 # python setup.py install
Сам agi:
#!/usr/bin/python # -*- coding: utf-8 -*- import urllib2 import urllib import sys import random from xml.dom.minidom import * import os import asterisk.agi agi=asterisk.agi.AGI() uuid='' key='28b22b47-a7b0-40d2-892d-ad79bbdbc304' def yadexASR(uuid, key, topic, callid): # Функция для преобразования аудио файла в текст # Генерим уникальный код id для запроса while (len(uuid)<32): uuid=uuid+random.choice('1234567890abcdef') #Ссылка для пост запроса url = 'https://asr.yandex.net/asr_xml?uuid=%s&key=%s&topic=%s&lang=ru-RU' % (uuid, key, topic) filename = callid+'.wav' #Считываем файл в двоичном режиме audio = open(filename,'rb').read() #Указываем тип аудиофайла для заголовка в POST запрос headers={'Content-Type': 'audio/x-pcm;bit=16;rate=8000'} #Отправляем запрос request = urllib2.Request(url, data=audio, headers=headers) response = urllib2.urlopen(request) #Считываем ответ answer=response.read() # Создаем xml файл для полученного ответа f_answer=('%s.xml' % callid) f=open(f_answer, 'w') # записываем в файл ответ полученный на POST запрос (ответ получен в xml формате) f.write(answer) f.close() # Парсим xml файл xml = parse(f_answer) # Выбираем из файла значение между тегами variant var = xml.getElementsByTagName('variant') # Выбираем первое значение из xml файла, оно же наиболее точное в распознании result=var[0].childNodes[0].nodeValue return result def generate(key,text, file): # Функция для преобразования текста в аудио формат #Форматируем текс для GET запрса (нужен для корректной обработки кирилици) text_f=urllib.quote_plus(text.encode('utf-8')) #отправляем запрос на яндекс url='http://tts.voicetech.yandex.net/generate?text=%s&format=mp3&lang=ru-RU&speaker=jane&key=%s' % (text_f, key) # Сохраняем айдио файл в file urllib.urlretrieve(url, file) if sys.argv[2].replace(' ','')=="1": agi.verbose('label='+sys.argv[2].replace(' ','')) try: # Распознаем полученный файл result=yadexASR(uuid, key, 'notes', sys.argv[1]+'label'+sys.argv[2].replace(' ','')) # Формируем ответ file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Вы сказали '+result+u'Скажите да или Нет', file+'.mp3') agi.set_variable('audio', file) except: agi.set_variable('audio', '/var/lib/asterisk/sounds/custom/enteraccount') agi.set_variable('label', '0') agi.verbose('label=0') elif sys.argv[2].replace(' ','')=="2": agi.verbose('label='+sys.argv[2].replace(' ','')) try: result=yadexASR(uuid, key, 'notes', sys.argv[1]+'label'+sys.argv[2].replace(' ','')) except: agi.set_variable('label', '1') if 'Да' in str(result.encode('utf-8')): agi.verbose('Yes') #/////////////////////////////// # Здест должна быть проверка на существование абонента в базе данных #///////////////////////////// file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Показания какого счетчика вы хотите сообщить горячего или холодного?', file+'.mp3') agi.set_variable('audio', file) elif 'Нет' in str(result.encode('utf-8')): agi.verbose('No') # Ставим метку в 0 что бы вернуться к шагй 1 и проигрываем файл agi.set_variable('label', '0') agi.set_variable('audio', '/var/lib/asterisk/sounds/custom/enteraccount') else: agi.verbose('Error') # Если ответ не да и ни нет, говорим об ошибки и ставим метку на 1 что бы вернуться к шагу 2 agi.set_variable('label', '1') elif sys.argv[2].replace(' ','')=="3": agi.verbose('label='+sys.argv[2].replace(' ','')) try: result=yadexASR(uuid, key, 'notes', sys.argv[1]+'label'+sys.argv[2].replace(' ','')) except: agi.set_variable('label', '2') if 'Горяч' in str(result.encode('utf-8')): agi.verbose('Hot') file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Сообщите показания горячего счетчика', file+'.mp3') agi.set_variable('audio', file) elif 'Холод' in str(result.encode('utf-8')): agi.verbose('Cold') file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Сообщите показания холодного счетчика', file+'.mp3') agi.set_variable('audio', file) else: agi.verbose('Error') agi.set_variable('label', '2') elif sys.argv[2].replace(' ','')=="4": agi.verbose('label='+sys.argv[2].replace(' ','')) try: result=yadexASR(uuid, key, 'notes', sys.argv[1]+'label'+sys.argv[2].replace(' ','')) file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Ваши показания '+result+u'Скажите да или Нет', file+'.mp3') agi.set_variable('audio', file) except: agi.set_variable('label', '3') agi.verbose('label=3') elif sys.argv[2].replace(' ','')=="5": try: result=yadexASR(uuid, key, 'notes', sys.argv[1]+'label'+sys.argv[2].replace(' ','')) except: agi.set_variable('label', '1') if 'Да' in str(result.encode('utf-8')): #///////////// #Обрабатываем результат и заносим в базу #///////////// file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Хотите сообщить показания другого счетчика да или нет?', file+'.mp3') agi.set_variable('audio', file) elif 'Нет' in str(result.encode('utf-8')): agi.set_variable('label', '2') file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Показания какого счетчика вы хотите сообщить горячего или холодного?', file+'.mp3') agi.set_variable('audio', file) else: agi.verbose('Error') agi.set_variable('label', '2') elif sys.argv[2].replace(' ','')=="6": try: result=yadexASR(uuid, key, 'notes', sys.argv[1]+'label'+sys.argv[2].replace(' ','')) except: agi.set_variable('label', '1') if 'Да' in str(result.encode('utf-8')): file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Показания какого счетчика вы хотите сообщить горячего или холодного?', file+'.mp3') agi.set_variable('audio', file) agi.set_variable('label', '2') elif 'Нет' in str(result.encode('utf-8')): file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Досвидания', file+'.mp3') agi.set_variable('audio', file) else: agi.verbose('Error') file=sys.argv[1]+'outlabel'+sys.argv[2].replace(' ','') generate(key, u'Хотите сообщить показания другого счетчика да или нет', file+'.mp3') agi.set_variable('label', '5')