Webhook 服務

如要在正式版系統中使用執行要求,請實作並部署 Webhook 服務。如要處理完成要求,Webhook 服務必須接受 JSON 要求,並傳回本指南中指定的 JSON 回應。如要瞭解履單和 Webhook 的詳細處理流程,請參閱履單總覽文件

Webhook 服務規定

Webhook 服務必須符合下列規定:

驗證

請務必保護 Webhook 服務安全,確保只有您或 Dialogflow 代理程式有權提出要求。Dialogflow 支援下列驗證機制:

字詞 定義
登入使用者名稱和密碼 在 Webhook 設定中,您可以指定選用的登入使用者名稱和密碼值。如果提供這項資訊,Dialogflow 會在 Webhook 要求中加入授權 HTTP 標頭。這個標頭的格式為 "authorization: Basic <base 64 encoding of the string username:password>"
驗證標頭 在 Webhook 設定中,您可以指定選用的 HTTP 標頭鍵/值組合。如果提供這些標頭,Dialogflow 會將其新增至 Webhook 要求。通常會提供單一鍵值對,且鍵為 authorization
Cloud Functions 內建驗證機制 使用 Cloud Functions 時,您可以採用內建驗證機制。如要使用這類驗證,請勿提供登入使用者名稱、登入密碼或授權標頭。如果您提供任何這類欄位,系統會使用這些欄位進行驗證,而非內建驗證。
服務身分權杖 您可以使用服務身分權杖進行驗證。如果您未提供登入使用者名稱、登入密碼或含有 authorization 鍵的標頭,Dialogflow 會自動假設應使用服務身分權杖,並在 Webhook 要求中加入授權 HTTP 標頭。這個標頭的格式為 "authorization: Bearer <identity token>"
雙向傳輸層安全標準 (TLS) 驗證 請參閱「雙向傳輸層安全標準 (TLS) 驗證」說明文件。

Webhook 要求

如果系統比對的是已設定執行要求的意圖,Dialogflow 則會將 HTTPS POST Webhook 要求傳送至 Webhook 服務。這項要求的主體是 JSON 物件,內含相符意圖的相關資訊。

除了使用者查詢內容,許多整合服務也會傳送一些使用者資訊。例如,可專門用來識別使用者的 ID。這項資訊可透過 Webhook 要求中的 originalDetectIntentRequest 欄位存取,其中會包含從整合平台傳送的資訊。

詳情請參閱 WebhookRequest 參考說明文件。

以下是範例要求:

{
  "responseId": "response-id",
  "session": "projects/project-id/agent/sessions/session-id",
  "queryResult": {
    "queryText": "End-user expression",
    "parameters": {
      "param-name": "param-value"
    },
    "allRequiredParamsPresent": true,
    "fulfillmentText": "Response configured for matched intent",
    "fulfillmentMessages": [
      {
        "text": {
          "text": [
            "Response configured for matched intent"
          ]
        }
      }
    ],
    "outputContexts": [
      {
        "name": "projects/project-id/agent/sessions/session-id/contexts/context-name",
        "lifespanCount": 5,
        "parameters": {
          "param-name": "param-value"
        }
      }
    ],
    "intent": {
      "name": "projects/project-id/agent/intents/intent-id",
      "displayName": "matched-intent-name"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": "en"
  },
  "originalDetectIntentRequest": {}
}

Webhook 回應

Webhook 收到 Webhook 要求後,必須傳送 Webhook 回應。這項回應的主體是 JSON 物件,內含下列資訊:

回覆內容有以下限制:

  • 如果是 Google 助理應用程式,回應必須在 10 秒內發生;至於其他所有應用程式,則應於 5 秒內進行回應,否則要求將會逾時。
  • 回應大小必須小於或等於 64 KiB。

詳情請參閱 WebhookResponse 參考說明文件。

簡訊回覆

文字回覆範例:

{
  "fulfillmentMessages": [
    {
      "text": {
        "text": [
          "Text response from webhook"
        ]
      }
    }
  ]
}

卡片回應

資訊卡回覆範例:

{
  "fulfillmentMessages": [
    {
      "card": {
        "title": "card title",
        "subtitle": "card text",
        "imageUri": "https://example.com/images/example.png",
        "buttons": [
          {
            "text": "button text",
            "postback": "https://example.com/path/for/end-user/to/follow"
          }
        ]
      }
    }
  ]
}

Google 助理回覆

Google 助理回覆範例:

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "this is a Google Assistant response"
            }
          }
        ]
      }
    }
  }
}

