Realização de um guia de voz baseado no YandexSpeechKit

Várias implementações são apresentadas na Internet, mas, na minha opinião, são todas bastante simples. Quero apresentar minha versão do guia de voz para um asterisco.


Nota: Eu não sou um programador profissional e talvez algumas soluções possam parecer loucas para você. Alguns truques podem estar desatualizados. Estou pronto para aceitar críticas e corrigir o sistema para melhor.


Breve descrição dos recursos:


O usuário entra na URA, faz sua solicitação e, na maioria dos casos, chega onde precisa. As estatísticas também são parafusadas no sistema com uma entrada na tabela mysql.
Brevemente sobre a empresa e a rede na qual este sistema está implantado:
~ 1000 telefones, cerca de 50 departamentos


Produtos de software usados ​​pelo sistema:


  • Asterisk 13.10
  • YandexSpeechKit
  • python 2.6.6
  • Mysql, MSSQL
  • sox 14.2.0
  • enrolar 7.19.7
  • coxo 3.99.5

Descrição do plano de discagem em um asterisco.


[officevoicerec] exten => s,1,Answer() same => n,Macro(hangercheck,${CALLERID(num)}) same => n,Set(ITERATIONS=1) same => n,Set(HANGFLAG=TRUE) same => n,Background(/var/lib/asterisk/sounds/ru/speechrec/zdravstvuite) 

Nesse fragmento, uma macro é iniciada para verificar se o chamador desligou logo após ouvir uma mensagem de saudação. A seguir, defina o valor das variáveis:
ITERAÇÕES - necessárias para repetir o processo de reconhecimento por um determinado número de vezes. HANGFLAG - essa variável é usada pela macro hangercheck.


 same => n(rec),Set(RECFILE=/tmp/${UNIQUEID}.wav) same => n,Playback(/var/lib/asterisk/sounds/en/beep) same => n,Record(${RECFILE},3,8) same => n,AGI(pyreq8.py,${RECFILE}) same => n,GotoIf($["${NUMTOCALL}" = "repeat"]?repeat) same => n,Set(HANGFLAG=FALSE) 

Defina a variável do arquivo de registro, escreva o arquivo. Executamos um script agi responsável por enviar o arquivo para reconhecimento e pesquisa de número (o script será descrito posteriormente), verificando a variável NUMTOCALL (o valor é definido pelo script), definimos o sinal HANGFLAG, o que significa que a pessoa não desligou antes do tempo.


 same => n,Macro(VRstat,${CALLERID(num)},${NUMTOCALL},${RSTATUS},${CHANNEL},${RECREZ}) same => n,GotoIf($[[${EXISTS(${FNAME})}]]?foundName:havenodescr) same => n(foundName),Set(FILE_FNAME=${STRREPLACE(FNAME, ,)}) same => n,GotoIf($["${STAT(f,/var/lib/asterisk/sounds/ru/cache/${FILE_FNAME}.mp3)}"="1"]?havecache:nocache) 

Nesse fragmento, uma macro é executada para verificar se o chamador desligou depois de ouvir a mensagem de saudação e, em seguida, as variáveis ​​são definidas. ITERATIONS é necessário para repetir o processo de reconhecimento um número especificado de vezes. HANGFLAG - essa variável é usada pela macro hangercheck.


 same => n(rec),Set(RECFILE=/tmp/${UNIQUEID}.wav) same => n,Playback(/var/lib/asterisk/sounds/en/beep) same => n,Record(${RECFILE},3,8) same => n,AGI(pyreq8.py,${RECFILE}) same => n,GotoIf($["${NUMTOCALL}" = "repeat"]?repeat) same => n,Set(HANGFLAG=FALSE) 

