iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 25
2
Modern Web

使用 Modern Web 技術來打造 Chat App系列 第 25

Day 25:「意圖」與「實體」的應用 -「Dialogflow」

昨天花一天的篇幅在講「問」與「答」的判斷跟回應要怎麼用、怎麼整合在 Bottender 中,但除了問與答以外,其實還有更常見的「意圖」與「實體」的應用方式。

今天要來介紹並試用的是 Google 的 Dialogflow

https://ithelp.ithome.com.tw/upload/images/20191010/201036301L8ejKIWQe.png

在官網上的介紹上看得出來,這個服務很適合串接「Google Assistant/Action on Google」,畢竟是他們自家相關的服務,因此在這個服務中文字、語音是處理的重點。

基本概念

在 Dialogflow 上,他們使用的概念幾乎都是現在一般處理自然語言理解時的常用概念,包括 Intents(意圖)、 Entity(實體)、Context(情境)還有 Fulfillment(執行要求)。

接下來來簡單介紹一下這些概念:

Intent(意圖)

就是要判斷一句話背後的用意,這跟「問」與「答」的問題分類很像似,舉例來說,下面這句話:

我想預約下禮拜二晚上 8 點的晚餐

背後的意圖就是「預約餐廳」。

Entity(實體)

就是句子中有提到的一些重要資訊,舉例來說,下面這句話:

我想預約下禮拜二晚上 8 點的晚餐

裡面的實體「日期」就是「下禮拜二」,時間是「晚上 8 點」。

Context(情境)

因為人的對話會有上下文,所以有時候一句話根據情境會有不同的意思,例如說了一句「ok」那到底是什麼東西 ok?必須要看情面的情境。

這個部分可以用 Bottender 來處理,也可以讓 Dialogflow 處理一部分,比較複雜今天就先不講。

Fulfillment(執行要求)

Dialogflow 上能編輯的回應非常有限,主要是靜態文字回應,但很多時候,其實必須要執行一些程式或是查詢資料庫,這時候就必須要用 Fulfillment,你可以在它的介面上寫程式部署到「Cloud Functions for Firebase」或是讓它打 HTTP API 到 Webhook。

這兩個方式我都不喜歡,所以基本上我不會去使用 Fulfillment,我還是會希望我的程式擁有主控權,而且能好好的進行版控跟測試,所以不管是在介面上寫程式還是去接這個 Webhook 都是把事情變複雜。我基本上還是推薦用程式去呼叫 Dialogflow API。

建立 Agent

從 Dialogflow 的頁面登入後,我們就可以前往 Dialogflow Console:

https://ithelp.ithome.com.tw/upload/images/20191010/201036304ii2EvTKsW.png

(這邊有點小尷尬是 Dialogflow 已經登入一次了,為什麼進 Dialogflow Console 還要再 Sign in....)

如果沒有使用過,應該近來會看到沒有 Agent 的畫面:

https://ithelp.ithome.com.tw/upload/images/20191010/20103630U6Leh8ZQC4.png

這時候就點「Create Agent」來建立 Agent:

https://ithelp.ithome.com.tw/upload/images/20191010/201036301dp7AkJlut.png

預設語系選「zh-tw」,而時區沒看到「Asia/Taipei」所以選了一樣是 +8 時區的香港。

過了大約 30 秒,新的 Agent 就建好了:

https://ithelp.ithome.com.tw/upload/images/20191010/20103630Mba1M207JG.png

(注意:在機器人或是 Bottender App 裡面不一定只能接一個 Agent,也是有依照情況使用不同 Agent 的可能)

裡面預設會有兩個意圖,Welcome Intent 跟 Fallback Intent,Welcome Intent(歡迎意圖)通常是當使用者第一次互動時觸發、Fallback Intent 則是當判斷不出意圖時觸發。

建立意圖

接著我們要來建立一個新意圖,按下「Create Intent」就會進入這個畫面:

https://ithelp.ithome.com.tw/upload/images/20191010/2010363047oqFSN8VW.png

接著把意圖名稱填一下,加一下訓練句,在訓練句上標記一下實體。反白就可以標記:

