Realización de una guía de voz basada en YandexSpeechKit

Se presentan varias implementaciones en Internet, pero, en mi opinión, todas son bastante simples. Quiero presentar mi versión de la guía de voz para un asterisco.


Nota: No soy un programador profesional, y quizás algunas soluciones puedan parecerle descabelladas. Algunos trucos pueden estar desactualizados. Estoy listo para aceptar las críticas y arreglar el sistema para mejor.


Breve descripción de las características:


El usuario ingresa al IVR, hace su solicitud y, en la mayoría de los casos, llega a donde necesita. Las estadísticas también se atornillan al sistema con una entrada en la tabla mysql.
Brevemente sobre la compañía y la red en la que se implementa este sistema:
~ 1000 teléfonos, unos 50 departamentos


Productos de software utilizados por el sistema:


  • Asterisco 13.10
  • YandexSpeechKit
  • Python 2.6.6
  • Mysql, MSSQL
  • sox 14.2.0
  • rizo 7.19.7
  • cojo 3.99.5

Descripción del plan de marcado en un 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) 

En este fragmento, se inicia una macro para verificar si la persona que llama ha colgado justo después de escuchar un mensaje de saludo. Lo siguiente es establecer el valor de las variables:
ITERACIONES: necesarias para repetir el proceso de reconocimiento durante un número determinado de veces. HANGFLAG: esta variable es utilizada por la 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) 

Establezca la variable del archivo de registro, escriba el archivo. Ejecutamos un agi-script responsable de enviar el archivo para reconocimiento y búsqueda de número (la descripción del script se proporcionará más adelante), verificando la variable NUMTOCALL (el valor lo establece el script), establece el signo HANGFLAG, lo que significa que la persona no colgó antes de tiempo.


 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) 

En este fragmento, se ejecuta una macro para verificar si la persona que llamó colgó después de escuchar el mensaje de saludo, luego se establecen las variables, es necesario ITERATIONS para repetir el proceso de reconocimiento un número específico de veces. HANGFLAG: esta variable es utilizada por la 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) 

Establezca la variable del archivo de registro, escriba el archivo. Comenzamos el guión responsable de enviar el archivo para su reconocimiento y buscamos el número (la descripción del guión estará debajo), verificando la variable NUMTOCALL (el valor lo establece el guión), establecemos el signo HANGFLAG que la persona no colgó antes de tiempo.


 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) 

Ejecutar la macro vrstat (responsable de las estadísticas, no se describirá debido a su trivialidad). Compruebe si la descripción de FNAME (la variable está establecida por pyreq8.py) de la solicitud. Si hay una descripción, establezca el nombre del archivo de caché en la variable y verifique su presencia. Si el archivo no existe, lo sintetizamos, lo convertimos a mp3, aumentamos el volumen y luego (o si existe el caché), lo reproducimos y llamamos al suscriptor.


 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 el reconocimiento. Si la cantidad de iteraciones excede la especificada, entonces traducimos a secretarias.


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

Traslado a secretarias. Ingresamos en las estadísticas, establecemos la bandera de que no colgaron. Llamamos a la secretaria.


 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() 

Llame a un suscriptor que no tenga una descripción en el directorio. Perdemos el mensaje, lo ingresamos en las estadísticas, lo llamamos.


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

Manejo de la terminación de llamadas para que funcione la macro Hangercheck.


Descripción del plan de marcado. 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 en la base de datos de estadísticas si el número dado llamó a las últimas 48 horas, y si colgó el teléfono sin esperar a que termine el reconocimiento, lo ingresamos en las estadísticas y lo conectamos con la secretaria.


Breve descripción de las tablas sql y mysql usadas.


Tabla SQL dbo.phrases.


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


La búsqueda de texto completo se ha generado para esta tabla; se utiliza para cambiar por palabras clave cuando se recibe una frase larga. Por ejemplo, la frase: "por favor, conécteme con un representante del departamento de publicidad", si hay una entrada en la base de datos donde frase = "departamento de publicidad", la persona que llama se conectará al número correspondiente.


