Réalisation d'un guide vocal basé sur YandexSpeechKit

Diverses implémentations sont présentées sur Internet, mais, à mon avis, elles sont toutes assez simples. Je souhaite présenter ma version du guide vocal pour un astérisque.


Remarque: je ne suis pas un programmeur professionnel, et certaines solutions peuvent vous sembler folles. Certaines astuces peuvent être obsolètes. Je suis prêt à accepter les critiques et à améliorer le système.


Brève description des fonctionnalités:


L'utilisateur entre dans l'IVR, fait sa demande et, dans la plupart des cas, se rend là où il doit. Les statistiques sont également vissées au système avec une entrée dans le tableau mysql.
En bref sur l'entreprise et le réseau dans lequel ce système est déployé:
~ 1000 téléphones, environ 50 départements


Produits logiciels utilisés par le système:


  • Asterisk 13.10
  • YandexSpeechKit
  • python 2.6.6
  • Mysql, MSSQL
  • sox 14.2.0
  • curl 7.19.7
  • boiteux 3.99.5

Description du plan de numérotation dans un astérisque.


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

Dans ce fragment, une macro est lancée pour vérifier si l'appelant a raccroché juste après avoir entendu un message d'accueil. Ensuite, définissez la valeur des variables:
ITERATIONS - nécessaire pour répéter le processus de reconnaissance un nombre donné de fois. HANGFLAG - cette variable est utilisée par la macro de contrôle de suspension.


 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) 

Définissez la variable du fichier d'enregistrement, écrivez le fichier. Nous exécutons un agi-script chargé d'envoyer le fichier pour la reconnaissance et la recherche de numéro (la description du script sera donnée plus tard), en vérifiant la variable NUMTOCALL (la valeur est définie par le script), définissez le signe HANGFLAG, ce qui signifie que la personne n'a pas raccroché à l'avance.


 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) 

Dans ce fragment, une macro est exécutée pour vérifier si l'appelant a raccroché après avoir entendu le message d'accueil, puis les variables sont définies, ITERATIONS est nécessaire pour répéter le processus de reconnaissance un nombre spécifié de fois. HANGFLAG - cette variable est utilisée par la macro de contrôle de suspension.


 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) 

Définissez la variable du fichier d'enregistrement, écrivez le fichier. Nous commençons le script responsable de l'envoi du fichier pour la reconnaissance et la recherche de numéro (la description du script sera ci-dessous), vérifiant la variable NUMTOCALL (la valeur est définie par le script), définissons le drapeau HANGFLAG que la personne n'a pas raccroché à l'avance.


 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) 

L'exécution de la macro vrstat (responsable des statistiques, ne sera pas décrite en raison de sa trivialité). Vérifiez si la description FNAME (la variable est définie par pyreq8.py) de la requête. S'il existe une description, définissez le nom du fichier cache dans la variable et vérifiez sa présence. Si le fichier n'existe pas, nous le synthétisons, le convertissons en mp3, augmentons le volume puis (ou si le cache existe), le lisons et appelons l'abonné.


 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) 

Répétez la reconnaissance. Si le nombre d'itérations dépasse celui spécifié, nous traduisons en secrétaires.


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

Transfert aux secrétaires. Nous entrons dans les statistiques, mettons le drapeau qu'ils n'ont pas raccroché. Nous appelons le secrétaire.


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

Appelez un abonné qui n'a pas de description dans l'annuaire. Nous perdons le message, l'inscrivons dans les statistiques, l'appelons.


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

Gestion de la terminaison d'appel pour que la macro de contrĂ´le de fonctionnement fonctionne.


Description du plan de numérotation. 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) 

Nous vérifions dans la base de données statistiques si le numéro donné a appelé les dernières 48 heures, et s'il a raccroché sans attendre la fin de la reconnaissance, nous le saisissons dans les statistiques et le connectons au secrétariat.


Brève description des tables sql et mysql utilisées.


Table sql dbo.phrases.


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


La recherche en texte intégral a été augmentée pour ce tableau; il est utilisé pour basculer par mots clés lors de la réception d'une longue phrase. Par exemple, la phrase: "veuillez me connecter avec un représentant du service de publicité", s'il y a une entrée dans la base de données où la phrase = "service de publicité", alors l'appelant sera connecté au numéro correspondant.


