O reconhecimento de fala (doravante - ASR, Reconhecimento Automático de Fala) é usado para criar bots e / ou IVR, bem como para pesquisas automatizadas. O Voximplant usa o ASR fornecido pela Good Corporation - o reconhecimento do Google funciona rapidamente e com alta precisão, mas ... Como sempre, há uma ressalva. Uma pessoa pode fazer uma pausa, mesmo em frases curtas, e precisamos de uma garantia de que o ASR não fará uma pausa como final da resposta. Se o ASR achar que a pessoa terminou de falar, depois da "resposta", o script poderá incluir a síntese de voz com a seguinte pergunta - ao mesmo tempo, a pessoa continuará falando e terá uma experiência ruim do usuário: o bot / IVR interrompe a pessoa. Hoje, mostraremos como lidar com isso, para que seus usuários não fiquem chateados ao se comunicar com os auxiliares de ferro.
Conceito
O objetivo é fazer uma pergunta e ouvir uma pessoa sem interromper e aguardar o final de sua resposta. O ASR é representado por um 
módulo separado , onde há um evento 
ASR.Result - é acionado quando uma pessoa termina de falar. Os detalhes específicos do ASR do Google são que o resultado do ASR com texto reconhecido retornará assim que uma pessoa parar por pelo menos um pouco e o Google decidir que a frase mencionada é reconhecida e concluída.
Para dar a uma pessoa a oportunidade de fazer uma pausa, você pode usar o evento 
ASR.InterimResult . Nele, o ASR no processo de reconhecimento retorna todo o texto "bruto", corrigindo e alterando, dependendo do contexto - e assim por diante, até que o ASR.Result seja acionado. Portanto, o evento ASR.InterimResult 
é um indicador de que uma pessoa está dizendo algo no momento . Vamos nos concentrar apenas nele e ver quanto tempo não chega. E os textos reconhecidos intermediários recebidos do ASR.Result - para adicionar.
Em termos gerais, ficará assim:
asr.addEventListener(ASREvents.InterimResult, e => { clearTimeout(timer) timer = setTimeout(stop, 3000) }) asr.addEventListener(ASREvents.Result, e => { answer += " " + e.text }) function stop(){  
Nós revelamos a essência. Temporizadores
Para funcionar corretamente com pausas, você pode criar um objeto especial:
 timeouts = { silence: null, pause: null, duration: null } 
Depois de fazer uma pergunta, uma pessoa costuma pensar por alguns segundos. É melhor definir o timer para o silêncio logo no início de 6 a 8 segundos, salvaremos o ID do timer no parâmetro 
timeouts.silence .
As pausas no meio da resposta são ótimas em 3-4 segundos, para que uma pessoa possa pensar, mas não sofreu expectativa quando terminou. Este é o parâmetro 
timeouts.pause .
O cronômetro geral para toda a resposta - 
timeouts.duração - é útil se não queremos que a pessoa fale por muito tempo. Também nos protege de casos em que uma pessoa está em uma sala barulhenta com vozes de fundo que serão tomadas por nós para a fala do cliente. E também nos casos em que chegamos a outro robô que fala com o robô em círculo.
Portanto, no início do script, conectamos o módulo ASR, declaramos as variáveis e criamos um objeto de 
timeouts :
 require(Modules.ASR) let call, asr, speech = "" timeouts = { silence: null, pause: null, duration: null } 
Chamada recebida
Quando uma chamada de entrada chega no script, o evento 
AppEvents.CallAlerting é acionado . Vamos criar um manipulador para este evento: atender a chamada, cumprimentar o cliente, iniciar o reconhecimento após a saudação. E vamos deixar uma pessoa interromper o robô no meio da pergunta (detalhes - um pouco mais).
manipulador AppEvents.CallAlerting VoxEngine.addEventListener(AppEvents.CallAlerting, e => { call = e.call  
 Pode-se observar que as funções 
startASR e 
startSilenceAndDurationTimeouts são chamadas - 
vamos ver o que é isso e por quê.
Reconhecimento e tempos limite
O reconhecimento é implementado na função 
startASR . Ele cria uma instância ASR e direciona a voz da pessoa para essa instância; também contém manipuladores de eventos para os 
eventos ASREvents.InterimResult e 
ASREvents.Result . Como dissemos acima, aqui tratamos o ASR.InterimResult como um sinal de que uma pessoa está falando. O manipulador deste evento limpa os tempos limites criados anteriormente, define um novo valor para 
timeouts.pause e 
, finalmente, interrompe a voz sintetizada (é assim que uma pessoa pode interromper o bot). O manipulador ASREvents.Result simplesmente concatena todas as respostas resultantes na variável de 
fala . Especificamente, nesse cenário, a 
fala não é usada de forma alguma, mas você pode 
transferi-la para o back-end, por exemplo, se desejar.
startASR function startASR() { asr = VoxEngine.createASR({ lang: ASRLanguage.RUSSIAN_RU, interimResults: true }) asr.addEventListener(ASREvents.InterimResult, e => { clearTimeout(timeouts.pause) clearTimeout(timeouts.silence) timeouts.pause = setTimeout(speechAnalysis, 3000) call.stopPlayback() }) asr.addEventListener(ASREvents.Result, e => {  
 Função 
startSilenceAndDurationTimeouts ... Grava os valores dos timers correspondentes:
 function startSilenceAndDurationTimeouts() { timeouts.silence = setTimeout(speechAnalysis, 8000) timeouts.duration = setTimeout(speechAnalysis, 30000) } 
E mais alguns recursos
speechAnalysis interrompe o reconhecimento e analisa o texto da 
fala (obtido de 
ASREvents.Result ). Se não houver texto, repetimos a pergunta; se houver um texto, diga educadamente adeus e desligue.
speechAnalysis function speechAnalysis() {  
 O 
handleSilence é responsável por repetir a pergunta:
 function handleSilence() { call.say(",   . , ,        ?", Language.RU_RUSSIAN_FEMALE)  
Finalmente, uma função auxiliar para parar o ASR:
 function stopASR() { asr.stop() call.removeEventListener(CallEvents.PlaybackFinished) clearTimeout(timeouts.duration) } 
Todos juntos
lista de scripts|  | require(Modules.ASR) | 
|  |  | 
|  | let call, | 
|  | asr, | 
|  | speech = "" | 
|  |  | 
|  | timeouts = { | 
|  | silence: null, | 
|  | pause: null, | 
|  | duration: null | 
|  | } | 
|  |  | 
|  | // CallAlerting , | 
|  | VoxEngine.addEventListener(AppEvents.CallAlerting, e => { | 
|  |  | 
|  | call = e.call | 
|  |  | 
|  | // . Connected | 
|  | call.answer() | 
|  | call.addEventListener(CallEvents.Connected, e => { | 
|  | call.say(", . , , ?", Language.RU_RUSSIAN_FEMALE) | 
|  |  | 
|  | // 4 | 
|  | setTimeout(startASR, 4000) | 
|  | // | 
|  | call.addEventListener(CallEvents.PlaybackFinished, startSilenceAndDurationTimeouts) | 
|  | }); | 
|  | call.addEventListener(CallEvents.Disconnected, e => { | 
|  | VoxEngine.terminate() | 
|  | }) | 
|  | }) | 
|  |  | 
|  | function startASR() { | 
|  | asr = VoxEngine.createASR({ | 
|  | lang: ASRLanguage.RUSSIAN_RU, | 
|  | interimResults: true | 
|  | }) | 
|  |  | 
|  | asr.addEventListener(ASREvents.InterimResult, e => { | 
|  | clearTimeout(timeouts.pause) | 
|  | clearTimeout(timeouts.silence) | 
|  |  | 
|  | timeouts.pause = setTimeout(speechAnalysis, 3000) | 
|  | call.stopPlayback() | 
|  | }) | 
|  |  | 
|  | asr.addEventListener(ASREvents.Result, e => { | 
|  | // | 
|  | speech += " " + e.text | 
|  | }) | 
|  |  | 
|  | // ASR | 
|  | call.sendMediaTo(asr) | 
|  | } | 
|  |  | 
|  | function startSilenceAndDurationTimeouts() { | 
|  | timeouts.silence = setTimeout(speechAnalysis, 8000) | 
|  | timeouts.duration = setTimeout(speechAnalysis, 30000) | 
|  | } | 
|  |  | 
|  | function speechAnalysis() { | 
|  | // ASR | 
|  | stopASR() | 
|  | const cleanText = speech.trim().toLowerCase() | 
|  |  | 
|  | if (!cleanText.length) { | 
|  | // , , | 
|  | // .. , , , | 
|  | handleSilence() | 
|  | } else { | 
|  | call.say( | 
|  | " ! !", | 
|  | Language.RU_RUSSIAN_FEMALE | 
|  | ) | 
|  | call.addEventListener(CallEvents.PlaybackFinished, () => { | 
|  | call.removeEventListener(CallEvents.PlaybackFinished) | 
|  | call.hangup() | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | function handleSilence() { | 
|  | call.say(", . , , ?", Language.RU_RUSSIAN_FEMALE) | 
|  |  | 
|  | // 3 | 
|  | setTimeout(startASR, 3000) | 
|  | call.addEventListener(CallEvents.PlaybackFinished, startSilenceAndDurationTimeouts) | 
|  | } | 
|  |  | 
|  | function stopASR() { | 
|  | asr.stop() | 
|  | call.removeEventListener(CallEvents.PlaybackFinished) | 
|  | clearTimeout(timeouts.duration) | 
|  | } | 
 O cenário final mostra como você pode "enobrecer" um robô retilíneo, acrescentando um pouco de tato e atenção. Certamente esse método não é o único possível; portanto, se você tiver alguma ideia de como terminar com elegância a interação usual entre um bot e uma pessoa, compartilhe nos comentários. Para quem quer algo mais avançado e de repente não leu nosso tutorial sobre o DialogFlow, 
recomendamos que você se familiarize .