使用 JS 语音识别构建虚拟助手
新的学年即将开始,我的妻子(一位高中西班牙语老师)对她的课程有了个想法。所以,让我向大家介绍我们的最新作品:教师助理弗里达 (Frida)。一个动画角色,她会用西班牙语聆听并回答一些基本问题。
它是使用原生 JavaScript 和内联 SVG 构建的。遗憾的是,目前它只能在特定浏览器中运行,因为它使用了两个尚未得到广泛支持的实验性 Web API(语音合成和语音识别)。
它仍在开发中,需要不断完善,但看起来有望实现我们的需求。毕竟,它只用了一个下午就快速开发完成(包括 SVG),HTML、JS 和 CSS 代码不到 250 行。
在我们继续之前,这是 Frida 的一个演示(要查看正在运行的演示,请转到本文底部):
为什么选择 JavaScript?为什么选择 Frida?
拥有一个能够分析语音并做出相应回复的虚拟助手,听起来需要的不仅仅是原生 JavaScript。即使有可能,构建这样的东西可能也有更好的替代方案……但我们还是遇到了一些限制。
该助理是为一所高中服务的,学校 IT 部门有限制:
- 我们无法安装任何软件。因此,开发应用程序是不可能的,这极大地限制了我们可以使用的技术。
- 我们无法访问某些网站,因为学校防火墙阻止了其中的许多网站(在某些情况下,列表有点荒谬。)
- 我们无法将内容导入网站(这与上一点相关)。
我们以前曾在学校的网络工具中嵌入过原生 JavaScript,所以这看起来是个不错的选择。而且,它还能把学习曲线降低到只需要 Web Speech API 的程度。
我们并没有期待太多。毕竟,我们想要的是简单的东西。不过,我不得不承认,结果比我们预期的要好得多。
为什么选择弗里达·卡罗?这个助教是西班牙语课的,我们想要一个与西班牙语和西班牙文化相关的、容易辨认的人物。弗里达·卡罗的漫画很符合这个要求……而且画起来也很简单。
你可能已经注意到,我们的大多数决策都有一个规律。大多数决策都基于这样一个问题:“这个问题最简单的解决方案是什么?” 一开始没必要把事情搞得太复杂(反正以后总会有的)。
语音识别 API
语音识别 API 非常庞大,本文不会深入探讨。我们只会查看与虚拟助手相关的代码。访问MDN 网站,了解更多关于该 API 的信息。
这是语音识别代码的简化版本:
// new speech recognition object in Spanish!
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition;
const recognition = new SpeechRecognition();
recognition.lang = "es";
recognition.onstart = function () {
// actions to be performed when speech recognition starts
};
recognition.onspeechend = function () {
// stop speech recognition when the person stops talking
recognition.stop();
}
recognition.onresult = function (event) {
// get the results of the speech recognition
const transcript = event.results[0][0].transcript.toLowerCase();
const confidence = event.results[0][0].confidence;
// perform actions based on transcript and level of confidence
}
出于安全考虑,语音识别代码只能在用户触发事件后执行。这很合理。我们不希望网站在用户不知情的情况下监听他们的对话(尽管他们会被要求提供麦克风访问权限)。
为了解决这个问题,我们添加了一个占据整个屏幕的按钮,单击该按钮即可启动语音识别:
recognition.start();
这样,老师就可以在教室里走动,用鼠标或指针点击按钮。然后,他们可以模拟与助手的对话,或者让学生提出一些要求。
此外,我们可以使用语音识别事件来为助手添加动画效果,并在其聆听时执行某些操作。例如,弗里达扬起了眉毛(这可能是一个错误,因为它会让她看起来像是已经结束了对话。)
现在,我们来谈谈兼容性和支持。Chromium和最新版本的 Safari 支持语音识别 API ,但 Firefox 和大多数移动浏览器不支持:
由于用户(老师)拥有并且将专门使用支持该功能的浏览器(Chrome),因此这对我们来说不是问题。
语音合成API
与语音识别 API 一样,这不会是深入探讨,而是助手如何使用 API 的演示。
我们的助理接下来的任务是回答老师和学生的问题。当然,我们可以录下所有答案,并在合适的时间播放,但那样会很费时间,也有点麻烦……而且,别忘了,我们一直在寻找一种更简单的方案。
最简单的解决方案是使用语音合成 API:为什么每次处理新功能时都要记录一个又一个句子?如果计算机能够读取我们提供的任何短语,那不是更好吗?
而且代码比我们想象的要简单得多。四行代码就帮我们说出了以下几句话:
let speech = new SpeechSynthesisUtterance();
speech.lang = "es";
speech.text = "This is the text to read.";
window.speechSynthesis.speak(speech);
与语音识别 API 类似,在使用语音合成 API 之前,我们需要等待用户交互。幸运的是,我们可以将这两个操作集成到同一个事件中:点击按钮后,助手会监听并处理语音;语音处理完毕并转换为文本后,我们就可以让助手说话了。
语音合成 API 远比这四行代码复杂得多。它允许完全自定义语音:速度、音调,甚至可以从可用列表中选择语音。
在我们的例子中,默认速度很好。对于母语人士来说可能有点慢,但对于学生来说还可以。我们主要关注的是语音本身。Frida 是女声,但默认语音取决于许多因素(浏览器、语言),在某些情况下,它可能是男声。但我们可以通过指定voice
属性来选择它。
让我们来谈谈支持。虽然语音合成 API 仍处于实验阶段,但它已得到广泛支持(它兼容95% 的浏览器!),所以这根本不是问题:
我们可以使用(而且我们也确实使用了)语音合成器事件为助手添加更多动画。例如,在计算机朗读文本时让她的嘴唇动起来……虽然这有点麻烦,因为时间安排并非 100% 准确,但我们需要让它与 CSS 动画匹配,以避免出现奇怪的跳跃。虽然不是很理想,但仍然可行。
这不是人工智能!只是一堆条件语句而已
网上有一个关于人工智能和if
声明的笑话:
这正是我们的助手。我们不会假装它是人工智能。它并非真正的人工智能,而是一堆链式条件语句(甚至不是嵌套的)。
一旦我们获得语音识别的结果,我们就会得到两个值:语音文本本身以及系统对识别结果的置信度。我们注意到,当置信度高于 75% 时,结果通常比较好。
如果是这种情况,那么我们会检查成绩单中的子字符串:
- 现在是几奌?
- 今天的天气预报怎么样?
- 今天是星期几?
- 今天是星期几?
- 谁是最好的老师?
并使用其他 JavaScript API 或预定义句子将它们与自动生成的答案进行匹配:
// default text
let textToSpeak = "Sorry, I didn't understand.";
if (confidence > 0.75) {
if (transcript.indexOf("time") > -1) {
const d = new Date();
const hours = d.getHours();
let minutes = d.getMinutes();
if (minutes === 0) minutes = "o'clock";
textToSpeak = `It is ${hours} ${minutes}`;
} else if (transcript.indexOf("best teacher") > -1) {
textToSpeak = "Miss Montoro is the best teacher";
} else if...
}
// Speech Synthesis code goes here
语音识别 API 允许使用语法来对结果进行分类。虽然我们现在没有使用它们(if
当时的说法似乎更简单),但对于第二个版本来说,这可能是一个令人兴奋的增强功能。
正在运行的演示。下一步是什么?
以下是教师助理 Frida 的演示:
注意:由于
iframe
安全策略原因,该演示在 DEV 版本中嵌入时可能无法运行。点击上方链接或在 CodePen 上编辑即可查看运行效果。
作为一款最低可行产品,这款课堂助手功能尚可,但远未完善。以下是我们考虑改进的一些方面:
- 添加更多句子和问题以理解
- 向语音识别系统添加语法,以便我们可以简化代码(并删除丑陋的条件)
- 添加自定义/设置,以便其他教师可以在小更新后使用它。
- 使用其他 Web API 扩展新功能。我们有一些想法:
- 检测环境噪音,如果达到阈值,让弗里达看起来很生气并说:“请安静!”
- 根据日期和天气为弗里达穿上不同的衣服。
- 让弗里达变得更友善一些(在动画片中她总是看起来很生气。)
- 添加一个包含学生姓名的列表,以便 Frida 可以随机叫他们回答问题/进行测试。