Defina a variável do arquivo de registro, escreva o arquivo. Iniciamos o script responsável por enviar o arquivo para reconhecimento e pesquisamos o número (a descrição do script estará abaixo), verificando a variável NUMTOCALL (o valor é definido pelo script), definimos o sinal HANGFLAG que a pessoa não desligou antes.


 same => n,Macro(VRstat,${CALLERID(num)},${NUMTOCALL},${RSTATUS},${CHANNEL},${RECREZ}) same => n,GotoIf($[[${EXISTS(${FNAME})}]]?foundName:havenodescr) same => n(foundName),Set(FILE_FNAME=${STRREPLACE(FNAME, ,)}) same => n,GotoIf($["${STAT(f,/var/lib/asterisk/sounds/ru/cache/${FILE_FNAME}.mp3)}"="1"]?havecache:nocache) same => n(nocache),System(curl "https://tts.voicetech.yandex.net/generate?format=mp3&lang=ru-RU&speaker=zahar&emotion=neutral&speed=0.8&key=" -G --data-urlencode "text= ${FNAME}." > /tmp/speech-${UNIQUEID}.mp3) same => n,System(/usr/local/bin/lame -S --scale 30 /tmp/speech-${UNIQUEID}.mp3 /var/lib/asterisk/sounds/ru/cache/${FILE_FNAME}.mp3) same => n(havecache),Playback(/var/lib/asterisk/sounds/ru/cache/${FILE_FNAME}) same => n,Dial(Local/${NUMTOCALL}@common-context) 

A execução da macro vrstat (responsável pelas estatísticas, não será descrita devido à sua trivialidade). Verifique se a descrição do FNAME (a variável é definida por pyreq8.py) da solicitação. Se houver uma descrição, defina o nome do arquivo de cache na variável e verifique sua presença. Se o arquivo não existe, sintetizamos, convertemos para mp3, aumentamos o volume e depois (ou se o cache existir), reproduzimos e chamamos o assinante.


 same => n(repeat),GotoIf($["${ITERATIONS}"="1"]?secretary) same => n,Background(/var/lib/asterisk/sounds/ru/speechrec/1-wav) same => n,Set(ITERATIONS=$[${ITERATIONS}+1]) same => n,Goto(rec) 

Repita o reconhecimento. Se o número de iterações exceder o especificado, nos converteremos em secretárias.


  same => n(secretary),Macro(VRstat,${CALLERID(num)},${NUMTOCALL},${RSTATUS},${CHANNEL},${RECREZ}) same => n,Set(HANGFLAG=FALSE) same => n,Dial(Local/1000@common-context) 

Transferir para secretárias. Entramos nas estatísticas, definimos a bandeira que eles não desligaram. Nós chamamos a secretária.


 same => n(havenodescr),Playback(/var/lib/asterisk/sounds/ru/speechrec/wait2);thanks-wait) same => n,Noop('no description') same => n,Dial(Local/${NUMTOCALL}@common-context) same => n,Hangup() 

Ligue para um assinante que não tem uma descrição no diretório. Perdemos a mensagem, inserimos nas estatísticas, chamamos.


 exten => h,1,Gotoif($["${HANGFLAG}"="TRUE"]?exec:noop) same => n(exec),Macro(VRstat,${CALLERID(num)},x,HANGER,${CHANNEL}) same => n(noop),Noop('exiting') 

Tratamento da finalização de chamadas para que a macro hangercheck funcione.


Descrição do plano de discagem. Macros.


 [macro-hangercheck] ;${ARG1} -clid exten => s,1,GotoIf($["${ARG1}"="anonymous"]?end) exten => s,n,MYSQL(Connect connid SRV user password db utf8) exten => s,n,MYSQL(SET NAMES utf8) exten => s,n,MYSQL(Query resultid ${connid} SELECT IFNULL((SELECT clid from ivr_stat where rstatus="HANGER" and calldate >=ADDDATE(NOW(),INTERVAL -48 HOUR) and clid="${ARG1}" order by calldate desc limit 1),"NF")) exten => s,n,MYSQL(Fetch fetchid ${resultid} VAR) exten => s,n,MYSQL(Clear ${resultid}) exten => s,n,MYSQL(Disconnect ${connid}) exten => s,n,GotoIf($["${VAR}"="NF"]?end) exten => s,n,Macro(VRstat,${ARG1},x,H_RECALL,${CHANNEL}) exten => s,n,Dial(Local/1000@common-context) exten => s,n,Hangup() exten => s,n(end),Noop(Hanger check failed) 

