Robot tactique: peut écouter et ne pas interrompre

La reconnaissance vocale (ci-après - ASR, reconnaissance automatique de la parole) est utilisée pour créer des robots et / ou IVR, ainsi que pour des sondages automatisés. Voximplant utilise l'ASR fourni par la "bonne société" - la reconnaissance Google fonctionne rapidement et avec une grande précision, mais ... Comme toujours, il y a une mise en garde. Une personne peut faire une pause même dans de courtes phrases, et nous avons besoin d'une garantie que l'ASR ne prendra pas une pause comme fin de la réponse. Si l'ASR pense que la personne a fini de parler, alors après la «réponse», le script peut inclure une synthèse vocale avec la question suivante - en même temps, la personne continuera à parler et à avoir une mauvaise expérience utilisateur: le bot / IVR interrompt la personne. Aujourd'hui, nous vous expliquerons comment y faire face, afin que vos utilisateurs ne se fâchent pas lorsqu'ils communiquent avec des assistants de repassage.


Concept


Le but est de poser une question et d'écouter une personne sans l'interrompre et attendre la fin de sa réponse. L'ASR est représenté par un module séparé où il y a un événement ASR.Result - il est déclenché lorsqu'une personne a fini de parler. La spécificité de l'ASR de Google est que ASR.Result avec un texte reconnu reviendra dès qu'une personne marque une pause d'au moins un peu et que Google décide que ladite phrase est reconnue et complétée.

Pour donner à une personne la possibilité de faire une pause, vous pouvez utiliser l'événement ASR.InterimResult . Dans ce document, l'ASR en cours de reconnaissance renvoie l'intégralité du texte "brut", le corrigeant et le modifiant en fonction du contexte - et ainsi de suite jusqu'à ce que l'ASR.Result soit déclenché. Ainsi, l'événement ASR.InterimResult est un indicateur qu'une personne dit actuellement quelque chose . Nous allons nous concentrer uniquement sur lui et voir combien de temps cela ne viendra pas. Et les textes intermédiaires reconnus reçus d'ASR.Résultat - à ajouter.

En termes généraux, cela ressemblera à ceci:

asr.addEventListener(ASREvents.InterimResult, e => { clearTimeout(timer) timer = setTimeout(stop, 3000) }) asr.addEventListener(ASREvents.Result, e => { answer += " " + e.text }) function stop(){ //... } 

Nous révélons l'essence. Minuteries


Pour travailler correctement avec les pauses, vous pouvez créer un objet spécial:

 timeouts = { silence: null, pause: null, duration: null } 

Après avoir posé une question, une personne réfléchit souvent pendant quelques secondes. Il est préférable de régler la minuterie pour le silence au tout début 6-8 secondes, nous enregistrerons l'ID de la minuterie dans le paramètre timeouts.silence .

Les pauses au milieu de la réponse sont optimales en 3-4 secondes, de sorte qu'une personne peut réfléchir, mais n'a pas souffert dans l'attente quand elle a fini. Il s'agit du paramètre timeouts.pause .

La minuterie générale pour la réponse entière - timeouts.duration - est utile si nous ne voulons pas que la personne parle trop longtemps. Il nous protège également des cas où une personne se trouve dans une pièce bruyante avec des voix de fond que nous prendrons pour le discours du client. Et aussi des cas où nous sommes arrivés à un autre robot qui parle avec notre robot en cercle.

Ainsi, au début du script, nous branchons le module ASR, déclarons les variables et créons un objet timeouts :

 require(Modules.ASR) let call, asr, speech = "" timeouts = { silence: null, pause: null, duration: null } 

Appel entrant


Lorsqu'un appel entrant arrive dans le script, l'événement AppEvents.CallAlerting se déclenche . Créons un gestionnaire pour cet événement: répondez à l'appel, saluez le client, lancez la reconnaissance après le message d'accueil. Et laissons une personne interrompre le robot au milieu de la question posée (détails - un peu plus loin).

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

On peut voir que les fonctions startASR et startSilenceAndDurationTimeouts sont appelées - voyons ce que c'est et pourquoi.

Reconnaissance et délais d'attente


La reconnaissance est implémentée dans la fonction startASR . Il crée une instance ASR et dirige la voix de la personne vers cette instance; il contient également des gestionnaires d'événements pour les événements ASREvents.InterimResult et ASREvents.Result . Comme nous l'avons dit ci-dessus, nous traitons ici ASR.InterimResult comme un signe qu'une personne parle. Le gestionnaire de cet événement efface les délais d'expiration créés précédemment, définit une nouvelle valeur pour timeouts.pause et arrête finalement la voix synthétisée (c'est ainsi qu'une personne peut interrompre le bot). Le gestionnaire ASREvents.Result concatène simplement toutes les réponses résultantes dans la variable de parole . Plus précisément, dans ce scénario, la parole n'est en aucun cas utilisée, mais vous pouvez la transférer vers votre backend, par exemple, si vous le souhaitez.

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

Fonction startSilenceAndDurationTimeouts ... Écrit les valeurs des temporisateurs correspondants:

 function startSilenceAndDurationTimeouts() { timeouts.silence = setTimeout(speechAnalysis, 8000) timeouts.duration = setTimeout(speechAnalysis, 30000) } 

Et quelques fonctionnalités supplémentaires


speechAnalysis arrête la reconnaissance et analyse le texte de la parole (qui est obtenu à partir d' ASREvents.Result ). S'il n'y a pas de texte, nous répétons la question; s'il y a un texte, dites poliment au revoir et raccrochez.

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

Le handleSilence est chargé de répéter la question:

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

Enfin, une fonction d'aide pour arrêter ASR:

 function stopASR() { asr.stop() call.removeEventListener(CallEvents.PlaybackFinished) clearTimeout(timeouts.duration) } 

Tous ensemble


liste des 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

Le scénario final montre comment vous pouvez «ennoblir» un robot rectiligne, en y ajoutant un peu de tact et d'attention. Cette méthode n'est certainement pas la seule possible, donc si vous avez des idées sur la façon de terminer avec élégance l'interaction habituelle entre un bot et une personne, partagez les commentaires. Pour ceux qui veulent quelque chose de plus avancé et qui soudainement n'ont pas lu notre tutoriel sur DialogFlow - nous vous recommandons de vous familiariser .

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


All Articles