
簡(jiǎn)評(píng):使用語(yǔ)音命令在今天變得非常普遍,許多手機(jī)用戶(hù)使用像 Siri 和 Cortana 這樣的語(yǔ)音助手,我們的臥室也被亞馬遜的 Echo 和 Google Home 這樣的設(shè)備“入侵”了。這些系統(tǒng)都離不開(kāi)語(yǔ)音識(shí)別軟件,現(xiàn)在,我們的瀏覽器也友好支持了 Web Speech API,可以讓用戶(hù)在 Web 應(yīng)用中集成語(yǔ)音功能。
這篇文章將介紹如何使用 API 來(lái)在瀏覽器中創(chuàng)建人工智能語(yǔ)音聊天界面。這個(gè)應(yīng)用會(huì)識(shí)別用戶(hù)的聲音,并且用一個(gè)合成的聲音來(lái)回答用戶(hù)。因?yàn)?Web Speech API 還處在試驗(yàn)階段,這個(gè)應(yīng)用只在支持的瀏覽器上可用。這篇文章使用的語(yǔ)音識(shí)別和語(yǔ)音合成功能,目前僅在基于 Chromium 的瀏覽器上支持,包括 Chrome 25+ 和 Opera 27+,目前 Firefox,Edge 和 Safari 僅支持語(yǔ)音合成。

Chrome 上運(yùn)行的 demo 視頻鏈接,接下來(lái)我們就來(lái)完成這個(gè) demo !
要完成這個(gè) Web 應(yīng)用,我們需要完成下面三個(gè)主要的步驟:
- 使用 Web Speech API 的 SpeechRecognition 接口來(lái)識(shí)別用戶(hù)的聲音;
- 將用戶(hù)的消息作為文本字符串發(fā)送到商業(yè)的自然語(yǔ)言處理 API;
- 一旦 API.AI 返回了響應(yīng)文本,使用 SpeechSynthesis 接口來(lái)合成語(yǔ)音。