Verificamos no banco de dados estatísticos se o número fornecido ligou nas últimas 48 horas e, se ele desligou o telefone sem esperar o reconhecimento terminar, o inserimos nas estatísticas e o conectamos à secretária.


Breve descrição das tabelas sql e mysql usadas.


Sql table dbo.phrases.


id(PK, int), phrase (text), number(varchar(50))


A pesquisa de texto completo foi gerada para esta tabela; é usada para alternar por palavras-chave ao receber uma frase longa. Por exemplo, a frase: "por favor, conecte-me a um representante do departamento de publicidade", se houver uma entrada no banco de dados em que frase = "departamento de publicidade", o chamador será conectado ao número correspondente.


Tabela mysql recogStats.


id(PK, int), date (datetime), ctime(float), rtime(float),stime(float), phrase(varchar(60))


Esta tabela é usada para armazenar resultados de reconhecimento e coletar estatísticas sobre o tempo de reconhecimento. ctime - tempo gasto na conversão do arquivo de áudio, rtime - tempo gasto no download e reconhecimento, stime - tempo gasto na pesquisa


Tabela mysql ivr_stat.


id(PK, int), calldate (datetime),clid(varchar(15)),duration(int(20)),callednum(varchar(10)),rstatus(varchar(20)),channame(varchar30)),RECREZ(varchar(200))


Esta tabela é usada para rastrear os resultados do reconhecimento (RECREZ, rstatus), para quem o chamador foi de acordo com os resultados da pesquisa para a frase reconhecida (namednum), para rastrear quanto tempo uma pessoa passou no menu de reconhecimento (duração) e para a depuração (channame)


Tabela Mysql CustomRequests.


id(PK, int), nomer(int(10)), request(varchar(100))
Um diretório auxiliar de grupos e departamentos adicionais.


Tabela de descrições numéricas do Mysql.


Num(text), name(text)


Diretório com a descrição do número digitado para dublagem.


Tabelas mysql spravochnik_rus e spravochnik_rus_name_num_no_tops


nomer(varchar(11)), fio(varchar(100))


Pessoas de referência com números. O primeiro está completo, para uso interno. No segundo, para uso externo, o número de diretores e gerentes é envolvido em uma secretaria.


Script AGI que converte, envia e pesquisa frases reconhecidas.


Bibliotecas usadas:


 import difflib from sys import exit import uuid import time import os import subprocess import xml.etree.ElementTree import MySQLdb import pymssql import string import re from itertools import permutations from os import remove import timeit 

E também a biblioteca asterisk.agi , que importamos dependendo da depuração (a depuração é feita em uma máquina Windows).


Descrição das variáveis


 WINDEBUG = False 

Sinalizador de depuração


 _digits = re.compile('\d') 

Variável de expressão regular compilada para números.


 uniqid = str(uuid.uuid1()).replace('-', '') 

Variável identificadora exclusiva.


 dkey = '12345678-9101-1121-3141-51617181920' 

Chave da API do kit de fala Yandex.


 lang = 'ru-RU' 

Opção de idioma para o kit de fala Yandex.


 topic = 'queries' 

Opção de tema de reconhecimento para o kit de fala Yandex.


 callnumber = '222' 

O número de chamada padrão.


 setVar('NUMTOCALL', callnumber) 

Definir o número de telefone padrão, caso algo dê errado.


 persondic = dict() 

Nome do dicionário - número.


 persondicFI=dict() 

Dicionário FI - número.


 persondicF=dict() 

Sobrenome do dicionário - número.


 otherdic = dict() 

Um dicionário com outras entradas do formulário Número do registro.


 descriptions = dict() 

Dicionário com descrições para a geração de dublagem, digite Record - number.


 duplicates = list() 

