Robô de tato: pode ouvir e não interrompe

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 //    .     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() }) }) 

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 => { //    speech += " " + e.text }) //    ASR call.sendMediaTo(asr) } 

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() { //   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() }) } } 

O handleSilence é responsável por repetir a pergunta:

 function handleSilence() { call.say(",   . , ,        ?", Language.RU_RUSSIAN_FEMALE) //    3          setTimeout(startASR, 3000) call.addEventListener(CallEvents.PlaybackFinished, startSilenceAndDurationTimeouts) } 

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)
}
view raw asr.js hosted with ❤ by GitHub

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 .

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


All Articles