背景資訊

設定輸出背景資訊的範例:

{
  "fulfillmentMessages": [
    {
      "text": {
        "text": [
          "Text response from webhook"
        ]
      }
    }
  ],
  "outputContexts": [
    {
      "name": "projects/project-id/agent/sessions/session-id/contexts/context-name",
      "lifespanCount": 5,
      "parameters": {
        "param-name": "param-value"
      }
    }
  ]
}

事件

以下範例會叫用自訂事件

{
  "followupEventInput": {
    "name": "event-name",
    "languageCode": "en-US",
    "parameters": {
      "param-name": "param-value"
    }
  }
}

工作階段實體

設定工作階段實體的範例:

{
  "fulfillmentMessages": [
    {
      "text": {
        "text": [
          "Choose apple or orange"
        ]
      }
    }
  ],
  "sessionEntityTypes":[
    {
      "name":"projects/project-id/agent/sessions/session-id/entityTypes/fruit",
      "entities":[
        {
          "value":"APPLE_KEY",
          "synonyms":[
            "apple",
            "green apple",
            "crabapple"
          ]
        },
        {
          "value":"ORANGE_KEY",
          "synonyms":[
            "orange"
          ]
        }
      ],
      "entityOverrideMode":"ENTITY_OVERRIDE_MODE_OVERRIDE"
    }
  ]
}

自訂酬載

提供自訂酬載的範例:

{
  "fulfillmentMessages": [
    {
      "payload": {
        "facebook": { // for Facebook Messenger integration
          "attachment": {
            "type": "",
            "payload": {}
          }
        },
        "slack": { // for Slack integration
          "text": "",
          "attachments": []
        },
        "richContent": [ // for Dialogflow Messenger integration
          [
            {
              "type": "image",
              "rawUrl": "https://example.com/images/logo.png",
              "accessibilityText": "Example logo"
            }
          ]
        ],
        // custom integration payload here
      }
    }
  ]
}

啟用及管理執行要求

如要使用主控台啟用及管理代理程式的執行要求,請按照下列步驟操作:

  1. 前往 Dialogflow ES 主控台
  2. 選取代理程式。
  3. 選取左側欄選單中的「執行要求」
  4. 將「Webhook」欄位切換為「Enabled」
  5. 在表單中提供 Webhook 服務的詳細資料。 如果 Webhook 不需要驗證,請將驗證欄位保留空白。
  6. 按一下頁面底部的 [Save] (儲存)

啟用完成動作的螢幕截圖。

如要透過 API 啟用及管理代理程式的完成功能,請參閱代理程式參考資料。您可以使用 getFulfillmentupdateFulfillment 方法管理出貨設定。

如要透過控制台為意圖啟用執行要求,請按照下列步驟操作:

  1. 選取左側欄選單中的 [Intents] (意圖)
  2. 選取一個意圖。
  3. 向下捲動至「Fulfillment」(執行要求) 部分。
  4. 將「Enable webhook call for this intent」(為這個意圖啟用 Webhook 呼叫) 切換為開啟。
  5. 按一下 [儲存]

如要透過 API 啟用意圖的執行要求,請參閱意圖參考資料。將 webhookState 欄位設為 WEBHOOK_STATE_ENABLED

Webhook 錯誤

如果 Webhook 服務發生錯誤,應傳回下列其中一個 HTTP 狀態碼:

  • 400 不正確的要求
  • 401 未經授權
  • 403 禁止
  • 404 找不到
  • 500 伺服器錯誤
  • 503 服務無法使用

在下列任何錯誤情況下,Dialogflow 會使用目前比對到的意圖所設定的內建回應,回覆使用者:

  • 回應逾時。
  • 收到錯誤狀態碼。
  • 回覆無效。
  • Webhook 服務無法使用。

此外,如果意圖比對是由偵測意圖 API 呼叫觸發,則偵測意圖回應中的 status 欄位會包含 Webhook 錯誤資訊。例如:

"status": {
    "code": 206,
    "message": "Webhook call failed. <details of the error...>"
}

自動重試

Dialogflow ES 內建機制,可在發生特定 Webhook 錯誤時自動重試,以提升穩定性。系統只會重試非終端錯誤 (例如逾時或連線錯誤)。