https://ithelp.ithome.com.tw/upload/images/20191011/20103630eXQjDebold.png

@sys. 開頭的表示是 Dialogflow 內建的實體類別。這邊直接採用上面那個訂餐的範例:

https://ithelp.ithome.com.tw/upload/images/20191010/20103630HYmPUSKOlz.png

這邊另一個重點是要設定一下 Action(動作) 跟 Parameter(參數),因為打 API 時會需要這幾個變數。其他 Context、Event、Response、Fulfillment 等等的我們都不需要用,所以不用動它。

時間太趕了來不及加很多訓練句,雖然我想兩句應該是有點少...。

建完可以直接在右邊「Try it now」的地方測試一下句子:

https://ithelp.ithome.com.tw/upload/images/20191011/20103630zqyoNqhwFm.png

開放 Dialogflow API

除了上面的步驟外,我們需要到 - 「快速入門導覽課程:使用 API 與代理程式互動
」這頁的,「設定 GCP 專案和驗證
」這個部分來下載私密金鑰的 JSON 檔,先同意一下條款後就可以下載檔案囉:

https://ithelp.ithome.com.tw/upload/images/20191011/20103630ynUwVE3Xns.png

https://ithelp.ithome.com.tw/upload/images/20191011/20103630Ims4ajF4Cs.png

載完還不是結束,還必須把 JSON 的路徑放到環境變數:

// .env

GOOGLE_APPLICATION_CREDENTIALS=你的 JSON 檔案路徑

這樣接下來我們寫程式時,Dialogflow SDK 才能讀到這個 JSON 檔。

接上 Bottender

因為 Dialog 有自己的 Node SDK,所以我們可以直接安裝來用

const dialogflow = require('dialogflow'); // 記得安裝
const { format } = require('date-fns'); // 記得安裝

// 這個要填自己的
const PROJECT_ID = 'YOUR-PROJECT_ID';

const sessionClient = new dialogflow.SessionsClient();

module.exports = async function App(context) {
  if (context.event.isText) {
    const sessionPath = sessionClient.sessionPath(
      PROJECT_ID,
      context.session.id
    );

    const request = {
      session: sessionPath,
      queryInput: {
        text: {
          text: context.event.text,
          languageCode: 'zh-tw',
        },
      },
      queryParams: {
        timeZone: 'Asia/Taipei',
      },
    };

    // 偵測意圖
    const responses = await sessionClient.detectIntent(request);
    const { intent, parameters } = responses[0].queryResult;

    if (intent.displayName === '預約餐廳') {
      const { fields } = parameters;
      const date =
        fields.date && fields.date.stringValue
          ? new Date(fields.date.stringValue)
          : new Date();
      const time =
        fields.time && fields.time.stringValue
          ? new Date(fields.time.stringValue)
          : new Date();

      await context.sendText(
        `你想預約的是:${format(date, 'yyyy-MM-dd')} ${format(time, 'p')}`
      );
    } else {
      await context.sendText('聽不懂喔');
    }
  }
};

今天時間有點趕,草率的試試看,好像還 ok 喔:

https://ithelp.ithome.com.tw/upload/images/20191010/2010363084nH4xmLe7.png

之後有空會來提供一下更好的整合方式,這還有點小粗糙。

結語

雖然「意圖」與「實體」應用起來比「問」與「答」更靈活,但要去設計、思考有哪些「意圖」與「實體」並撰寫例句並不是一件簡單的事,而且需要持續地維護以及補強。要怎麼要去使用這些服務沒有唯一的答案,更多時候是要用混合式的解法,看何種方式跟你的程式去結合最合理順暢。Dialogflow 在很多概念上的設計都不錯,但由於它是跟 Google Assistant 做整合的,主要還是在處理文字跟語音上最適合,看起來雖然可以處理比較複雜的互動但那部分就沒有那麼好用了。


上一篇
Day 24:問與答的應用 -「QnA Maker」
下一篇
Day 26:Action 傳參數與模組化 - 傑出的一手
系列文
使用 Modern Web 技術來打造 Chat App30

尚未有邦友留言

立即登入留言