LINE Bot basic build with Wit.ai === 為了讓line bot 可以建入更多彈性的內容,而非僅有寫死在程式內的語言判斷 可以與機器學習串接起來 因為著名的 LUIS.ai 近期開始收費,所以選用最近被 facebook 買下來的 Wit.ai --- 首先創好帳號,並選擇 new App ![](https://i.imgur.com/uiz30Nk.png) ` 若要吃中文,語言記得選Chinese (BETA) ` ![](https://i.imgur.com/HerYxeI.png) --- ### 基礎篇 - 建入entity 從 ![](https://i.imgur.com/1KSIPwb.png) 來訓練我們的ai ![](https://i.imgur.com/I8j47JN.png) 1. 先從一句話去訓練,將一句話的關鍵字選取,分別給予意圖。 :::info "你好,我想要吃蛋餅當早餐" 這句話 有兩個關鍵字: 早餐 & 蛋餅 ::: * 若反白選取沒有內建的意圖,可以直接建立新的 ![](https://i.imgur.com/DRT0NjR.png) * 並建入keyword 2. 點選Validate ![](https://i.imgur.com/hn9Qime.png) 3. 擴充 entity 新增keyword & Synonyms ![](https://i.imgur.com/ANTEawh.png) :::warning 如何建入entity,是根據你要怎麼去使用為主。程式與wit.ai串起來後,判斷後是傳回keyword。 `再舉一個例子,匯率 如下` ![](https://i.imgur.com/Oj14k5p.png) ::: 4. 到這邊就訓練完成。可以反覆使用 sentence 去讓wit 更準確的判斷entity ###### 備註 > 其餘還有 stories等進階功能,詳細可以參閱 [wit.ai docs](https://wit.ai/docs) --- ### 與 line bot 串接 1. nodejs 安裝 node-wit套件 [node-wit官方文件](https://github.com/wit-ai/node-wit) ``` npm install --save node-wit ``` 2. 至 ![](https://i.imgur.com/DCBWc4t.png) 拿取accessToken ![](https://i.imgur.com/Va0IITG.jpg) 3. 設定好 wit ```javascript= const {Wit, log} = require('node-wit'); // generate wit bot const witBot = new Wit({ accessToken: 'your accessToken', logger: new log.Logger(log.DEBUG), // optional }); ``` 4. 看到官方文件,基礎版的 我們只需要用到 message api ![](https://i.imgur.com/I1QYA7d.png) :::info 需要注意的是,到時候程式是直接去跟wit拿,是屬於非同步動作。因此我們需要用到 ES6 的 Promise 去做處理。 也可以用到更新的 async/await (不過只有node 7.0版本以上才支援) ::: > :star2: 關於 Promise - 就是進階版的 callback 可以[避免掉 callback hell](http://huli.logdown.com/posts/292655-javascript-promise-generator-async-es6) 有更好的可讀性, 推薦一個簡單易懂的 [網站](http://azu.github.io/promises-book/#introduction) & [文章](http://eddychang.me/blog/javascript/88-promise-basic-usage.html) > > :eyes: 更進階一點的 可以了解看看 ES6 generator 的 [yield](http://huli.logdown.com/posts/292655-javascript-promise-generator-async-es6) 是什麼 > :star2: 關於 async/await 最新的callBack 可以看 [這裡](https://jigsawye.com/2016/04/18/understanding-javascript-async-await/) 若可以使用async/await 程式會更加簡單好讀 :relaxed: 5. 用message api ```javascript= // messageText - line bot user 傳入的 text witBot.message(messageText, {}) .then((data) => { // data - wit.ai 分析過後的資料回傳回來的 object (請見下方) console.log('Yay, got Wit.ai response: ' + JSON.stringify(data)); console.log('result in msg:' + data.msg_id); const entities = {}; // 只需要entities 所以稍做處理 Object.keys(data.entities).forEach((entityKey) => { entities[entityKey] = data.entities[entityKey][0].value; }); }) .catch(console.error); ``` > 補充 [Object.keys](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) [使用方式](http://stackoverflow.com/questions/8763125/get-array-of-objects-keys) wit message 處理好後丟回來的object ```jsx= { "msg_id": "79171cbb-afbe-4ff9-aaea-a97ed6aef3bc", "_text": "請告訴我日幣匯率是多少", "entities": { // 有兩個entities "ask": [ { "confidence": 1, "type": "value", "value": "告訴我" //這邊我毎建一個word 就當作是keyword //所以直接回回來的就是從message裡抓到的 }, { "confidence": 1, "type": "value", "value": "是多少" } ], "exchangeRate": [ //日幣匯率 { "confidence": 1, "type": "value", "value": "JPY" // return 回來的是 keyword } ] } } ``` 6. 接下來就是,要怎麼把非同步處理好的 `entities` 這個object 回回去給主程式呢? **use Promise** return new Promise回去給主程式 ```javascript= function getIntent(messageText) { return new Promise(function(resolve, reject){ witBot.message(messageText, {}) .then((data) => { console.log('Yay, got Wit.ai response: ' + JSON.stringify(data)); console.log('result in msg:' + data.msg_id); const entities = {}; Object.keys(data.entities).forEach((entityKey) => { entities[entityKey] = data.entities[entityKey][0].value; }); resolve(entities); // callback, return result to main function }) .catch(console.error); }) } ``` 主程式 ```javascript= getIntent(msg).then(function(entities){ // entities 就是我們需要的主要資料了 // 邏輯部分 }.catch((error) => console.log(error)); ``` > 若需要再拿 entities 的 keyword 去做爬蟲,也可以使用Promise --- :sparkles: 若想使用 async / await 程式碼如下 ```javascript= async function getIntent(messageText) { const result = await witBot.message(messageText); const entities = {}; Object.keys(result.entities).forEach((entityKey) => { entities[entityKey] = result.entities[entityKey][0].value; }); const newResult = { ...result, entities }; if (!newResult.entities.intent) { newResult.entities.intent = 'None'; } return newResult; } ``` > [name=Hyman Chen] 主程式 ```javascript= const result = await getIntent(messageText); ``` ###### 真的簡單很多... --- wit 還有一個好用的功能 就是 Inbox 他會紀錄所有由外部傳入的句子 你可以藉此訓練你的wit 點選 ![](https://i.imgur.com/CA7hvUw.png) ![](https://i.imgur.com/HD1ULU8.png) --- 教學就到此,若與line bot串起來的話,真的會增加不少彈性 ![](https://i.imgur.com/gSyCoZx.png)