使用 Actions on Google Node.js 用戶端程式庫 (Dialogflow) 建構執行要求

如果您要以 JavaScript 建立執行要求 Webhook,建議透過 Actions on Google Node.js 用戶端程式庫存取並與 Actions on Google 平台互動。

簡介

Node.js 用戶端程式庫是 Actions on Google 的執行要求程式庫,提供以下功能:

  • 支援所有 Actions on Google 功能,包括文字和互動式多媒體回應、帳戶登入、資料儲存、交易等。
  • 提供 JavaScript 中的慣用抽象層,能納入 對話 HTTP/JSON Webhook API
  • 處理執行要求與 Actions on Google 平台之間的低階通訊詳細資料。
  • 可使用 npmyarn 等熟悉的套件管理工具安裝。
  • 可讓您輕鬆在 Cloud Functions for FirebaseAWS Lambda 等無伺服器運算平台上部署執行要求 Webhook。您也可以在雲端服務供應商或自行託管的自行管理環境中託管執行執行 Webhook。
  • Node.js v6.0.0 以上版本相容。

您可以將用戶端程式庫與 Actions on Google 的 Dialogflow 整合或是 Actions SDK 搭配使用。

如要查看使用用戶端程式庫的完整程式碼範例,請參閱範例頁面

查看 API 參考資料

API 參考資料託管於 Actions on Google Node.js 用戶端程式庫 GitHub 頁面。

您也可以在用戶端程式庫程式碼的下載目錄中執行下列指令,以產生參照的本機副本:

yarn docs

產生的文件可在用戶端程式庫程式碼的下載目錄的 docs 資料夾中取得。

瞭解運作方式

使用用戶端程式庫之前,建議您先瞭解執行要求 Webhook 如何使用用戶端程式庫來處理 Actions on Google 傳送至執行要求的使用者要求。

在 JavaScript 中建立執行要求 Webhook 時,您可以在 Google 的 Cloud Functions for Firebase 或 AWS Lambda 等無伺服器運算環境中部署及託管程式碼。您也可以利用 Express 網路架構自行代管程式碼,無須進行其他操作。

在執行階段環境中,執行要求 Webhook 可以呼叫用戶端程式庫中的函式,藉此處理使用者要求,並將回應傳回 Actions on Google,以便轉譯至使用者輸出內容。

執行要求 Webhook 使用用戶端程式庫協助處理的重要工作大致如下:

圖 1. Node.js 用戶端程式庫的高階架構
  1. 接收使用者要求:當使用者向 Google 助理查詢時,Actions on Google 平台會將 HTTP 要求傳送至您的執行要求 Webhook;要求會包含內含意圖和其他資料 (例如使用者輸入內容的原始文字,以及裝置途徑功能) 的 JSON 酬載。如需 JSON 酬載內容的更多範例,請參閱 Dialogflow Webhook 格式對話 Webhook 格式指南。
  2. 架構呼叫格式偵測:針對支援的架構,用戶端程式庫會自動偵測架構的呼叫格式 (例如,要求來自 Express 網路架構或 AWS Lambda),且知道如何順暢處理與 Actions on Google 平台的通訊。
  3. 服務處理常式處理:用戶端程式庫代表 Dialogflow 和 Actions SDK 的對話 HTTP/JSON Webhook API 做為服務函式。您的執行要求 Webhook 會使用適當的服務建立全域 app 執行個體。app 執行個體可做為 HTTP 要求的處理常式,並瞭解服務專用的通訊協定。
  4. 對話處理:用戶端程式庫會以附加至 app 執行個體的 Conversation 物件的形式呈現單次對話資訊。執行要求 Webhook 可以使用 Conversation 物件擷取跨對話儲存的資料或狀態資訊、傳送回應給使用者,或關閉麥克風。
  5. 中介軟體處理:用戶端程式庫可讓您建立自己的對話服務中介軟體,且包含您定義用戶端程式庫在呼叫意圖處理常式前自動執行的一或多個函式。執行要求 Webhook 可以使用中介軟體將屬性或輔助類別新增至 Conversation 物件。
  6. 意圖處理常式處理:用戶端程式庫可讓您為執行要求 Webhook 瞭解的意圖定義處理常式。如果是 Dialogflow,用戶端程式庫會將要求對應至 Dialogflow 主控台中定義的意圖名稱字串,將要求轉送至正確的意圖處理常式。針對 Actions SDK,會根據 Actions on Google 傳送的 intent 屬性進行轉送。
  7. 傳送回應給使用者:為了建構回應,執行要求 Webhook 會呼叫 Conversation#ask() 函式。您可以多次呼叫 ask() 函式,逐步建構回應。用戶端程式庫會使用 JSON 酬載將回應序列化為 HTTP 要求,並傳送至 Actions on Google。close() 函式的行為與 ask() 類似,但會關閉對話。