如要降低重複呼叫的可能性:

  • 設定較長的 Webhook 逾時門檻。
  • 在 Webhook 邏輯中支援等冪性或重複資料刪除。

使用 Cloud Functions

您可以使用幾種方法,透過 Cloud Functions 執行完成動作。Dialogflow 內嵌編輯器Cloud Functions 整合。使用內嵌編輯器建立及編輯 Webhook 程式碼時,Dialogflow 會與 Cloud Function 建立安全連線。

您也可以使用不是透過內嵌編輯器建立的 Cloud Function (可能是因為您想使用 Node.js 以外的語言)。如果 Cloud Function 與代理程式位於同一個專案,代理程式即可呼叫 Webhook,不需要任何特殊設定。

不過,在下列兩種情況下,您必須手動設定這項整合:

  1. 代理程式專案必須有下列地址的 Dialogflow 服務代理程式 服務帳戶
    service-agent-project-number@gcp-sa-dialogflow.iam.gserviceaccount.com
    建立專案的第一個代理程式時,系統通常會自動建立這個特殊服務帳戶和相關聯的金鑰。 如果您的代理程式是在 2021 年 5 月 10 日前建立,您可能需要使用下列項目觸發建立這個特殊服務帳戶:
    1. 為專案建立新代理程式。
    2. 執行下列指令:
      gcloud beta services identity create --service=dialogflow.googleapis.com --project=agent-project-id
  2. 如果 Webhook 函式與代理程式位於不同專案,您必須在函式專案中,為 Dialogflow 服務代理程式服務帳戶提供 Cloud Functions 叫用者 IAM 角色

服務身分權杖

Dialogflow 呼叫 Webhook 時,會隨要求提供 Google 身分識別權杖。任何 Webhook 都可以選擇使用 Google 用戶端程式庫或開放原始碼程式庫 (例如 github.com/googleapis/google-auth-library-nodejs) 驗證權杖。舉例來說,您可以驗證 ID 權杖的 email,如下所示:

service-agent-project-number@gcp-sa-dialogflow.iam.gserviceaccount.com

範例

下列範例說明如何接收 WebhookRequest 和傳送 WebhookResponse。這些範例會參照在快速入門導覽課程中建立的意圖。

Go

如要向 Dialogflow 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type intent struct {
	DisplayName string `json:"displayName"`
}

type queryResult struct {
	Intent intent `json:"intent"`
}

type text struct {
	Text []string `json:"text"`
}

type message struct {
	Text text `json:"text"`
}

// webhookRequest is used to unmarshal a WebhookRequest JSON object. Note that
// not all members need to be defined--just those that you need to process.
// As an alternative, you could use the types provided by
// the Dialogflow protocol buffers:
// https://godoc.org/google.golang.org/genproto/googleapis/cloud/dialogflow/v2#WebhookRequest
type webhookRequest struct {
	Session     string      `json:"session"`
	ResponseID  string      `json:"responseId"`
	QueryResult queryResult `json:"queryResult"`
}

// webhookResponse is used to marshal a WebhookResponse JSON object. Note that
// not all members need to be defined--just those that you need to process.
// As an alternative, you could use the types provided by
// the Dialogflow protocol buffers:
// https://godoc.org/google.golang.org/genproto/googleapis/cloud/dialogflow/v2#WebhookResponse
type webhookResponse struct {
	FulfillmentMessages []message `json:"fulfillmentMessages"`
}

// welcome creates a response for the welcome intent.
func welcome(request webhookRequest) (webhookResponse, error) {
	response := webhookResponse{
		FulfillmentMessages: []message{
			{
				Text: text{
					Text: []string{"Welcome from Dialogflow Go Webhook"},
				},
			},
		},
	}
	return response, nil
}

// getAgentName creates a response for the get-agent-name intent.
func getAgentName(request webhookRequest) (webhookResponse, error) {
	response := webhookResponse{
		FulfillmentMessages: []message{
			{
				Text: text{
					Text: []string{"My name is Dialogflow Go Webhook"},
				},
			},
		},
	}
	return response, nil
}

// handleError handles internal errors.
func handleError(w http.ResponseWriter, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprintf(w, "ERROR: %v", err)
}