RecogStats de la table Mysql.


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


Ce tableau est utilisé pour stocker les résultats de reconnaissance et collecter des statistiques sur le temps de reconnaissance. ctime - temps passé à convertir le fichier audio, rtime - temps passé à télécharger et à reconnaître, stime - temps passé à chercher


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


Ce tableau est utilisé pour garder une trace des résultats de la reconnaissance (RECREZ, rstatus), à qui l'appelant est allé en fonction des résultats de la recherche pour une phrase reconnue (appelée n °), pour garder des statistiques sur le temps qu'une personne a passé dans le menu de reconnaissance (durée) et pour le débogage (nom de la chanson)


Table Mysql CustomRequests.


id(PK, int), nomer(int(10)), request(varchar(100))
Un répertoire auxiliaire de groupes et départements supplémentaires.


Table de numdescriptions Mysql.


Num(text), name(text)


Répertoire avec la description du numéro saisi pour la voix.


Tables mysql spravochnik_rus et spravochnik_rus_name_num_no_tops


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


Personnes de référence avec numéros. Le premier est complet, à usage interne. Le second, à usage externe, le nombre d'administrateurs et de managers sont regroupés dans un secrétariat.


Script AGI qui convertit, envoie et recherche des phrases reconnues.


Bibliothèques utilisées:


 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 

Et aussi la bibliothèque asterisk.agi , que nous importons en fonction du débogage (le débogage se fait sur une machine Windows).


Description des variables.


 WINDEBUG = False 

Drapeau de débogage


 _digits = re.compile('\d') 

Variable d'expression régulière compilée en nombres.


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

Variable d'identifiant unique.


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

Clé API du kit vocal Yandex.


 lang = 'ru-RU' 

Option de langue pour Yandex speechkit.


 topic = 'queries' 

Option de thème de reconnaissance pour Yandex speechkit.


 callnumber = '222' 

Le numéro d'appel par défaut.


 setVar('NUMTOCALL', callnumber) 

Définition du numéro d'appel par défaut en cas de problème.


 persondic = dict() 

Nom du dictionnaire - numéro.


 persondicFI=dict() 

Dictionnaire FI - Numéro.


 persondicF=dict() 

Nom de famille du dictionnaire - Numéro.


 otherdic = dict() 

Un dictionnaire avec d'autres entrées du formulaire Numéro d'enregistrement.


 descriptions = dict() 

Dans le dictionnaire contenant les descriptions de la génération de voix, tapez Record - number.


 duplicates = list() 

Liste de services pour le filtrage prend.


 nums = list() 

Liste avec toutes les extensions.


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

Fichier audio de sortie temporaire variable.


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

Détails pour la connexion à mysql et 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 = '' 

Importez la bibliothèque, définissez une variable pour le nom du fichier audio d'entrée et le numéro de l'appelant, en fonction du débogage.


Description des fonctionnalités


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

Selon la valeur de la variable de débogage, nous affichons des messages dans la console astérisque ou dans stdout.


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

En fonction de la valeur de la variable de débogage, nous attribuons la valeur de la variable dialplan à un astérisque ou l’affichons dans stdout.


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

Vérifiez si s contient des chiffres.


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

Nous renvoyons uniquement les chiffres de l'art.


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

Vérifiez l'existence d'une extension dans la liste des numéros.


 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" 

Fonction de réglage d'extension.


 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) 

En vérifiant la reconnaissance du fichier d'enregistrement audio reçu, si la taille est trop petite, nous supposons qu'ils étaient silencieux dans le combiné.


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

Fonction de reconnaissance des statistiques d'enregistrement.


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

Cette fonction remplit les dictionnaires Nom (persondicF), Nom (persondicFI), Nom (persondic), noms de service (otherdic), descriptions pour la synthèse vocale (descriptions) et tous les numéros d'entreprise (nums).
En fonction de la longueur du numéro de l'appelant, une table est prise qui contient le nombre de directeurs (appelant interne) ou ne contient pas (appelant externe).
Le répertoire des noms de famille est testé pour son caractère unique afin d'exclure la présence de deux Ivanov avec des numéros différents.


 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 fonction de conversion du fichier enregistré dans l'astérisque, dans le cas où sox donne une erreur, définissez le numéro par défaut et quittez le 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 fonction d'envoyer un fichier pour reconnaissance et de recevoir une réponse de Yandex. Si la reconnaissance réussit, nous prenons la première réponse. Je ne pouvais pas faire cela avec la bibliothèque curl, pour cette raison cette option est utilisée.


 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 

