使用 Actions on Google Node.js 客户端库构建 fulfillment (Dialogflow)

建议使用 Actions on Google Node.js 客户端库来访问和 与 Actions on Google 平台交互 执行 webhook。

简介

Node.js 客户端库是适用于 提供以下功能的 Actions on Google:

  • 支持所有 Actions on Google 功能,包括文本和富媒体响应、 账号登录、数据存储、事务处理等。
  • 在 JavaScript 中提供了一个惯用的抽象层,用于封装 对话 HTTP/JSON webhook API
  • 处理 fulfillment 与 Actions on Google 平台。
  • 可以使用熟悉的软件包管理工具进行安装,例如 npmyarn
  • 让您可以在无服务器计算平台上轻松部署 fulfillment webhook 例如 Cloud Functions for FirebaseAWS Lambda。您还可以托管您的 fulfillment webhook 在云服务提供商或自行托管且自行管理的环境中使用。
  • Node.js v6.0.0 及更高版本兼容。

您可以将此客户端库与 适用于 Actions on Google 的 Dialogflow 集成Actions SDK 搭配使用。

要查看使用客户端库的完整代码示例,您可以访问 示例页面

查看 API 参考文档

API 参考文档托管在 Actions on Google Node.js 客户端库上 GitHub 页面。

您还可以运行以下命令来生成引用的本地副本 命令。 代码:

yarn docs

生成的文档将出现在目录的 docs 文件夹中 您下载客户端库代码的位置

了解工作原理

在使用客户端库之前,了解您的执行 webhook 如何使用客户端库处理 Google 协助行动发送到您的执行端的用户请求会很有帮助。