這篇文章使用的完整源代碼在 GitHub 上。(先幫妹子贊一個(gè))
開(kāi)始你的 Node.js 應(yīng)用
首先,我們要用 Node.js 搭建一個(gè) Web 應(yīng)用框架。創(chuàng)建你的應(yīng)用目錄,就像這樣:
.
├── index.js
├── public
│ ├── css
│ │ └── style.css
│ └── js
│ └── script.js
└── views
└── index.html
然后,執(zhí)行下面的命令來(lái)初始化你的 Node.js 應(yīng)用:
$ npm init -f
-f 命令表示接受默認(rèn)的配置(你也可以去掉,然后手動(dòng)配置你的應(yīng)用),這樣會(huì)生成一個(gè) package.json文件,包含一些基本信息。
現(xiàn)在,安裝下面的依賴(lài)庫(kù):
$ npm install express socket.io apiai --save
--save 命令將會(huì)在package.json 中自動(dòng)更新依賴(lài)。
我們將要使用 Express 庫(kù),一個(gè) Node.js 寫(xiě)的 Web 應(yīng)用服務(wù)框架,可以在本地運(yùn)行服務(wù)器。為了實(shí)現(xiàn)在瀏覽器和服務(wù)器之間實(shí)時(shí)雙向交流,我們將會(huì)使用 Socket.IO。同時(shí),我們將會(huì)安裝自然語(yǔ)音處理服務(wù)工具,API.AI用來(lái)構(gòu)建一個(gè) AI 聊天機(jī)器人。
Socket.IO 是一個(gè)在 Node.js 中輕松使用 WebSocket 的庫(kù)。通過(guò)在客戶(hù)端和服務(wù)端建立 socket 連接,只要 Web Speech API(語(yǔ)音消息)或者 API.AI API (AI 消息)返回了文本數(shù)據(jù),我們的聊天信息就能在瀏覽器和服務(wù)器之間往返。
現(xiàn)在,讓我們創(chuàng)建index.js 文件,并實(shí)例化 Express 以及監(jiān)聽(tīng)服務(wù)器:
const express = require('express');
const app = express();
app.use(express.static(__dirname + '/views')); // html
app.use(express.static(__dirname + '/public')); // js, css, images
const server = app.listen(5000);
app.get('/', (req, res) => {
res.sendFile('index.html');
});
下一步,我們將使用 Web Speech API 集成前端代碼。
用 SpeechRecognition 接口接收語(yǔ)音
Web Speech API 有一個(gè)主要的控制接口,叫 SpeechRecognition,從麥克風(fēng)接收用戶(hù)的語(yǔ)音并加以識(shí)別。
創(chuàng)建用戶(hù)界面
這個(gè)應(yīng)用的 UI 很簡(jiǎn)單:一個(gè)打開(kāi)語(yǔ)音識(shí)別的按鈕。打開(kāi)index.html,將前端的 JavaScript 文件(script.js)和 Socket.IO 包含進(jìn)去。
<html lang="en">
<head>…</head>
<body>
…
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.1/socket.io.js"></script>
<script src="js/script.js"></script>
</body>
</html>
然后,我們?cè)?body 中添加一個(gè)按鈕:
<button>Talk</button>
為了讓我們的按鈕看起來(lái)像 demo 中的那樣,我們需要在源代碼中引用style.css文件。
用 JavaScript 捕捉聲音
在 script.js中,調(diào)用 SpeechRecognition 的實(shí)例,Web Speech API 的控制接口:
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();
我們同時(shí)使用了有前綴和沒(méi)有前綴的對(duì)象,因?yàn)?Chrome 目前支持 API 的前綴屬性。
同時(shí),我們使用了 ECMAScript6 語(yǔ)法,因?yàn)?ES6,const 關(guān)鍵字和箭頭函數(shù)以及 Speech API 接口 SpeechRecognition 和 SpeechSynthesis 都在瀏覽器中支持。
你可以隨意地設(shè)置一些屬性變量來(lái)自定義語(yǔ)音識(shí)別:
recognition.lang = 'en-US';
recognition.interimResults = false;
然后,拿到按鈕的 DOM 引用,監(jiān)聽(tīng)點(diǎn)擊事件來(lái)初始化語(yǔ)音識(shí)別:
document.querySelector('button').addEventListener('click', () => {
recognition.start();
});
一旦開(kāi)始語(yǔ)音識(shí)別,就能用 result 事件將剛剛說(shuō)的話(huà)轉(zhuǎn)成文本:
recognition.addEventListener('result', (e) => {
let last = e.results.length - 1;
let text = e.results[last][0].transcript;
console.log('Confidence: ' + e.results[0][0].confidence);
// We will use the Socket.IO here later…
});
這將返回 SpeechRecognitionResultList 對(duì)象,你可以在這個(gè)對(duì)象的數(shù)組中得到文本結(jié)果。同時(shí),你可以看到上面代碼中返回的 confidence 轉(zhuǎn)錄。
現(xiàn)在,是時(shí)候使用 Socket.IO 來(lái)向服務(wù)端發(fā)送文本了。
用 Socket.IO 實(shí)時(shí)交互
你可能會(huì)好奇為什么我們不用簡(jiǎn)單的 HTTP 或者 AJAX 來(lái)代替。你可以通過(guò) POST 方法向服務(wù)端發(fā)送數(shù)據(jù),但是,通過(guò) Socket.IO,我們使用 WebSocket 發(fā)送數(shù)據(jù),因?yàn)?socket 是雙向交流的最佳解決方案,尤其是當(dāng)我們從服務(wù)端向?yàn)g覽器推送事件時(shí)。通過(guò)持續(xù)的 socket 連接,我們不用重新加載瀏覽器或者頻繁地持續(xù)發(fā)送 AJAX 請(qǐng)求。

在 script.js中實(shí)例化 http://Socket.IO:
const socket = io();
然后插入你監(jiān)聽(tīng) SpeechRecognition 的 result 事件代碼:
socket.emit('chat message', text);
現(xiàn)在重新回到 Node.js 代碼,接收這條文本,然后使用 AI 響應(yīng)用戶(hù)。
從 AI 中得到答復(fù)
許多平臺(tái)和服務(wù)都能夠讓你在應(yīng)用中用語(yǔ)音-文本自然語(yǔ)言處理來(lái)集成 AI 系統(tǒng),包括 IBM 的 Watson,微軟的 LUIS 和 臉書(shū)的 Wit.a。為了快速構(gòu)建對(duì)話(huà)界面,我們將使用 API.AI,因?yàn)樗峁┝嗣赓M(fèi)的開(kāi)發(fā)者賬戶(hù),讓我們可以使它的 Web 接口和 Node.js 庫(kù)快速地建立一個(gè)小型對(duì)話(huà)系統(tǒng)。
設(shè)置API.AI
創(chuàng)建一個(gè)賬戶(hù)后,你就創(chuàng)建了一個(gè)“代理”。參考文檔指南的第一步。
接著,點(diǎn)擊左邊菜單的 “Small Talk”,然后開(kāi)啟啟用服務(wù)選項(xiàng)。