Fonction de recherche d'un nom complet, selon que seul le nom ou le nom et le prénom sont entrés.


 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 "" 

Fonction de recherche combinée, exemple: phrase = "Alexey Petr", pdic contient l'entrée "Alexeyev Peter", la fonction get_close_matches renverra "Alexeyev Peter". La fonction peut produire des résultats incorrects, mais, comme la pratique l'a montré, il existe des réponses beaucoup plus correctes. Le paramètre de qualité vous permet de spécifier la précision de la recherche d'expressions similaires.


 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 fonction principale de trouver des nombres par phrase.


Analysons cette fonction en plusieurs parties.


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

Exemple: l'expression "Please, extension 1234" renverra le numéro de l'extension (le cas échéant).


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

Si l'utilisateur a prononcé un nombre à quatre chiffres, renvoyez l'extension correspondante (le cas échéant).


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

Afin de ne pas exécuter inutilement le programme en phrases courtes (cela peut se produire lorsque Yandex a entendu et reconnu une partie de la conversation, lorsque l'appelant n'a pas écouté le message de reconnaissance).


 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="" 

Pour les phrases longues (plus de cinq mots), nous nous tournons immédiatement vers 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 phrase reconnue se compose d'un mot, nous examinons d'abord la base de données FT, avec une précision de 80%, recherchons une description du nombre reconnu, définissons la variable de résultat et la variable de plan de nombre du plan de statut, sinon nous recherchons des mots similaires dans le dictionnaire des noms de famille.


 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 l'expression se compose de deux mots, nous supposons qu'il s'agit d'un nom de famille et d'un nom.
Nous faisons une liste de combinaisons (Ivan Ivan, Ivan Ivan) et recherchons des correspondances similaires.


 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 

Dans le cas d'une phrase de trois mots, nous supposons qu'il s'agit d'un nom complet, établissons une liste de combinaisons et cherchons dans le dictionnaire un nom complet. Nous retournons le résultat si la variable n'est pas vide.


 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 

Si nous n'avons trouvé aucune correspondance, nous effectuons une recherche dans la base de données FT, si nous trouvons une correspondance, nous renvoyons le résultat et la description, sinon nous renvoyons le numéro par défaut et définissons l'état que la phrase n'a pas été trouvée.


Le corps principal du programme.


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

La fonction de remplissage des dictionnaires est lancée, si nous ne sommes pas dans le programme de débogage - nous remplissons les variables de temporisation, vérifions la taille du fichier, convertissons, l'envoyons pour reconnaissance, sinon nous remplissons la variable de chaîne de contrôle avec une phrase de contrôle.
Ensuite, nous remplissons les variables des temporisateurs pour la reconnaissance, nous recherchons des phrases dans notre structure. Et, dans le cas du fonctionnement au combat, nous définissons une variable pour le plan de numérotation et saisissons des statistiques.


Quelques statistiques.


Juin 2018:
reconnaissance réussie - 1010
étaient silencieux dans un tube - 78
échec de la reconnaissance (aucune correspondance trouvée) - 79
demande courte - 4
département reconnu - 7
temps de reconnaissance moyen (temps de réponse Yandex moyen) - 2,6 sec


Juin 2017:
reconnaissance réussie - 1271
département reconnu - 18
échec de la reconnaissance (aucune correspondance trouvée) - 127
brève demande - 9
étaient silencieux dans un tube - 71
temps de reconnaissance moyen (temps de réponse Yandex moyen) - 1,5 sec


Ce service est utilisé avec succès dans l'entreprise où je travaille. J'espère que mes réalisations aideront d'autres personnes dans la mise en œuvre de leurs idées ou de fonctionnalités similaires. Prêt à répondre à toutes les questions.

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


All Articles