如何用DialogFlow、Node.js和React构建一个聊天机器人(附代码)

383 阅读13分钟

对我们人类来说,对话是第二天性;它对我们来说是自然而然的,但对机器人来说就不是这样了。即使是一个简单的问题,如 "你今天过得怎么样?",也可以用几种方式重新表述(例如,"过得怎么样?","你怎么样?"),机器人通常都不能理解这些。

我们可以解释问题背后的意图,但要建立一个逻辑来促进与机器人更聪明的对话,需要做很多工作,而对于大多数开发人员来说,从头开始编码是不可行的。

幸运的是,有一种叫做NLU(自然语言理解)的东西,它可以实现更好的人机对话--换句话说,一个智能聊天机器人利用机器学习和其他技术,以便更好地理解人类的互动。

NLU算法不只是识别文本,还能解释文本背后的意图。因此,它是聊天机器人的一种理想算法。在这里阅读更多关于NLU的信息。

这就是本文的目的所在。我们将使用谷歌的NLU平台DialogFlow建立我们的聊天机器人。继续阅读以了解更多关于DialogFlow的信息,以及如何通过这个后续教程将其集成到React应用程序中。

开始使用DialogFlow

简单来说,DialogFlow是一个由NLU驱动的端到端工具,用于设计和整合聊天机器人到我们的界面中。它使用由我们提供的语言训练的机器学习(ML)模型将自然语言翻译成机器可读的数据。它是如何工作的?让我们在建立我们的聊天机器人时学习一下。

打开DialogFlow控制台,用你的谷歌账户登录。登录成功后,我们看到以下界面:

Welcome To Dialogflow Dashboard

首先可能引起你注意的是创建代理 选项。

什么是代理?

没有什么花哨的!聊天机器人本身就是一个代理。收集用户的询问,对其采取行动,并最终发送回应,都由我们的代理处理。

让我们来创建我们的聊天机器人;就我们的例子而言,为一家咖啡馆创建一个机器人如何?猜中我的咖啡店的灵感是没有奖的:

Central Perk Create Agent Dashboard

这就是控制台现在应该有的样子:

DialogFlow Intents Dashboard

现在,屏幕上出现了更多的专业术语--意图

什么是意图?

控制台说,"意图是用户的查询和你的软件完成的行动之间的映射"。那么,这是什么意思呢?

让我解释一下:就我们的机器人而言,我们希望收到这样的查询:"我想喝杯卡布奇诺 "和 "商店什么时候开门?",等等。我们可以将这些查询归类为用户意图,如 "接单"、"时间 "等。为了处理这些问题,我们将这些类别定义为我们代理中的意图

我们还看到,我们的代理有两个默认的意图;默认的欢迎意图默认的回退意图。让我们更详细地探讨一下它们:

Default Welcome Intent

这里有很多专业术语;让我们一个一个地看。

语境

在人类对话中,为了理解短语,我们通常需要一些背景。同样,对于机器人,一个意图需要知道查询的背景。为了实现这一点,我们通过上下文来连接一个或多个意图。我们将在本文的后面了解更多这方面的信息。

训练短语

这些是训练的例子短语,帮助我们的代理与正确的意图匹配查询。更多的短语和变化将提高意图匹配的准确性。

通过看到默认的短语列表,很明显,这个意图是用来问候我们的用户。

事件

我们刚刚了解到,代理寻找训练短语来触发意图。然而,意图也可以由事件触发。有两种类型的事件:

  • 平台事件:这些是由平台本身提供的,并在平台特定的事件发生时发生(例如,一个 欢迎事件)。
  • 自定义事件:这些是由我们定义的(例如,由我们发出的API调用的响应)。

行动和参数

一旦查询与正确的意图相匹配,接下来就是对其采取行动。为了采取行动,有时我们需要从查询中提取一些数据。为了提取,我们用实体类型定义参数。以此为例:"咖啡店今天营业吗?";这里要提取的参数是今天,这是执行一些逻辑和作出相应反应所需要的关键信息。我们将在本教程的后面学习更多这方面的知识。

响应

对用户的回应。在这里,我们看到的是静态短语,但如果我们利用参数或履行,它们也可以是动态的。

履行

当我们启用履行时,代理会通过我们定义的API调用给出一个动态响应(例如,如果用户想预订一个桌子,我们可以检查数据库并根据可用性做出响应)。同样,稍后会有更多关于这个的内容。

在进一步讨论之前,让我们先试试这个默认的意图。在右边,我们有一个Dialogflow模拟器来测试我们的代理。我们说一些类似于训练短语的东西,代理就会从响应列表中做出反应:

Agent Default Response Screen

而且,如果我们说一些完全不同的东西,代理会触发一个默认的回退意图

Default Response User Question

因此,如果代理未能找到正确的意图,它就会触发后备意图。很顺利,对吗?但是,让我们试着避免它,添加意图来进行整个对话。

添加常规意图

让我们试着创建下图所示的这个对话:

Chatbot Flow Chart

修改默认欢迎意图的响应,以问候和询问订单。像这样;"您好!你想点什么?"。