A lista de serviços para filtragem é necessária.


 nums = list() 

Lista com todas as extensões.


 outfile = '/tmp/' + uniqid + '-pcm.wav' 

Arquivo de áudio de saída temporária variável.


 mysqlhost='myhost' mysqlpass='mypass' mysqluser='myuser' mysqldb='mydb' mssqlhost='mshost' mssqlpass='mspass' mssqluser='msuser' mssqldb='msdb' 

Detalhes para conectar ao mysql e mssql.


 if not WINDEBUG: from asterisk.agi import * agi = AGI() infile = agi.env['agi_arg_1'] caller = agi.get_variable('CALLERID(num)') else: caller = '1064' infile = '' 

Importe a biblioteca, defina uma variável para o nome do arquivo de áudio de entrada e o número do chamador, dependendo da depuração.


Recurso Descrição


 def verb(s): if not WINDEBUG: agi.verbose(s) else: print s 

Dependendo do valor da variável de depuração, exibimos mensagens no console do asterisco ou no stdout.


 def setVar(varname, varval): if not WINDEBUG: agi.set_variable(varname, varval) else: print "setting var " + varname + " with value " + varval 

Dependendo do valor da variável de depuração, atribuímos o valor da variável dialplan a um asterisco ou a uma saída ao stdout.


 def contains_digits(s): verb('enter contains_digits') return bool(_digits.search(s)) 

Verifique se s contém dígitos.


 def return_digits(s): verb('return digits') pstr = s.encode("utf-8") all = string.maketrans('', '') nodigs = all.translate(all, string.digits) return unicode(pstr.translate(all, nodigs), "utf-8") 

Retornamos apenas números de s.


 def check_dob(num): if int(num) in nums: verb('checkdob success') return True else: verb('checkdob fail' + num) return False 

Verifique a existência de uma extensão na lista de números.


 def set_dob(strnum): buf = return_digits(strnum) if contains_digits(strnum): if check_dob(buf): verb('setting var ' + buf) setVar('RSTATUS', 'SAYDIAL') return buf else: return "repeat" else: return "repeat" 

Função de configuração de extensão.


 def checkSize(infile): if int(os.stat(infile).st_size) <= 26364: setVar('NUMTOCALL', 'repeat') setVar('RSTATUS', 'SILENCE') verb('empty file received') remove(infile) exit(9) 

Verificando o reconhecimento do arquivo de gravação de áudio recebido, se o tamanho for muito pequeno, assumimos que eles estavam silenciosos no aparelho.


 def addSessionStat(ctime, rtime, stime, phrase): cdate = time.strftime("%Y-%m-%d %H:%M:%S") db = MySQLdb.connect(host=mysqlhost, user=mysqluser, passwd=mysqluser, db=mysqldb, charset='utf8') cur = db.cursor() cur.execute( "INSERT INTO recogStats(date,ctime,rtime,stime,phrase) VALUES ('" + cdate + "','" + str(ctime) + "','" + str( rtime) + "','" + str(stime) + "','" + phrase + "')") db.commit() db.close() 

Função de reconhecimento de estatísticas de gravação.


 def fillDics(): db = MySQLdb.connect(host=mysqlhost, user=mysqluser, passwd=mysqlpass, db="central_cdr", charset='utf8') cur = db.cursor() if len(caller) != 4: tbname = 'spravochnik_rus_name_num_no_tops' else: tbname = 'svravochnik_rus_persons' cur.execute("""SELECT nomer,fio from """ + tbname) for row in cur.fetchall(): fullfio=row[1].lower() f=" ".join(fullfio.split(' ')[0:1]) if not f in persondicF.keys():# and f not in duplicates : persondicF[f] = int(row[0]) elif fullfio in persondic.keys(): pass else: duplicates.append(f) if not fullfio in persondic.keys(): persondic[fullfio] = int(row[0]) fi=" ".join(fullfio.split(' ')[0:2]) if not fi in persondicFI.keys(): persondicFI[fi] = int(row[0]) elif fullfio in persondic.keys(): pass else: persondicFI.pop(fi) uniquedups=[x for x in list(set(duplicates)) if x != ''] for item in uniquedups: for key in persondicF.keys(): if item in key: persondicF.pop(key) cur.execute("""SELECT nomer,request from CustomRequests """) for row in cur.fetchall(): otherdic[row[1].lower()] = row[0] cur.execute("""SELECT num,name from numdescriptions """) for row in cur.fetchall(): descriptions[str(row[0])] = row[1] cur.execute("""SELECT nomer from spravochnik_rus """) for row in cur.fetchall(): nums.append(int(row[0])) db.close() 