設定本機開發環境

實作執行要求 Webhook 之前,請務必先安裝用戶端程式庫。

安裝用戶端程式庫

如要將用戶端程式庫安裝至本機開發環境,最簡單的方法是使用套件管理員,例如 npmyarn

如要安裝,請在終端機中執行下列任一指令:

  • 如果使用 npmnpm install actions-on-google
  • 如使用紗線yarn add actions-on-google

設定專案資料夾

視您打算部署執行要求 Webhook (Google 的 Cloud Functions for Firebase、AWS Lambda 或自行託管的 Express) 而定,您可能需要建立特定的專案資料夾結構以儲存檔案。

例如,如果您使用的是 Cloud Functions for Firebase,則可以執行設定 Node.js 和 Firebase CLI 以及為 Cloud Functions 初始化 Firebase 中所述的步驟,設定所需的專案資料夾。如果是 Cloud Functions for Firebase,您通常會在 /functions/index.js 檔案中寫入執行要求 Webhook。

建構應用程式執行個體

Actions on Google 會根據您要使用 DialogflowActions SDK 建構對話動作,或是建構智慧型住宅動作,使用特定的訊息格式與執行要求 Webhook 交換要求和回應。

用戶端程式庫提供三種服務函式,用來表示這些不同的要求和回應通訊協定:

對話服務 (Dialogflow 和 Actions SDK) 都使用對話 Webhook 通訊協定,但每項服務都能以不同方式包裝訊息。

您必須使用服務來建立 app 執行個體。app 執行個體會封裝 Webhook 的全域狀態和執行要求邏輯,並使用服務專屬通訊協定處理 Actions on Google 和執行要求之間的通訊。

您可以設定 app 執行個體的屬性,並呼叫其方法,引導執行要求 Webhook 的行為。您也可以輕鬆將 app 執行個體插入無伺服器運算環境 (例如 Cloud Functions for Firebase),這類環境接受 JavaScript 函式做為 HTTP 要求的處理常式。

如要在執行要求 Webhook 中建立 app 執行個體,請按照下列步驟操作:

  1. 呼叫 require() 函式來匯入「actions-on-google」模組,並載入所需服務。舉例來說,下列程式碼片段說明如何載入 dialogflow 服務和一些用於建構回應的元素,並指派給名為 dialogflow 的常數:

    // Import the service function and various response classes
    const {
      dialogflow,
      actionssdk,
      Image,
      Table,
      Carousel,
    } = require('actions-on-google');

    此處的 actions-on-google 是指在專案資料夾的 package.json 檔案中指定的依附元件 (如需範例,請參閱package.json 範例檔案)。

    取得 app 執行個體時,您可以視需要指定代表複合式回應、輔助意圖,以及您要使用的其他 Actions on Google 功能的類別。如需您可以載入的有效類別完整清單,請參閱對話回應輔助意圖模組的參考說明文件。

  2. 呼叫您載入的服務,建立 app 執行個體。例如:

    const app = dialogflow();

  3. 如要在初始化時設定 app 例項,您可以在呼叫服務時提供 options 物件做為第一個引數。(詳情請參閱 DialogflowOptions。)例如,下列程式碼片段說明如何設定 { debug: true } 旗標,從使用者要求或回應記錄原始 JSON 酬載:

const app = dialogflow({
  debug: true
});

設定事件的處理常式

如要處理用戶端程式庫在使用者與動作的互動生命週期內建立的 Actions on Google 相關事件,您將使用用戶端程式庫建構處理常式,以便處理使用者要求並傳送返回回應。