// HandleWebhookRequest handles WebhookRequest and sends the WebhookResponse.
func HandleWebhookRequest(w http.ResponseWriter, r *http.Request) {
	var request webhookRequest
	var response webhookResponse
	var err error

	// Read input JSON
	if err = json.NewDecoder(r.Body).Decode(&request); err != nil {
		handleError(w, err)
		return
	}
	log.Printf("Request: %+v", request)

	// Call intent handler
	switch intent := request.QueryResult.Intent.DisplayName; intent {
	case "Default Welcome Intent":
		response, err = welcome(request)
	case "get-agent-name":
		response, err = getAgentName(request)
	default:
		err = fmt.Errorf("Unknown intent: %s", intent)
	}
	if err != nil {
		handleError(w, err)
		return
	}
	log.Printf("Response: %+v", response)

	// Send response
	if err = json.NewEncoder(w).Encode(&response); err != nil {
		handleError(w, err)
		return
	}
}

Java

如要向 Dialogflow 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。


// TODO: add GSON dependency to Pom file
// (https://mvnrepository.com/artifact/com.google.code.gson/gson/2.8.5)
// TODO: Uncomment the line bellow before running cloud function
// package com.example;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.BufferedWriter;

public class Example implements HttpFunction {

  public void service(HttpRequest request, HttpResponse response) throws Exception {
    JsonParser parser = new JsonParser();
    Gson gson = new GsonBuilder().create();

    JsonObject job = gson.fromJson(request.getReader(), JsonObject.class);
    String str =
        job.getAsJsonObject("queryResult")
            .getAsJsonObject("intent")
            .getAsJsonPrimitive("displayName")
            .toString();
    JsonObject o = null;
    String a = '"' + "Default Welcome Intent" + '"';
    String b = '"' + "get-agent-name" + '"';
    String responseText = "";

    if (str.equals(a)) {
      responseText = '"' + "Hello from a Java GCF Webhook" + '"';
    } else if (str.equals(b)) {
      responseText = '"' + "My name is Flowhook" + '"';
    } else {
      responseText = '"' + "Sorry I didn't get that" + '"';
    }

    o =
        parser
            .parse(
                "{\"fulfillmentMessages\": [ { \"text\": { \"text\": [ "
                    + responseText
                    + " ] } } ] }")
            .getAsJsonObject();

    BufferedWriter writer = response.getWriter();
    writer.write(o.toString());
  }
}

Node.js

如要向 Dialogflow 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。

const functions = require('@google-cloud/functions-framework');

// TODO: Add handleWebhook to 'Entry point' in the Google Cloud Function
functions.http('handleWebhook', (request, response) => {
  const tag = request.body.queryResult.intent.displayName;

  let jsonResponse = {};
  if (tag === 'Default Welcome Intent') {
    //fulfillment response to be sent to the agent if the request tag is equal to "welcome tag"
    jsonResponse = {
      fulfillment_messages: [
        {
          text: {
            //fulfillment text response to be sent to the agent
            text: ['Hello from a GCF Webhook'],
          },
        },
      ],
    };
  } else if (tag === 'get-name') {
    //fulfillment response to be sent to the agent if the request tag is equal to "welcome tag"
    jsonResponse = {
      fulfillment_messages: [
        {
          text: {
            //fulfillment text response to be sent to the agent
            text: ['My name is Flowhook'],
          },
        },
      ],
    };
  } else {
    jsonResponse = {
      //fulfillment text response to be sent to the agent if there are no defined responses for the specified tag
      fulfillment_messages: [
        {
          text: {
            ////fulfillment text response to be sent to the agent
            text: [
              `There are no fulfillment responses defined for "${tag}"" tag`,
            ],
          },
        },
      ],
    };
  }
  response.send(jsonResponse);
});

Python

如要向 Dialogflow 進行驗證,請設定應用程式預設憑證。 詳情請參閱「為本機開發環境設定驗證」。

# TODO: change the default Entry Point text to handleWebhook
import functions_framework


@functions_framework.http
def handleWebhook(request):
    req = request.get_json()

    responseText = ""
    intent = req["queryResult"]["intent"]["displayName"]

    if intent == "Default Welcome Intent":
        responseText = "Hello from a GCF Webhook"
    elif intent == "get-agent-name":
        responseText = "My name is Flowhook"
    else:
        responseText = f"There are no fulfillment responses defined for Intent {intent}"

    # You can also use the google.cloud.dialogflowcx_v3.types.WebhookRequest protos instead of manually writing the json object
    res = {"fulfillmentMessages": [{"text": {"text": [responseText]}}]}

    return res