使用 JavaScript 创建 fulfillment webhook 时,您可以部署和托管代码 无服务器计算环境(例如 Google 的 Cloud Functions for Firebase 或 AWS Lambda。您还可以自行托管代码,无需执行额外的工作 使用 Express Web 框架

在运行时,fulfillment webhook 可以调用 客户端库处理用户请求并将响应发回 Actions on Google,用于渲染到用户输出中。

您的 fulfillment 网络钩子借助 客户端库简述如下:

图 1. Node.js 客户端库的概要架构
  1. 接收用户请求:当用户向 Google 助理发出请求时, Actions on Google 平台会向您的 fulfillment webhook 发送 HTTP 请求;请求 包括一个包含 intent 和其他数据(例如原始数据)的 JSON 载荷, 用户输入的文本以及用户设备的 Surface 功能。 如需查看 JSON 载荷内容的更多示例,请参阅 Dialogflow webhook 格式会话网络钩子格式 指南。
  2. 框架调用格式检测:对于受支持的框架, 客户端库会自动检测框架的调用格式(例如, 如果请求来自 Express Web 框架或 AWS Lambda),并且知道 如何无缝处理与 Actions on Google 平台的通信。
  3. 服务处理程序处理:客户端库代表 适用于 Dialogflow 和 Actions SDK 的对话 HTTP/JSON webhook API 即服务函数您的 fulfillment webhook 会使用相应的服务 创建全局 app 实例。app 实例充当 HTTP 的处理程序 请求并了解服务的具体协议。
  4. 对话处理:客户端库代表 以 Conversation 对象的形式提供每个会话的信息,并附加到 app 实例。您的 fulfillment webhook 可以使用 Conversation 对象执行以下操作: 检索跨对话存储的数据或状态信息、发送响应 或关闭麦克风。
  5. 中间件处理:借助客户端库,您可以创建自己的 自己的对话服务中间件,包含一个或多个函数 则您可以定义客户端库在调用 intent 处理程序。您的 fulfillment webhook 可以使用中间件添加属性 或辅助类添加到 Conversation 对象中。
  6. Intent 处理程序处理:利用客户端库,您可以定义 您的 fulfillment webhook 理解的 intent 的处理程序。对于 Dialogflow, 客户端库通过以下方式将请求路由到正确的 intent 处理程序: 映射到 Dialogflow 控制台。对于 Actions SDK,系统会根据发送的 intent 属性来路由请求 来自 Actions on Google。
  7. 向用户发送响应:为了构建响应,您的 fulfillment webhook 调用 Conversation#ask() 函数。ask() 函数可以 多次调用以逐步构建响应。通过 客户端库将响应序列化为 HTTP 请求,其中包含 JSON 载荷,并将其发送到 Actions on Google。close() 函数具有 行为与 ask() 类似,但会关闭对话。

设置本地开发环境

在实现执行 Webhook 之前,请务必先安装客户端库。

安装客户端库

将客户端库安装到本地开发环境的最简单方法 环境使用软件包管理器,例如 npmyarn

如需安装,请在终端中运行以下命令之一:

  • 如果使用 npmnpm install actions-on-google
  • 如果使用 yarnyarn add actions-on-google

设置项目文件夹

根据您计划部署 fulfillment webhook 的位置(Google 的 Cloud Functions) (例如 Firebase、AWS Lambda 或自托管 Express),您可能需要创建一个 特定的项目文件夹结构来保存您的文件。

例如,如果您使用的是 Cloud Functions for Firebase,则可以设置 所需的项目文件夹,具体如 设置 Node.js 和 Firebase CLI 初始化 Firebase for Cloud Functions。对于 Cloud Functions for Firebase,您通常需要编写 /functions/index.js 文件中的 fulfillment webhook。

构建应用实例

Actions on Google 使用特定的消息格式来交换请求和响应 您的 fulfillment webhook,具体取决于您是否正在构建对话 使用 DialogflowActions SDK 执行操作 或构建智能家居 Action

为了表示这些不同的请求和响应协议,客户端库提供了三个服务函数:

对话式服务(Dialogflow 和 Actions SDK)都使用对话式网络钩子协议,但每项服务封装消息的方式各不相同。

您可以使用服务创建 app 实例。app 实例用于封装 webhook 的全局状态和 fulfillment 逻辑,并处理 使用 协议。

您可以配置 app 实例的属性并调用其方法来 指导 fulfillment webhook 的行为。您还可以轻松插入 app 实例 无服务器计算环境,例如 Cloud Functions for Firebase, 它接受 JavaScript 函数作为 HTTP 请求的处理程序。

如需在 fulfillment 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 实例时,您可以选择指定类 表示富响应、 辅助 intent 和您想使用的其他 Actions on Google 功能。如需查看您可以加载的有效类的完整列表,请参阅对话响应辅助 intent 模块的参考文档。

  2. 通过调用您加载的服务来创建 app 实例。例如

    const app = dialogflow();

  3. 如需在初始化时配置 app 实例,您可以在调用服务时提供 options 对象作为第一个参数。(请参阅 DialogflowOptions 了解更多详情。) 例如,以下代码段展示了如何在日志中记录原始 JSON 载荷: 通过设置 { debug: true } 标志来响应用户请求或响应:

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

为事件设置处理程序

为了处理在 用户与 Action 互动的整个生命周期中,您将使用 客户端库构建用于处理用户请求并将用户请求返回 响应。

您可以创建函数来充当客户端库识别的以下主要事件类型的处理脚本:

  • intent 事件:intent 是 Actions on Google 发送的唯一标识符 您的执行方式。如果 这对应于 Dialogflow 与用户查询 添加到 Dialogflow 代理中的一个意图。
  • 错误事件:发生 JavaScript 或客户端库错误时,您可以使用 app 实例的 catch 函数来适当地处理错误异常。您应该实现单个 catch 函数来处理所有 你的执行方式关注的错误
  • 后备事件:当用户执行以下操作时,系统会发生后备事件 发送 Actions on Google 无法识别的查询。您可以使用 app 实例的 fallback 函数注册通用回退处理脚本,如果没有与传入执行请求匹配的意图处理脚本,系统会触发该脚本。您应该实现单个 fallback 函数来处理所有 后备广告事件。如果您使用的是 Dialogflow,Dialogflow 可触发特定的 后备意图。您应为该后备 intent 创建相应的 intent 处理脚本。

每当用户向您的 Action 发送请求时,app 实例都会创建一个 Conversation 代表该对话会话的对象。可通过以下方式访问此对象: conv 变量名称作为 第一个函数参数。您通常会在处理程序中使用 conv 对象向用户发送响应。

用户查询还可以包含您的 Action 可以提取和使用的参数 来优化回答。

  • 如果您使用 Actions SDK,则可以在 Action 软件包中定义参数。 如需查看有关如何从 intent 中提取参数的示例,请参阅 Eliza 代码示例
  • 如果您使用的是 Dialogflow,则可以通过 params 变量。如需查看在 Dialogflow 中使用参数处理 intent 的示例,请参阅访问参数和上下文

为 intent 设置处理程序

如需为 intent 设置处理程序,请调用 appintent() 函数 实例。例如,如果您使用的是 Dialogflow,则 DialogflowApp#intent() 函数。在参数中,指定 intent 名称并提供处理程序函数。

如果您使用的是 Dialogflow,则无需为 您的代理。作为替代方案,您可以利用 Dialogflow 的内置响应 处理程序自动处理 intent,而无需实现自己的处理程序 函数。例如,可以通过这种方式将默认欢迎 intent 委托给 Dialogflow。

以下示例显示了适用于“greeting”的 intent 处理程序和“再见” intent。它们的匿名处理程序函数接受 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() 类似,但它会关闭麦克风,并结束对话。

如需详细了解如何为 intent 构建处理程序,请参阅 构建 intent 处理程序

为错误事件设置处理程序

如需设置错误处理程序,请调用 appcatch() 函数 实例。(例如,如果您使用的是 Dialogflow,则 DialogflowApp#catch() function.)

以下示例展示了一个简单的捕获错误处理程序,该处理程序会将错误发送到控制台输出,并发回一个简单的字符串响应,以便通过 conv.ask() 函数提示用户:

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

设置后备事件的处理脚本

要在没有 intent 与 传入的 fulfillment 请求,请调用 的 fallback() 函数 您的 app 实例。(例如,如果您使用的是 Dialogflow,则 DialogflowApp#fallback() function.)

以下示例展示了一个简单的回退处理程序,该处理程序会发回一个简单的字符串响应,以便通过 conv.ask() 函数提示用户:

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

构建 intent 处理脚本

本部分介绍了实现 intent 处理程序的一些常见用例 使用客户端库查看客户端库如何匹配 请参阅“intent 处理程序处理”部分 了解运作方式

访问参数和上下文

如果您使用的是 Dialogflow,则可以定义 parameters上下文,以维护 状态信息并控制对话流

参数对于捕获用户中的重要字词、短语或值非常有用 查询。Dialogflow 会在运行时从用户询问中提取相应的参数,您可以在 fulfillment webhook 中处理这些参数值,以确定如何回复用户。

每当用户向您的 Action 发送请求时,DialogflowApp 实例都会 用于创建 parameters 对象,该对象表示 Dialogflow 从中提取的参数值 请求。您可以通过 params 变量名称访问此对象。

以下代码段展示了如何从name params 对象:

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 对象。以下代码段展示了如何以编程方式设置上下文 如何检索上下文对象:

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('...');
});

访问帮助程序 intent 结果

为方便起见,客户端库提供了辅助 intent 类,用于封装 Action 经常请求的常见类型的用户数据。这些 包含表示各种 Actions on Google 结果的类 辅助意图。当您执行以下操作时,可以使用辅助 intent。 用户必须让 Google 助理来处理对话的部分内容, 提供输入继续对话。

示例:确认帮助程序结果

确认帮助程序 intent 可让 您可以要求用户作出“是/否”确认,并得到最终答案。 以下代码段展示了 webhook 如何根据确认辅助 intent 返回的结果自定义其响应。对于 更完整的示例,请参阅 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 如何根据用户对轮播界面的输入自定义其响应。借助轮播组件 您的 Action 会提供一系列选项供用户选择。如需查看更完整的示例,请参阅 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!`);
  }
});

配置对话响应对象

客户端库提供了 对话响应类别 ,用于表示您的 Action 可以发送的富响应或多媒体元素。 通常在用户不需要提供回答时,您就会发送这些回复或元素 任何输入以继续对话。

示例:图片

以下代码段展示了 fulfillment 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 客户端库是专为异步而设计的 intent 处理程序可以返回一个 promise,当您的 fulfillment webhook 生成了响应。

以下代码段展示了如何对 返回一个 promise 对象,然后返回一个消息作为 fulfillment webhook 收到“问候”intent。在此代码段中,该 promise 可确保只有在外部 API 调用的 promise 已解析后,您的执行 webhook 才会返回对话响应。

在此示例中,我们使用虚构 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}.`);
  });
});

以下经过简化的代码段具有相同的效果,但使用的是 async ECMA 2017(Node.js 版本 8)中引入的 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}.`);
});

存储对话数据

客户端库允许您的 fulfillment webhook 在对话中保存数据 供日后使用。可用于数据存储的关键对象包括:

以下代码段展示了 fulfillment webhook 如何将数据存储在 您定义的任意属性 (someProperty),并将其附加到 Conversation#user.storage 对象。有关更完整的示例,请参阅 Conversation#user.storage 类参考文档。

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

您可以使用 Conversation#user 对象来获取用户的相关信息,包括字符串标识符和 个人信息。某些字段,例如 conv.user.name.displayconv.user.email要求申请conv.ask(new Permission)支付 分别为 Google 登录的 NAME 和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!`);
  }
});

使用中间件进行扩缩

您可以通过中间件扩展客户端库。

中间件层由您定义的一个或多个函数组成, 在调用 intent 处理程序。通过使用中间件层,您可以修改 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();
});

导出应用

如需向 Web 框架或无服务器计算平台公开您的执行 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;