用 API.AI 界面自定義你的 small-talk 代理。
點(diǎn)擊菜單中你的代理名字旁邊的齒輪圖標(biāo),回到 “基本設(shè)置” 頁(yè)面,拿到 API 密鑰。你將會(huì)在 Node.js SDK 中使用“客戶(hù)端訪(fǎng)問(wèn)令牌”。
使用API.AI
我們將使用 Node.js SDK 來(lái)將我們的 Node.js 應(yīng)用連接到 API.AI?;氐?index.js,用你的訪(fǎng)問(wèn)令牌初始化 API.AI:
const apiai = require('apiai')(APIAI_TOKEN);
如果你只想在本地運(yùn)行,你可以在這里硬編碼你的 API 密鑰。這里有幾種方式來(lái)設(shè)置環(huán)境變量,但我通常使用 .env 文件來(lái)聲明變量。在 GitHub 中的源碼中,我用 .gitignore 隱藏了我的證書(shū)文件。但是你可以參考 .env-test 文件看看它是如何設(shè)置的。
現(xiàn)在我們要用服務(wù)端的 Socket.IO 來(lái)接收瀏覽器的數(shù)據(jù)。
一旦建立連接收到消息,使用 API.AI 的接口來(lái)響應(yīng)用戶(hù):
io.on('connection', function(socket) {
socket.on('chat message', (text) => {
// Get a reply from API.AI
let apiaiReq = apiai.textRequest(text, {
sessionId: APIAI_SESSION_ID
});
apiaiReq.on('response', (response) => {
let aiText = response.result.fulfillment.speech;
socket.emit('bot reply', aiText); // Send the result back to the browser!
});
apiaiReq.on('error', (error) => {
console.log(error);
});
apiaiReq.end();
});
});
當(dāng) API.AI 返回結(jié)果后,用 Socket.IO 的 socket.emit()方法發(fā)送到客戶(hù)端。
用 SpeechSynthesis 接口讓 AI 發(fā)出聲音
回到 script.js,用 Web Speech API 的 SpeechSynthesis 控制器接口創(chuàng)建一個(gè)合成聲音的函數(shù)。這個(gè)函數(shù)接收一個(gè)字符串參數(shù),讓瀏覽器讀出文本:
function synthVoice(text) {
const synth = window.speechSynthesis;
const utterance = new SpeechSynthesisUtterance();
utterance.text = text;
synth.speak(utterance);
}
上面的代碼首先創(chuàng)建了一個(gè) window.speechSynthesis 這個(gè) API 接入點(diǎn),你可能會(huì)注意到這次是沒(méi)有前綴屬性的,這個(gè) API 比 SpeechRecognition 更廣泛地被支持,所有的瀏覽器都棄用了 SpeechSysthesis 的前綴。
接著創(chuàng)建了一個(gè) SpeechSynthesisUtterance() ,然后設(shè)置要被合成聲音的文本。你可以設(shè)置其他的屬性,比如 voice,來(lái)選擇瀏覽器和操作系統(tǒng)支持的聲音類(lèi)型。最終調(diào)用 SpeechSynthesis.speak() 來(lái)發(fā)出聲音。
現(xiàn)在再次用 Socket.IO 來(lái)獲得服務(wù)端響應(yīng),一旦消息收到了,就調(diào)用上面的函數(shù)。
socket.on('bot reply', function(replyText) {
synthVoice(replyText);
});
讓我們來(lái)試試我們的 AI 機(jī)器人吧!

參考文章:
- “Web Speech API,” Mozilla Developer Network
- “Web Speech API Specification,” W3C
- “Web Speech API: Speech Synthesis” (Microsoft Edge documentation) Microsoft
- Guide, Node.js
- Documentation, npm
- “Hello world example,” Express
- “Get Started,” Socket.IO
還可以試試不同的自然語(yǔ)言處理工具:
原文鏈接:Building A Simple AI Chatbot With Web Speech API And Node.js
推薦閱讀:
JavaScript 數(shù)組和對(duì)象就像書(shū)和報(bào)紙一樣