Essa função preenche os dicionários Sobrenome (persondicF), Sobrenome (persondicFI), Nome (persondic), nomes de departamento (otherdic), descrições para síntese de fala (descrições) e todos os números de empresa (nums).
Dependendo do tamanho do número do chamador, é tomada uma tabela que contém os números de diretores (chamador interno) ou não (chamador externo).
O diretório de sobrenomes é testado quanto à exclusividade para excluir a presença de dois Ivanovs com números diferentes.


 def convert(infile, outfile): #convert file verb("Converting WAV " + infile) soxconvert = subprocess.Popen(['sox', infile, '-r', '16000', '-b', '16', '-c', '1', outfile], stdout=subprocess.PIPE) (out, err) = soxconvert.communicate() # remove(infile) if soxconvert.returncode != 0: setVar('NUMTOCALL', callnumber) # return "" exit(9) 

A função de converter o arquivo gravado no asterisco, caso o sox dê um erro, defina o número padrão e saia do script.


 def sendRecog(file): verb("Sending file to yandex: " + outfile) proc = subprocess.Popen(['curl', '--max-time', '5', '--silent', 'asr.yandex.net/asr_xml?key=' + dkey + '&uuid=' + uniqid + '&topic=' + topic + '&lang=ru-RU', '-F', 'Content-Type=audio/x-pcm;bit=16;rate=16000', '-F', 'audio=@' + outfile], stdout=subprocess.PIPE) (out, err) = proc.communicate() verb("return code is: " + str(proc.returncode)) if proc.returncode != 0: return "" remove(file) e = xml.etree.ElementTree.fromstring(out) if e.attrib['success'] == '1': verb(e._children[0].text) return e._children[0].text else: return "" 

A função de enviar um arquivo para reconhecimento e receber uma resposta do Yandex. Se o reconhecimento for bem sucedido, tomamos a primeira resposta. Não pude fazer isso com a biblioteca curl, por esse motivo essa opção é usada.


 def searchfiobyf(f): if len(f.split(" "))==2: limit=2 elif len(f.split(" "))>=3: return f else: limit=1 for fio,num in persondic.iteritems(): cutfio=" ".join(fio.split(' ')[0:limit]) if f == cutfio: return fio 

A função de procurar um nome completo, dependendo se apenas o sobrenome ou o sobrenome e o primeiro nome chegaram à entrada.


 def combinationSearcher(phrase,pdic,quality): verb(u'searching in persons') result = difflib.get_close_matches(phrase, pdic.keys(), 1, quality) #verb(" ".join(result)) if len(result) > 0: fullfio=searchfiobyf(result[0]) verb(u'found ' + str(pdic[result[0]])) setVar('RSTATUS', 'NAMESUCCESS') setVar('FNAME', fullfio) return pdic[result[0]] else: return "" 