針對用戶端程式庫可識別的主要類型事件,您可以建立函式做為處理常式:

  • 意圖事件:意圖是使用者要求特定功能時,Actions on Google 就會傳送至執行要求的專屬 ID。如果您使用的是 Dialogflow,這會對應將使用者查詢內容對應至 Dialogflow 代理程式中意圖的 Dialogflow。
  • 錯誤事件:發生 JavaScript 或用戶端程式庫錯誤時,您可以使用 app 執行個體的 catch 函式適當處理錯誤例外狀況。您應實作單一 catch 函式來處理執行要求關注的所有錯誤。
  • 備用事件:當使用者傳送 Actions on Google 無法辨識的查詢時,就會發生備用事件。您可以使用 app 執行個體的 fallback 函式來註冊一般備用處理常式,在系統未符合傳入執行要求的意圖處理常式時,觸發這個處理常式。您應實作單一 fallback 函式來處理所有備用事件。如果您使用 Dialogflow,則沒有其他相符的意圖時,Dialogflow 可能會觸發特定備用意圖。您應為該備用意圖建立對應的意圖處理常式。

每當使用者傳送要求至動作時,app 例項都會建立代表該對話工作階段的 Conversation 物件。這個物件會透過在意圖處理常式函式中傳遞的 conv 變數名稱,做為第一個函式引數存取。您通常會在處理常式中使用 conv 物件,將回應傳送給使用者。

使用者查詢內容也能包含動作可擷取並使用的參數,以修正回應。

  • 如果您使用 Actions SDK,必須在動作套件中定義參數。如果想查看如何從意圖中擷取參數的範例,請參閱 Eliza 程式碼範例
  • 如果您使用 Dialogflow,可以透過 params 變數存取參數值。如要查看在 Dialogflow 中處理具有參數的意圖的範例,請參閱存取參數和背景資訊

設定意圖的處理常式

如要為意圖設定處理常式,請呼叫 app 執行個體的 intent() 函式。例如,如果您使用的是 Dialogflow,則為 DialogflowApp#intent() 函式。在引數中,指定意圖名稱並提供處理常式函式。

如果使用 Dialogflow,則無需為代理程式中的每個意圖設定處理常式。您可以改用 Dialogflow 的內建回應處理常式來自動處理意圖,而不必實作自己的處理常式函式。舉例來說,您可以透過這種方式將預設歡迎意圖委派給 Dialogflow。

以下範例顯示「greeting」和「bye」意圖的意圖處理常式。他們的匿名處理常式函式會使用 conv 引數,並透過 conv.ask() 函式將簡單的字串回應傳回給使用者:

app.intent('Default Welcome Intent', (conv) => {
  conv.ask('How are you?');
});

app.intent('bye', (conv) => {
  conv.close('See you later!');
});

請注意,close() 函式與 ask() 類似,只是會關閉麥克風且對話已結束。

如要進一步瞭解如何為意圖建構處理常式,請參閱「建構意圖處理常式」。

設定錯誤事件的處理常式

