Realisierung eines Voice Guides basierend auf YandexSpeechKit

Verschiedene Implementierungen werden im Internet vorgestellt, aber meiner Meinung nach sind sie alle recht einfach. Ich möchte meine Version des Sprachführers als Sternchen präsentieren.


Hinweis: Ich bin kein professioneller Programmierer, und vielleicht erscheinen Ihnen einige Lösungen wild. Einige Tricks sind möglicherweise veraltet. Ich bin bereit, Kritik anzunehmen und das System zum Besseren zu reparieren.


Kurze Beschreibung der Funktionen:


Der Benutzer gibt den IVR ein, stellt seine Anfrage und gelangt in den meisten Fällen dorthin, wo er benötigt wird. Statistiken werden auch mit einem Eintrag in der MySQL-Tabelle an das System angeschraubt.
Kurz über das Unternehmen und das Netzwerk, in dem dieses System bereitgestellt wird:
~ 1000 Telefone, ungefähr 50 Abteilungen


Vom System verwendete Softwareprodukte:


  • Sternchen 13.10
  • YandexSpeechKit
  • Python 2.6.6
  • MySQL, MSSQL
  • sox 14.2.0
  • Locken 7.19.7
  • lahm 3.99.5

Beschreibung des Wählplans in einem Sternchen.


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

In diesem Fragment wird ein Makro gestartet, um zu überprüfen, ob der Anrufer unmittelbar nach dem Hören einer Begrüßungsnachricht aufgelegt hat. Als nächstes wird der Wert der Variablen festgelegt:
ITERATIONEN - erforderlich, um den Erkennungsprozess für eine bestimmte Anzahl von Malen zu wiederholen. HANGFLAG - Diese Variable wird vom Hangercheck-Makro verwendet.


 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) 

Legen Sie die Variable für die Aufzeichnungsdatei fest und schreiben Sie die Datei. Wir führen ein agi-Skript aus, das für das Senden der Datei zur Erkennung und Nummernsuche verantwortlich ist (das Skript wird später beschrieben), überprüfen die Variable NUMTOCALL (der Wert wird vom Skript festgelegt) und setzen das Zeichen HANGFLAG, was bedeutet, dass die Person nicht vorzeitig aufgelegt hat.


 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) 

In diesem Fragment wird ein Makro ausgeführt, um zu überprüfen, ob der Anrufer aufgelegt hat, nachdem er die Begrüßungsnachricht gehört hat. Anschließend werden die Variablen festgelegt. ITERATIONS ist erforderlich, um den Erkennungsprozess eine bestimmte Anzahl von Malen zu wiederholen. HANGFLAG - Diese Variable wird vom Hangercheck-Makro verwendet.


 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) 

Legen Sie die Variable für die Aufzeichnungsdatei fest und schreiben Sie die Datei. Wir starten das Skript, das für das Senden der Datei zur Erkennung verantwortlich ist, und suchen nach der Nummer (die Beschreibung des Skripts finden Sie unten), überprüfen die Variable NUMTOCALL (der Wert wird vom Skript festgelegt) und setzen das Zeichen HANGFLAG, dass die Person nicht vorzeitig aufgelegt hat.


 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) 

Das Ausführen des vrstat-Makros (verantwortlich für Statistiken, wird aufgrund seiner Trivialität nicht beschrieben). Überprüfen Sie, ob die FNAME-Beschreibung (die Variable wird von pyreq8.py festgelegt) der Anforderung vorhanden ist. Wenn eine Beschreibung vorhanden ist, legen Sie den Namen der Cache-Datei in der Variablen fest und überprüfen Sie deren Vorhandensein. Wenn die Datei nicht vorhanden ist, synthetisieren wir sie, konvertieren sie in MP3, erhöhen die Lautstärke und spielen sie dann (oder wenn der Cache vorhanden ist) ab und rufen den Abonnenten an.


 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) 

Wiederholen Sie die Erkennung. Wenn die Anzahl der Iterationen die angegebene überschreitet, übersetzen wir in Sekretäre.


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

Übergabe an Sekretäre. Wir geben in die Statistik ein, setzen das Flag, dass sie nicht aufgelegt haben. Wir rufen die Sekretärin an.


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

Rufen Sie einen Teilnehmer an, der keine Beschreibung im Verzeichnis hat. Wir verlieren die Nachricht, geben sie in die Statistik ein und rufen sie an.


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

Anrufbeendigungsverarbeitung für die Hangercheck-Makrooperation.


Wählplan Beschreibung. Makros.


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

Wir überprüfen in der Statistikdatenbank, ob die angegebene Nummer die letzten 48 Stunden angerufen hat. Wenn er aufgelegt hat, ohne auf die Erkennung zu warten, geben wir sie in die Statistik ein und verbinden sie mit der Sekretärin.


Kurze Beschreibung der verwendeten SQL- und MySQL-Tabellen.


SQL-Tabelle dbo.phrases.


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