Função de busca combinada, exemplo: frase = "Alexey Petr", pdic contém a entrada "Alexeyev Peter", a função get_close_matches retornará "Alexeyev Peter". A função pode produzir resultados incorretos, mas, como a prática demonstrou, há respostas muito mais corretas. O parâmetro quality permite especificar a precisão da pesquisa de frases semelhantes.


 def getNumByName(recognizedString): if u'' in recognizedString: verb('enter dobavochn') return set_dob(recognizedString) elif len(recognizedString.replace(" ", "")) == 4: verb('enter num say') return set_dob(recognizedString) if len(recognizedString) <= 5 and recognizedString.lower() not in [u"",u"",u""]: setVar('RSTATUS', 'SHORT') return "repeat" verb(u'start searching') #  split = list(set(recognizedString.split(" "))) parts = len(split) fixedstring=" ".join(split) if parts >= 5: verb('Phrase is long. Using FTDB') buf = mssqlwrapper(fixedstring) if buf != '': buf = mssqlwrapper(fixedstring)[0] if str(buf) in descriptions.keys(): setVar('FNAME', descriptions[str(buf)]) setVar('RSTATUS', 'DEPTSUCCESS') return buf else: setVar('RSTATUS', 'REQUESTNOTFOUND') return "repeat" result="" if parts == 1: mssqlcheck=mssqlwrapper(fixedstring) if mssqlcheck!='': if mssqlcheck[2]>=80: buf=str(mssqlcheck[0]) if buf in descriptions.keys(): setVar('FNAME', descriptions[str(buf)]) setVar('RSTATUS', 'DEPTSUCCESS') result=buf else: result=combinationSearcher(fixedstring,persondicF,0.7) elif parts ==2: combs = list(permutations(split, parts)) # (  ,   ,   .....) for item in combs: element = " ".join(item) result=combinationSearcher(element,persondicFI,0.7) if result!="": break elif parts ==3: combs = list(permutations(split, parts)) for item in combs: element = " ".join(item) result=combinationSearcher(element,persondic,0.8) if result!="": break if result!="": return result verb('Low ftdbsearch') buf = mssqlwrapper(recognizedString) if buf != '': buf = mssqlwrapper(recognizedString)[0] if str(buf) in descriptions.keys(): verb('it is') setVar('FNAME', descriptions[str(buf)]) setVar('RSTATUS', 'DEPTSUCCESS') return buf else: verb(u'item not found ' + recognizedString) #    setVar('RSTATUS', 'REQUESTNOTFOUND') return callnumber 

A principal função de encontrar números por frase.


Vamos analisar essa função em partes.


 if u'' in recognizedString: verb('enter dobavochn') return set_dob(recognizedString) 

Exemplo: a frase "Por favor, ramal 1234" retornará o número do ramal (se houver).


 elif len(recognizedString.replace(" ", "")) == 4: verb('enter num say') return set_dob(recognizedString) 

Se o usuário pronunciou um número de quatro dígitos, retorne o ramal correspondente (se houver).


 if len(recognizedString) <= 5 and recognizedString.lower() not in [u"",u"",u""]: setVar('RSTATUS', 'SHORT') return "repeat" 

Para não executar execuções desnecessárias do programa em frases curtas (isso pode acontecer quando o Yandex ouviu e reconheceu parte da conversa, quando o chamador não ouviu a mensagem de reconhecimento).


 if parts >= 5: verb('Phrase is long. Using FTDB') buf = mssqlwrapper(fixedstring) if buf != '': buf = mssqlwrapper(fixedstring)[0] if str(buf) in descriptions.keys(): setVar('FNAME', descriptions[str(buf)]) setVar('RSTATUS', 'DEPTSUCCESS') return buf else: setVar('RSTATUS', 'REQUESTNOTFOUND') return "repeat" result="" 

Para frases longas (mais de cinco palavras), passamos imediatamente para a base do mssql no FT.


 if parts == 1: mssqlcheck=mssqlwrapper(fixedstring) if mssqlcheck!='': if mssqlcheck[2]>=80: buf=str(mssqlcheck[0]) if buf in descriptions.keys(): setVar('FNAME', descriptions[str(buf)]) setVar('RSTATUS', 'DEPTSUCCESS') result=buf else: result=combinationSearcher(fixedstring,persondicF,0.7) 