创建一个接受订单的意图,并将该意图命名为简单而有意义的东西,如接受订单

现在,我们添加训练短语。用户可能会说,"我想喝两杯拿铁"。把这句话添加到训练短语的列表中,就足以让它与这个意图相匹配。但是,我们还需要限定的信息;"2 "和 "拿铁"。

为了提取它们,我们将在短语中添加几个参数 --数量 (@sys.number实体类型)和项目 (@sys.any实体类型):

Take Order Flow Dashboard

添加更多的变化,使之更好。这里是我添加的一些:

Alternative Response

一旦添加了富含参数的短语,这个表格就会自动填充。我们可以将它们标记为必填/可选,并在需要时设置一个默认值。此外,我们需要定义一个问题作为提示,强迫用户输入所需参数,如果第一次没有输入的话。

因此,这意味着,如果我们从列表中得到第一个短语,"我想订购",而项目(必填)缺失,提示将被触发以询问它。如果我们从列表中得到第二个短语;"我想订购摩卡",而数量(可选)不在那里,那么,我们将不提示用户输入数量,而只是设置默认值为1并使用它。

我们可以返回一个动态响应,利用参数 来重复订单,并询问他们是否想要一个附加组件:

Text Response

要做到这一点,创建一个意图来处理用户对附加物问题的反应。

响应将是 " "或 ""。这是引入后续意图的理想场景。顾名思义,这些意图是用来跟进父意图中的对话的。

DialogFlow为 "是"、"否 "或 "取消 "等常见的回答提供了许多预定义的后续意图。另外,如果你需要处理自定义回复,你可以创建你自己的自定义跟进。我们将在本教程中使用预定义的,但如果你想把你自己的东西放在上面,那就取决于你了。

在意向列表中,将鼠标悬停在TakeOrder 意向上,点击添加后续意向。选择**"是**",后续意图就被创建了,名为 "接单-是"。

Take Order Yes Template

你可能已经注意到有一个输入上下文**(TakeOrder-followup**)的添加。为了使后续行动发挥作用,它们需要与父意图相联系,这就是上下文的用武之地。

当我们创建一个后续意向时,一个输出上下文被自动添加到父意向中,一个相同名称的输入上下文被添加到后续意向中。

我们不需要担心训练短语的问题,因为它们已经到位了。现在,为这个意图添加一个动态响应。由于上下文已经到位,我们可以使用父意图中的参数,像这样。

Text Response 1

同样地,创建一个预定义的后续意图来处理Noin response。这个意图将被命名为 "接单--不",在这里也添加一个动态响应。

Text Response 2

代理人已经准备好进行对话了。我们总是可以在模拟器中测试,但让我们尝试不同的东西。点击侧边栏的集成 ,启用网络演示。打开那里提供的URL,开始对话。

Dialogflow Central Perk Agent

代理人按预期工作得很好,但如果能显示订单的总账单金额,那不是很好吗?我们可以从服务器上获取价格并将计算出的金额返回给用户。

与NodeJS服务器连接

在讨论意图的时候,我们遇到了Fulfillment,它有助于通过我们定义的API调用返回动态响应。这就是我们在这里需要与我们的服务器互动的东西。

在侧边栏中找到Fulfillment 并打开它。DialogFlow提供了两种方法来使用履行:

  • 由谷歌云功能提供的在线编辑器。但是,要启用这个功能,我们需要添加一个有效的计费账户,因为这个集成是收费的,如果使用超过一定的限度
    • 一个webhook服务

我将建立一个webhook服务并将其公开。在继续之前,确保该服务符合这里提到的要求。

构建我们的服务器

在一个新的目录下创建一个node应用,并安装必要的依赖:

mkdir centralperk-server
cd centralperk-server
npm init -y
npm i express dialogflow-fulfillment

从index.js中的一个基本服务器开始:

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("Hi from server!");
});

app.listen(8080, () => {
  console.log("server running...");
});

这只是在8080端口运行服务器。现在,让我们写一些代码来处理来自DialogFlow的webhook请求:

const express = require("express");
const app = express();

const { WebhookClient } = require("dialogflow-fulfillment");
const getPrice = require("./helpers");

app.get("/", (req, res) => {
  res.send("Hi from server!");
});

app.post("/", express.json(), (req, res) => {
  const agent = new WebhookClient({ request: req, response: res });

  function handleIntent(agent) {
    const intent = agent.intent;
    const item = agent.contexts[0].parameters.item;
    const quantity = agent.contexts[0].parameters.quantity;
    const billingAmount = getPrice(intent, item, quantity);

    const response =
      intent === "Take Order - yes"
        ? `Great! Your ${quantity} ${item} and cookies will be ready in no time. Please pay ${billingAmount}$.`
        : `Okay! Your ${quantity} ${item} will be ready in no time. Please pay ${billingAmount}$.`;

    agent.add(response);
  }

  const intentMap = new Map();
  intentMap.set("Take Order - yes", handleIntent);
  intentMap.set("Take Order - no", handleIntent);
  agent.handleRequest(intentMap);
});

app.listen(8080, () => {
  console.log("server running...");
});

帮手来计算价格:

const priceList = {
  mocha: 5,
  latte: 7,
  cookies: 2,
};

module.exports = function (intent, item, quantity) {
  const total =
    intent === "Take Order - yes"
      ? priceList[`${item}`] * quantity + priceList["cookies"]
      : priceList[`${item}`] * quantity;

  return total;
};

让我们走过上面的实现。

导入的WebhookClient将处理与DialogFlow的webhook履行API的通信。当一个启用了履行功能的意图被匹配时,一个HTTPS POST webhook请求被发送到我们的服务器。这个请求是由agent.handleRequest(intentMap)处理的。它接收了一个处理程序的地图,每个处理程序都是一个函数回调。这里定义的处理程序从传递的实例中提取所有需要的信息,计算出账单金额,然后最后返回动态响应。

使服务器公开

使用ngrok是将服务器放在互联网上的最快和最简单的方法。按照这里的步骤进行快速设置和安装。一旦完成了这些步骤,运行以下命令:

ngrok http 8080

而安全的公共URL很快就准备好了:

ngrok                                                                                                   (Ctrl+C to quit)                                                                                                                        Visit http://localhost:4040/ to inspect, replay, and modify your requests                                                                                                                                                                       Session Status                online                                                                                    Account                       Piyush (Plan: Free)                                                                       Version                       3.0.6                                                                                     Region                        India (in)                                                                                Latency                       55ms                                                                                      Web Interface                 http://127.0.0.1:4040                                                                     Forwarding                    https://2c73-182-64-199-236.in.ngrok.io -> http://localhost:8080                                                                                                                                                  Connections                   ttl     opn     rt1     rt5     p50     p90                                                                             5       0       0.00    0.01    2.37    5.21

(注意:记住要保持本地服务器的正常运行)

启用webhook

继续在 "履行 "窗口启用webhook,并输入安全的公共URL。此外,请记住,webhook调用需要为两个后续意图启用。

一切看起来不错。现在让我们测试一下:

Using the Chatbot

我们的聊天机器人已经设置好了

将DialogFlow聊天机器人集成到React应用程序中

有很多方法可以将聊天机器人集成到React应用程序中:

  • 在React中从头开始构建聊天小部件。使用像Redux这样的库来处理传入和传出信息的状态,并修改Node服务器来处理来自React应用的调用,以及将它们发送到DialogFlow。这听起来确实很有趣,但要涵盖的内容很多,不在这篇文章的范围之内。
  • 使用Kommunicate更轻松地将DialogFlow聊天机器人集成到React应用程序中。

在这个博客教程中,我们将使用Kommunicate选项。

DialogFlow ES:Kommunicate集成

遵循这些步骤。

进入免费试用,用你的谷歌账户注册。

点击Bot Integrations,选择DialogFlow ES

使用下图中提到的说明,从DialogFlow云账户获取JSON密钥:

Dialogflow Json Key

接下来,我们可以选择一个自定义的头像:

Dialogflow Avatar

这就是我们需要为DialogFlow ES和Kommunicate集成所做的一切

Chatbot Creation Completion

将Kommunicate聊天小部件集成到React应用程序中

创建一个聊天工具组件并在useEffect()中粘贴以下代码:

(function (d, m) {
      var kommunicateSettings = {
        appId: "<YOUR APP_ID>",
        popupWidget: true,
        automaticChatOpenOnNavigation: true,
      };
      var s = document.createElement("script");
      s.type = "text/javascript";
      s.async = true;
      s.src = "https://widget.kommunicate.io/v2/kommunicate.app";
      var h = document.getElementsByTagName("head")[0];
      h.appendChild(s);
      window.kommunicate = m;
      m._globals = kommunicateSettings;
    })(document, window.kommunicate || {});
import React, { useEffect } from "react";

function Chatbot() {
  useEffect(() => {
    (function (d, m) {
      var kommunicateSettings = {
        appId: "<YOUR APP_ID>",
        popupWidget: true,
        automaticChatOpenOnNavigation: true,
      };
      var s = document.createElement("script");
      s.type = "text/javascript";
      s.async = true;
      s.src = "https://widget.kommunicate.io/v2/kommunicate.app";
      var h = document.getElementsByTagName("head")[0];
      h.appendChild(s);
      window.kommunicate = m;
      m._globals = kommunicateSettings;
    })(document, window.kommunicate || {});
  }, []);
  return <div></div>;
}

export default Chatbot;

记住要用你的appId替换占位符。最后,在App组件中导入它:

import "./App.css";
import Chatbot from "./Chatbot";

function App() {
  return (
    <div className="App">
      <Chatbot />
    </div>
  );
}

export default App;

在本地运行该应用,以测试集成:

Chatbot Run Locally

就这样,我们将我们的聊天机器人添加到了我们的React应用程序中。访问这个仪表板,为颜色图标 和通知声音等添加更多的定制内容。

结语

这篇博客就写到这里!我们学习了聊天机器人开发的几个方面;从用DialogFlow构建它,与NodeJS服务器连接,到最后将它整合到React应用中。我希望这对你来说是有意义的,你能够轻松地跟上这个教程。