Für diese Tabelle wurde eine Volltextsuche gestartet, mit der beim Empfang einer langen Phrase nach Schlüsselwörtern gewechselt wird. Beispiel: "Bitte verbinden Sie mich mit einem Vertreter der Werbeabteilung". Wenn in der Datenbank ein Eintrag mit dem Satz "Werbeabteilung" vorhanden ist, wird der Anrufer mit der entsprechenden Nummer verbunden.


MySQL-Tabelle recogStats.


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


In dieser Tabelle werden Erkennungsergebnisse gespeichert und Statistiken zur Erkennungszeit erfasst. ctime - Zeit für das Konvertieren der Audiodatei, rtime - Zeit für das Herunterladen und Erkennen, stime - Zeit für die Suche


MySQL-Tabelle ivr_stat.


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


Diese Tabelle wird verwendet, um die Erkennungsergebnisse (RECREZ, rstatus) zu verfolgen, zu denen der Anrufer gemäß den Suchergebnissen für die erkannte Phrase (callnum) gegangen ist, um zu verfolgen, wie viel Zeit eine Person im Erkennungsmenü verbracht hat (Dauer) und um Fehler zu beheben (Kanalname).


MySQL CustomRequests Tabelle.


id(PK, int), nomer(int(10)), request(varchar(100))
Ein Hilfsverzeichnis mit zusätzlichen Gruppen und Abteilungen.


MySQL-Nummernbeschreibungstabelle.


Num(text), name(text)


Verzeichnis mit der Beschreibung der eingegebenen Nummer für die Sprachausgabe.


MySQL-Tabellen spravochnik_rus und spravochnik_rus_name_num_no_tops


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


Referenzpersonen mit Nummern. Die erste ist für den internen Gebrauch vollständig. Die zweite, für den externen Gebrauch, ist die Anzahl der Direktoren und Manager in einem Sekretariat zusammengefasst.


AGI-Skript, das erkannte Phrasen konvertiert, sendet und nach ihnen sucht.


Verwendete Bibliotheken:


 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 

Und auch die asterisk.agi- Bibliothek, die wir je nach Debugging importieren (das Debuggen erfolgt auf einem Windows-Computer).


Beschreibung der Variablen.


 WINDEBUG = False 

Debug-Flag


 _digits = re.compile('\d') 

Variable für reguläre Ausdrücke in Zahlen kompiliert.


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

Eindeutige Bezeichnungsvariable.


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

Yandex Speech Kit API-Schlüssel.


 lang = 'ru-RU' 

Sprachoption für Yandex Speechkit.


 topic = 'queries' 

Option für das Erkennungsthema für das Yandex-Sprachkit.


 callnumber = '222' 

Die Standardanrufnummer.


 setVar('NUMTOCALL', callnumber) 

Festlegen der Standard-Rufnummer für den Fall, dass etwas schief geht.


 persondic = dict() 

Wörterbuchname - Nummer.


 persondicFI=dict() 

FI Dictionary - Nummer.


 persondicF=dict() 

Wörterbuch Nachname - Nummer.


 otherdic = dict() 

Ein Wörterbuch mit anderen Einträgen der Form Datensatznummer.


 descriptions = dict() 

Wörterbuch mit Beschreibungen zur Erzeugung von Sprachausgabe, Typ Datensatznummer.


 duplicates = list() 

Serviceliste zum Filtern dauert.


 nums = list() 

Liste mit allen Erweiterungen.


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

Variable Audiodatei mit temporärer Ausgabe.


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

Details zum Herstellen einer Verbindung zu MySQL und 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 = '' 

Importieren Sie die Bibliothek, legen Sie je nach Debugging eine Variable für den Namen der eingegebenen Audiodatei und die Nummer des Anrufers fest.


Funktionsbeschreibung


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

Abhängig vom Wert der Debugging-Variablen werden Meldungen in der Sternchenkonsole oder in stdout angezeigt.


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

Abhängig vom Wert der Debugging-Variablen weisen wir den Wert der Dialplan-Variablen einem Sternchen zu oder zeigen ihn in stdout an.


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

Überprüfen Sie, ob s Ziffern enthält.


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

Wir geben nur Zahlen von s zurück.


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

Überprüfen Sie, ob in der Nummernliste eine Erweiterung vorhanden ist.


 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" 

Erweiterungseinstellungsfunktion.


 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) 

Wenn die empfangene Audioaufzeichnungsdatei auf Erkennung überprüft wird und die Größe zu klein ist, wird davon ausgegangen, dass sie im Mobilteil stumm geschaltet sind.


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

Aufzeichnungsstatistik-Erkennungsfunktion.


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

Diese Funktion füllt die Wörterbücher Nachname (persondicF), Nachname (persondicFI), Name (persondic), Abteilungsnamen (otherdic), Beschreibungen für die Sprachsynthese (Beschreibungen) und alle Firmennummern (nums) aus.
Abhängig von der Länge der Anrufernummer wird eine Tabelle erstellt, die die Anzahl der Direktoren (interner Anrufer) oder nicht (externer Anrufer) enthält.
Das Verzeichnis der Nachnamen wird auf Eindeutigkeit geprüft, um die Anwesenheit von zwei Ivanovs mit unterschiedlichen Nummern auszuschließen.


 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) 