Se a frase reconhecida consistir em uma palavra, primeiro olharemos para o banco de dados FT, com uma precisão de 80%, procuraremos uma descrição do número reconhecido, definiremos a variável de resultado e a variável de plano de número do plano de status, caso contrário, procuraremos palavras semelhantes no dicionário de sobrenomes.


 elif parts ==2: combs = list(permutations(split, parts)) for item in combs: element = " ".join(item) result=combinationSearcher(element,persondicFI,0.7) if result!="": break 

Se a frase consistir em duas palavras, assumimos que é um sobrenome e um nome.
Fazemos uma lista de combinações (Ivan Ivan, Ivan Ivan) e procuramos correspondências semelhantes.


 elif parts ==3: combs = list(permutations(split, parts)) for item in combs: element = " ".join(item) result=combinationSearcher(element,persondic,0.8) if result!="": break if result!="": return result 

No caso de uma frase de três palavras, assumimos que é um nome completo, faça uma lista de combinações e procure no dicionário com um nome completo. Retornamos resultado se a variável não estiver vazia.


 buf = mssqlwrapper(recognizedString) if buf != '': buf = mssqlwrapper(recognizedString)[0] if str(buf) in descriptions.keys(): verb('it is') setVar('FNAME', descriptions[str(buf)]) setVar('RSTATUS', 'DEPTSUCCESS') return buf else: verb(u'item not found ' + recognizedString) #    setVar('RSTATUS', 'REQUESTNOTFOUND') return callnumber 

Caso não encontremos correspondências, fazemos uma pesquisa no banco de dados do FT; se encontrarmos uma correspondência, retornamos o resultado e a descrição; caso contrário, retornamos o número padrão e definimos o status em que a frase não foi encontrada.


O corpo principal do programa.


 fillDics() if not WINDEBUG: checkSize(infile) start_time = timeit.default_timer() convert(infile, outfile) convert_elapsed = timeit.default_timer() - start_time start_time = timeit.default_timer() checkstring = sendRecog(outfile).lower() recog_elapsed = timeit.default_timer() - start_time verb('convert_elapsed = ' + str(recog_elapsed)) if checkstring == "": verb('not recognized. using default.') setVar('NUMTOCALL', 'repeat') setVar('RSTATUS', 'SILENCE') exit(9) else: setVar('RECREZ', checkstring) else: checkstring = u"" start_time = timeit.default_timer() callnumber = getNumByName(checkstring) search_elapsed = timeit.default_timer() - start_time if not WINDEBUG: setVar('NUMTOCALL', str(callnumber)) addSessionStat(convert_elapsed, recog_elapsed, search_elapsed, checkstring.lower()) 

A função de preencher dicionários é ativada, se não estivermos no programa de depuração - preenchemos as variáveis ​​do timer, verificamos o tamanho do arquivo, convertemos, enviamos para reconhecimento, caso contrário - preenche a variável checkstring com uma frase de verificação.
Em seguida, preenchemos as variáveis ​​dos temporizadores para reconhecimento, procuramos frases em nossa estrutura. E, no caso de funcionamento de combate, definimos uma variável para o plano de numeração e inserimos estatísticas.


Algumas estatísticas.


Junho de 2018:
reconhecimento bem sucedido - 1010
ficaram em silêncio em um tubo - 78
falha no reconhecimento (nenhuma correspondência encontrada) - 79
pedido curto - 4
departamento reconhecido - 7
tempo médio de reconhecimento (tempo médio de resposta Yandex) - 2,6 seg


Junho de 2017:
reconhecimento bem sucedido - 1271
departamento reconhecido - 18
falha no reconhecimento (nenhuma correspondência encontrada) - 127
pequeno pedido - 9
ficaram em silêncio em um tubo - 71
tempo médio de reconhecimento (tempo médio de resposta Yandex) - 1,5 seg


Este serviço é utilizado com sucesso na empresa em que trabalho. Espero que minhas realizações ajudem outras pessoas na implementação de suas idéias ou funcionalidade semelhante. Pronto para responder a todas as perguntas.

Source: https://habr.com/ru/post/pt417273/


All Articles