Mysql table recogStats.


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


Esta tabla se utiliza para almacenar resultados de reconocimiento y recopilar estadísticas sobre el tiempo de reconocimiento. ctime: tiempo dedicado a convertir el archivo de audio, rtime: tiempo dedicado a descargar y reconocer, stime: tiempo dedicado a buscar


Mysql tabla 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 tabla se utiliza para realizar un seguimiento de los resultados del reconocimiento (RECREZ, rstatus), a quién acudió la persona que llama de acuerdo con los resultados de búsqueda de la frase reconocida (llamada núm.), Para realizar un seguimiento de cuánto tiempo pasó una persona en el menú de reconocimiento (duración) y para la depuración (nombre del canal)


Tabla Mysql CustomRequests.


id(PK, int), nomer(int(10)), request(varchar(100))
Un directorio auxiliar de grupos y departamentos adicionales.


Tabla de descripciones de Mysql.


Num(text), name(text)


Directorio con la descripción del número ingresado para la actuación de voz.


Tablas Mysql spravochnik_rus y spravochnik_rus_name_num_no_tops


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


Personas de referencia con números. El primero está completo, para uso interno. El segundo, para uso externo, los números de directores y gerentes están envueltos en una secretaría.


Script AGI que convierte, envía y busca frases reconocidas.


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 

Y también la biblioteca asterisk.agi , que importamos dependiendo de la depuración (la depuración se realiza en una máquina con Windows).


Descripción de variables.


 WINDEBUG = False 

Indicador de depuración


 _digits = re.compile('\d') 

Variable de expresión regular compilada a números.


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

Identificador único variable.


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

Clave API del kit de voz Yandex.


 lang = 'ru-RU' 

Opción de idioma para el kit de discurso Yandex.


 topic = 'queries' 

Opción de tema de reconocimiento para el kit de discurso Yandex.


 callnumber = '222' 

El número de llamada predeterminado.


 setVar('NUMTOCALL', callnumber) 

Establecer el número de llamada predeterminado en caso de que algo salga mal.


 persondic = dict() 

Nombre del diccionario: número.


 persondicFI=dict() 

Diccionario FI - Número.


 persondicF=dict() 

Apellido del diccionario - Número.


 otherdic = dict() 

Un diccionario con otras entradas de la forma Registro - número.


 descriptions = dict() 

Diccionario con descripciones para la generación de actuación de voz, escriba Record - number.


 duplicates = list() 

Lista de servicios para filtrar tomas.


 nums = list() 

Lista con todas las extensiones.


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

Archivo de audio de salida temporal variable.


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

Detalles para conectarse a mysql y 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 la biblioteca, establezca una variable para el nombre del archivo de audio de entrada y el número de la persona que llama, según la depuración.


Descripción de la característica


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

Dependiendo del valor de la variable de depuración, mostramos mensajes en la consola de asterisco o en stdout.


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

Dependiendo del valor de la variable de depuración, asignamos el valor de la variable de plan de marcado a un asterisco o lo mostramos en stdout.


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

Verifique si s contiene 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") 

Solo devolvemos números del s.


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

Verifique que la extensión esté en la 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" 

Función de ajuste de extensión.


 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 el archivo de grabación de audio recibido para el reconocimiento, si el tamaño es demasiado pequeño, asumimos que estaban en silencio en el teléfono.


 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() 

Función de reconocimiento de estadísticas de grabación.


 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() 

Esta función completa los diccionarios Apellido (persondicF), Apellido (persondicFI), Nombre (persondic), nombres de departamento (otherdic), descripciones para síntesis de voz (descripciones) y todos los números de compañías (nums).
Dependiendo de la longitud del número de la persona que llama, se toma una tabla que contiene el número de directores (llamante interno) o no contiene (llamador externo).
Se prueba la unicidad del directorio de apellidos para excluir la presencia de dos Ivanovs con diferentes números.


 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) 

