В этой статье я расскажу как с помощью 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')