如要為錯誤設定處理常式,請呼叫 app 執行個體的 catch() 函式。(例如,如果您使用的是 Dialogflow,則為 DialogflowApp#catch() 函式)。

下列範例顯示簡易擷取錯誤處理常式,該處理常式會將錯誤傳送至控制台輸出內容,並傳回簡單的字串回應,透過 conv.ask() 函式提示使用者:

app.catch((conv, error) => {
  console.error(error);
  conv.ask('I encountered a glitch. Can you say that again?');
});

設定備用事件的處理常式

如要在沒有符合傳入執行要求要求的意圖時設定一般備用處理常式,請呼叫 app 執行個體的 fallback() 函式。(例如,如果您使用的是 Dialogflow,則為 DialogflowApp#fallback() 函式)。

以下範例顯示一個簡單的備用處理常式,該處理常式會傳回簡單的字串回應,透過 conv.ask() 函式提示使用者:

app.fallback((conv) => {
  conv.ask(`I couldn't understand. Can you say that again?`);
});

建構意圖處理常式

本節說明使用用戶端程式庫實作意圖處理常式時的一些常見用途。如要查看用戶端程式庫如何比對意圖,請參閱「瞭解意圖運作方式」一節中的「意圖處理常式處理」一節。

存取參數和情境

如果您使用 Dialogflow,可以在 Dialogflow 代理程式中定義參數背景資訊,藉此維護狀態資訊及控制對話流程。

參數可用於擷取使用者查詢中的重要字詞、詞組或值。Dialogflow 會在執行階段從使用者查詢中擷取對應的參數,而您可以在執行要求 Webhook 中處理這些參數值,以判斷如何回應使用者。

每當使用者傳送要求至動作時,DialogflowApp 執行個體都會建立 parameters 物件,代表 Dialogflow 從該要求中擷取的參數值。您可以透過 params 變數名稱存取這個物件。

以下程式碼片段顯示如何在使用者傳送要求時,從 params 物件存取 name 屬性:

app.intent('Default Welcome Intent', (conv, params) => {
  conv.ask(`How are you, ${params.name}?`);
});

以下另一個程式碼片段的作用相同。大括號 ({}) 會執行 JavaScript 解構作業,以便從 parameters 物件取得 name 屬性,並做為本機變數使用:

app.intent('Default Welcome Intent', (conv, {name}) => {
  conv.ask(`How are you, ${name}?`);
});

在以下程式碼片段中,參數名稱是 full-name,但會去結構化,並指派給名為 name 的本機變數:

app.intent('Default Welcome Intent', (conv, {'full-name': name}) => {
  conv.ask(`How are you, ${name}?`);
});

背景資訊是 Dialogflow 的進階功能。您可以使用結構定義管理對話狀態、流程和分支版本。用戶端程式庫會透過 DialogflowConversation#contexts 物件提供背景資訊的存取權。下列程式碼片段說明如何在執行要求 Webhook 中設定結構定義,以及如何擷取內容物件:

app.intent('intent1', (conv) => {
  const lifespan = 5;
  const contextParameters = {
    color: 'red',
  };
  conv.contexts.set('context1', lifespan, contextParameters);
  // ...
  conv.ask('...');
});

app.intent('intent2', (conv) => {
  const context1 = conv.contexts.get('context1');
  const contextParameters = context1.parameters;
  // ...
  conv.ask('...');
});

app.intent('intent3', (conv) => {
  conv.contexts.delete('context1');
  // ...
  conv.ask('...');
});

存取輔助意圖結果

為方便起見,用戶端程式庫提供輔助意圖類別,可納入動作經常要求的常見使用者資料類型。這些類別包括代表各種 Actions on Google 輔助意圖結果的類別。如果您希望 Google 助理處理對話中使用者必須輸入內容才能繼續對話的部分,就可以使用輔助意圖。

範例:確認輔助程式結果

確認輔助意圖可讓您要求使用者提供是/否確認,並取得結果。下列程式碼片段說明 Webhook 如何根據確認輔助程式意圖傳回的結果來自訂回應。如需更完整的範例,請參閱 Confirmation 類別參考說明文件。

// Create Dialogflow intent with `actions_intent_CONFIRMATION` event
app.intent('get_confirmation', (conv, input, confirmation) => {
  if (confirmation) {
    conv.close(`Great! I'm glad you want to do it!`);
  } else {
    conv.close(`That's okay. Let's not do it now.`);
  }
});

下列程式碼片段說明執行要求 Webhook 如何根據使用者在輪轉介面的輸入內容來自訂回應。輪轉介面元件可讓您的動作顯示一系列選項供使用者挑選。如需更完整的範例,請參閱 Carousel 類別參考說明文件。

app.intent('carousel', (conv) => {
  conv.ask('Which of these looks good?');
  conv.ask(new Carousel({
    items: {
      car: {
        title: 'Car',
        description: 'A four wheel vehicle',
        synonyms: ['automobile', 'vehicle'],
      },
      plane: {
        title: 'Plane',
        description: 'A flying machine',
        synonyms: ['aeroplane', 'jet'],
      }
    }
  }));
});

// Create Dialogflow intent with `actions_intent_OPTION` event
app.intent('get_carousel_option', (conv, input, option) => {
  if (option === 'one') {
    conv.close(`Number one is a great choice!`);
  } else {
    conv.close(`Number ${option} is a great choice!`);
  }
});

設定對話回應物件

用戶端程式庫提供對話回應類別,代表動作可傳送的複合式回應或多媒體元素。如果使用者不需要提供任何輸入內容就能繼續對話,通常就會傳送這些回應或元素。

範例:圖片

下列程式碼片段說明執行要求 Webhook 如何在回應中傳送 Image,該回應會由程式庫自動附加至 BasicCard 回應:

app.intent('Default Welcome Intent', (conv) => {
  conv.ask('Hi, how is it going?');
  conv.ask(`Here's a picture of a cat`);
  conv.ask(new Image({
    url: '/web/fundamentals/accessibility/semantics-builtin/imgs/160204193356-01-cat-500.jpg',
    alt: 'A cat',
  }));
});

發出非同步函式呼叫

Actions on Google Node.js 用戶端程式庫是專為非同步程式設計而設計。意圖處理常式可傳回「承諾」,在執行要求 Webhook 產生回應後就會解決。

下列程式碼片段說明如何進行非同步函式呼叫以傳回承諾物件,然後在執行要求 Webhook 收到「greeting」意圖時以訊息回應。在這個程式碼片段中,我們保證您的執行要求 Webhook 只會在外部 API 呼叫的承諾已解決後傳回對話回應。

在此範例中,我們使用假的 API 取得天氣資料。

/**
 * Make an external API call to get weather data.
 * @return {Promise<string>}
 */
const forecast = () => {
  // ...
};

app.intent('Default Welcome Intent', (conv) => {
  return forecast().then((weather) => {
    conv.ask('How are you?');
    conv.ask(`Today's weather is ${weather}.`);
  });
});

以下簡化程式碼片段的效果相同,但使用 ECMA 2017 (Node.js 版本 8) 中導入的 async await 功能。如要將這個程式碼與 Cloud Functions for Firebase 搭配使用,請確認您使用的firebase-tools 版本正確,且設定正確無誤。

app.intent('Default Welcome Intent', async (conv) => {
  const weather = await forecast();
  conv.ask('How are you?');
  conv.ask(`Today's weather is ${weather}.`);
});

儲存對話資料

用戶端程式庫可讓執行要求 Webhook儲存對話中的資料,以供日後使用。可用於資料儲存的重要物件包括:

下列程式碼片段顯示執行要求 Webhook 如何將資料儲存在您定義的任意屬性 (someProperty) 中,並附加到 Conversation#user.storage 物件。如需更完整的範例,請參閱 Conversation#user.storage 類別參考說明文件。

app.intent('Default Welcome Intent', (conv) => {
  conv.user.storage.someProperty = 'someValue';
  conv.ask('...');
});

您可以使用 Conversation#user 物件來取得使用者相關資訊,包括字串 ID 和個人資訊。某些欄位 (例如 conv.user.name.displayconv.user.email) 會要求針對 Google 登入要求 conv.ask(new Permission)conv.ask(new SignIn)

const {Permission} = require('actions-on-google');
app.intent('Default Welcome Intent', (conv) => {
  if (conv.user.last.seen) {
    conv.ask('Welcome back! How are you?');
  } else {
    conv.ask('Nice to meet you! How are you doing?');
  }
});

app.intent('permission', (conv) => {
  conv.ask(new Permission({
    context: 'To greet you personally',
    permissions: 'NAME',
  }));
});

// Create Dialogflow intent with `actions_intent_PERMISSION` event
app.intent('get_permission', (conv, input, granted) => {
  if (granted) {
    conv.close(`Hi ${conv.user.name.display}!`);
  } else {
    // User did not grant permission
    conv.close(`Hello!`);
  }
});

使用中介軟體調度資源

您可以透過中介軟體擴充用戶端程式庫。

「中介軟體」層包含您定義的一或多個函式,用戶端程式庫會在呼叫意圖處理常式之前自動執行。您可以使用中介軟體層修改 Conversation 執行個體並新增其他功能。

Dialogflow 和 Actions SDK 服務公開了 app.middleware() 函式,可讓您將屬性或輔助類別新增至 Conversation 執行個體。

以下程式碼片段示範如何使用中介軟體:

class Helper {
  constructor(conv) {
    this.conv = conv;
  }

  func1() {
    this.conv.ask(`What's up?`);
  }
}

app.middleware((conv) => {
  conv.helper = new Helper(conv);
});

app.intent('Default Welcome Intent', (conv) => {
  conv.helper.func1();
});

匯出應用程式

如要針對網路架構或無伺服器運算平台公開執行要求 Webhook,您必須將 app 物件匯出為可公開存取的 Webhook。用戶端程式庫支援可立即部署至許多環境。

以下程式碼片段顯示如何在不同執行階段中匯出 app

範例:Cloud Functions for Firebase

const functions = require('firebase-functions');
// ... app code here
exports.fulfillment = functions.https.onRequest(app);

範例:Dialogflow 內嵌編輯器

const functions = require('firebase-functions');

// ... app code here

// Exported function name must be 'dialogflowFirebaseFulfillment'
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

範例:自行管理的 Express 伺服器 (簡易)

const express = require('express');
const bodyParser = require('body-parser');  

// ... app code here

express().use(bodyParser.json(), app).listen(3000);

範例:自行託管的 Express 伺服器 (多個路徑)

const express = require('express');
const bodyParser = require('body-parser');

// ... app code here

const expressApp = express().use(bodyParser.json());

expressApp.post('/fulfillment', app);

expressApp.listen(3000);

範例:AWS Lambda API 閘道

// ... app code here

exports.fulfillment = app;