La función de convertir el archivo grabado en el asterisco, en caso de que sox produzca un error, establezca el número predeterminado y salga del 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 "" 

La función de enviar un archivo para su reconocimiento y recibir una respuesta de Yandex. Si el reconocimiento es exitoso, tomamos la primera respuesta. No pude hacer esto con la biblioteca de rizos, por esta razón se utiliza esta opción.


 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 

La función de buscar un nombre completo, dependiendo de si solo el apellido o el apellido y el nombre llegaron a la 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 "" 

Función de búsqueda combinada, ejemplo: frase = "Alexey Petr", pdic contiene la entrada "Alexeyev Peter", la función get_close_matches devolverá "Alexeyev Peter". La función puede producir resultados incorrectos, pero, como lo ha demostrado la práctica, hay respuestas mucho más correctas. El parámetro de calidad le permite especificar la precisión de las búsquedas de frases similares.


 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 

La función principal de encontrar números por frase.


Analicemos esta función en partes.


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

Ejemplo: la frase "Por favor, extensión 1234" devolverá el número de extensión (si corresponde).


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

Si el usuario ha pronunciado un número de cuatro dígitos, devuelva la extensión correspondiente (si corresponde).


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

Para no realizar ejecuciones innecesarias del programa en frases cortas (esto puede suceder cuando Yandex escuchó y reconoció parte de la conversación, cuando la persona que llamó no escuchó el mensaje de reconocimiento).


 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 largas (más de cinco palabras), inmediatamente pasamos a la base FT de mssql.


 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) 

Si la frase reconocida consta de una palabra, primero miramos la base de datos FT, con una precisión del 80%, buscamos una descripción del número reconocido, establecemos la variable de resultado y la variable de plan de número del plan de estado, de lo contrario buscamos palabras similares en el diccionario de apellidos.


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

Si la frase consta de dos palabras, suponemos que es un apellido y un nombre.
Hacemos una lista de combinaciones (Ivan Ivan, Ivan Ivan) y buscamos coincidencias similares.


 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 

En el caso de una frase de tres palabras, suponemos que es un nombre completo, hacemos una lista de combinaciones y buscamos en el diccionario un nombre completo. Devolvemos el resultado si la variable no está vacía.


 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 

En caso de que no encontremos coincidencias, realizamos una búsqueda en la base de datos de FT, si encontramos una coincidencia, devolvemos el resultado y la descripción; de lo contrario, devolvemos el número predeterminado y establecemos el estado en el que no se encontró la frase.


El cuerpo principal del 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()) 

Se inicia la función de rellenar diccionarios, si no estamos en el programa de depuración: rellenamos las variables del temporizador, verificamos el tamaño del archivo, lo convertimos, lo enviamos para su reconocimiento; de lo contrario, llena la variable de la cadena de verificación con una frase de verificación.
A continuación, completamos las variables de los temporizadores para el reconocimiento, buscamos frases en nuestra estructura. Y, en el caso del funcionamiento de combate, establecemos una variable para el plan de numeración e ingresamos estadísticas.


Algunas estadísticas


Junio ​​2018:
reconocimiento exitoso - 1010
guardaron silencio en un tubo - 78
reconocimiento fallido (no se encontró coincidencia) - 79
solicitud corta - 4
departamento reconocido - 7
tiempo de reconocimiento promedio (tiempo de respuesta promedio de Yandex) - 2.6 segundos


Junio ​​2017:
reconocimiento exitoso - 1271
departamento reconocido - 18
reconocimiento fallido (no se encontró coincidencia) - 127
solicitud corta - 9
estaban en silencio en un tubo - 71
tiempo de reconocimiento promedio (tiempo de respuesta promedio de Yandex) - 1.5 segundos


Este servicio se utiliza con éxito en la empresa donde trabajo. Espero que mis logros ayuden a otras personas en la implementación de sus ideas o funcionalidades similares. Listo para responder todas las preguntas.

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


All Articles