Die Funktion zum Konvertieren der im Sternchen aufgezeichneten Datei, falls sox einen Fehler ausgibt, legen Sie die Standardnummer fest und beenden Sie das Skript.


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

Die Funktion zum Senden einer Datei zur Erkennung und zum Empfangen einer Antwort von Yandex. Wenn die Erkennung erfolgreich ist, nehmen wir die erste Antwort. Ich konnte dies mit der Curl-Bibliothek nicht tun, aus diesem Grund wird diese Option verwendet.


 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 

Die Funktion der Suche nach einem vollständigen Namen, abhängig davon, ob nur der Nachname oder der Nachname und der Vorname zur Eingabe kamen.


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

Kombinationssuchfunktion, Beispiel: Phrase = "Alexey Petr", pdic enthält den Eintrag "Alexeyev Peter", die Funktion get_close_matches gibt "Alexeyev Peter" zurück. Die Funktion kann zu falschen Ergebnissen führen, aber wie die Praxis gezeigt hat, gibt es viel korrektere Antworten. Mit dem Qualitätsparameter können Sie die Genauigkeit der Suche nach ähnlichen Phrasen festlegen.


 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 

Die Hauptfunktion beim Finden von Zahlen nach Phrasen.


Lassen Sie uns diese Funktion in Teilen analysieren.


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

Beispiel: Der Ausdruck "Bitte, Nebenstelle 1234" gibt die Nebenstellennummer (falls vorhanden) zurück.


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

Wenn der Benutzer eine vierstellige Nummer ausgesprochen hat, geben Sie die entsprechende Nebenstelle zurück (falls vorhanden).


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

Um das Programm nicht in kurzen Sätzen unnötig auszuführen (dies kann passieren, wenn Yandex einen Teil des Gesprächs gehört und erkannt hat, wenn der Anrufer die Erkennungsnachricht nicht abgehört hat).


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

Bei langen Phrasen (mehr als fünf Wörter) wenden wir uns sofort der FT-Basis von mssql zu.


 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) 

Wenn die erkannte Phrase aus einem Wort besteht, schauen wir uns zuerst die FT-Datenbank mit einer Genauigkeit von 80% an, suchen nach einer Beschreibung der erkannten Nummer, setzen die Ergebnisvariable und die Statusplan-Nummernplanvariable, andernfalls suchen wir im Wörterbuch der Nachnamen nach ähnlichen Wörtern.


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

Wenn die Phrase aus zwei Wörtern besteht, nehmen wir an, dass es sich um einen Nachnamen und einen Namen handelt.
Wir machen eine Liste von Kombinationen (Ivan Ivan, Ivan Ivan) und suchen nach ähnlichen Übereinstimmungen.


 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 

Bei einer Phrase mit drei Wörtern nehmen wir an, dass es sich um einen vollständigen Namen handelt, erstellen eine Liste mit Kombinationen und suchen im Wörterbuch nach einem vollständigen Namen. Wir geben das Ergebnis zurück, wenn die Variable nicht leer ist.


 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 

Falls wir keine Übereinstimmungen gefunden haben, führen wir eine Suche in der FT-Datenbank durch. Wenn wir eine Übereinstimmung finden, geben wir das Ergebnis und die Beschreibung zurück. Andernfalls geben wir die Standardnummer zurück und legen den Status fest, dass die Phrase nicht gefunden wurde.


Der Hauptteil des Programms.


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

Die Funktion zum Ausfüllen von Wörterbüchern wird gestartet, wenn wir nicht im Debugging-Programm sind - wir füllen die Timer-Variablen aus, überprüfen die Dateigröße, konvertieren, senden sie zur Erkennung, andernfalls - füllen Sie die Checkstring-Variable mit einem Prüfsatz.
Als nächstes füllen wir die Variablen der Timer zur Erkennung aus und suchen nach Phrasen in unserer Struktur. Und im Falle einer Kampffunktion setzen wir eine Variable für den Nummerierungsplan und geben Statistiken ein.


Einige Statistiken.


Juni 2018:
erfolgreiche Anerkennung - 1010
schwiegen in einer Röhre - 78
Erkennung fehlgeschlagen (keine Übereinstimmung gefunden) - 79
kurze Anfrage - 4
anerkannte Abteilung - 7
durchschnittliche Erkennungszeit (durchschnittliche Yandex-Antwortzeit) - 2,6 Sek


Juni 2017:
erfolgreiche Anerkennung - 1271
anerkannte Abteilung - 18
Erkennung fehlgeschlagen (keine Übereinstimmung gefunden) - 127
kurze Anfrage - 9
schwiegen in einer Röhre - 71
durchschnittliche Erkennungszeit (durchschnittliche Yandex-Reaktionszeit) - 1,5 Sek


Dieser Service wird in der Firma, in der ich arbeite, erfolgreich eingesetzt. Ich hoffe, dass meine Erfolge anderen Menschen bei der Umsetzung ihrer Ideen oder ähnlichen Funktionen helfen werden. Bereit, alle Fragen zu beantworten.

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


All Articles