对我们人类来说,对话是第二天性;它对我们来说是自然而然的,但对机器人来说就不是这样了。即使是一个简单的问题,如 "你今天过得怎么样?",也可以用几种方式重新表述(例如,"过得怎么样?","你怎么样?"),机器人通常都不能理解这些。
我们可以解释问题背后的意图,但要建立一个逻辑来促进与机器人更聪明的对话,需要做很多工作,而对于大多数开发人员来说,从头开始编码是不可行的。
幸运的是,有一种叫做NLU(自然语言理解)的东西,它可以实现更好的人机对话--换句话说,一个智能聊天机器人利用机器学习和其他技术,以便更好地理解人类的互动。
NLU算法不只是识别文本,还能解释文本背后的意图。因此,它是聊天机器人的一种理想算法。在这里阅读更多关于NLU的信息。
这就是本文的目的所在。我们将使用谷歌的NLU平台DialogFlow建立我们的聊天机器人。继续阅读以了解更多关于DialogFlow的信息,以及如何通过这个后续教程将其集成到React应用程序中。
开始使用DialogFlow
简单来说,DialogFlow是一个由NLU驱动的端到端工具,用于设计和整合聊天机器人到我们的界面中。它使用由我们提供的语言训练的机器学习(ML)模型将自然语言翻译成机器可读的数据。它是如何工作的?让我们在建立我们的聊天机器人时学习一下。
打开DialogFlow控制台,用你的谷歌账户登录。登录成功后,我们看到以下界面:
首先可能引起你注意的是创建代理 选项。
什么是代理?
没有什么花哨的!聊天机器人本身就是一个代理。收集用户的询问,对其采取行动,并最终发送回应,都由我们的代理处理。
让我们来创建我们的聊天机器人;就我们的例子而言,为一家咖啡馆创建一个机器人如何?猜中我的咖啡店的灵感是没有奖的:
这就是控制台现在应该有的样子:
现在,屏幕上出现了更多的专业术语--意图。
什么是意图?
控制台说,"意图是用户的查询和你的软件完成的行动之间的映射"。那么,这是什么意思呢?
让我解释一下:就我们的机器人而言,我们希望收到这样的查询:"我想喝杯卡布奇诺 "和 "商店什么时候开门?",等等。我们可以将这些查询归类为用户意图,如 "接单"、"时间 "等。为了处理这些问题,我们将这些类别定义为我们代理中的意图 。
我们还看到,我们的代理有两个默认的意图;默认的欢迎意图和默认的回退意图。让我们更详细地探讨一下它们:
这里有很多专业术语;让我们一个一个地看。
语境
在人类对话中,为了理解短语,我们通常需要一些背景。同样,对于机器人,一个意图需要知道查询的背景。为了实现这一点,我们通过上下文来连接一个或多个意图。我们将在本文的后面了解更多这方面的信息。
训练短语
这些是训练的例子短语,帮助我们的代理与正确的意图匹配查询。更多的短语和变化将提高意图匹配的准确性。
通过看到默认的短语列表,很明显,这个意图是用来问候我们的用户。
事件
我们刚刚了解到,代理寻找训练短语来触发意图。然而,意图也可以由事件触发。有两种类型的事件:
- 平台事件:这些是由平台本身提供的,并在平台特定的事件发生时发生(例如,一个 欢迎事件)。
- 自定义事件:这些是由我们定义的(例如,由我们发出的API调用的响应)。
行动和参数
一旦查询与正确的意图相匹配,接下来就是对其采取行动。为了采取行动,有时我们需要从查询中提取一些数据。为了提取,我们用实体类型定义参数。以此为例:"咖啡店今天营业吗?";这里要提取的参数是今天,这是执行一些逻辑和作出相应反应所需要的关键信息。我们将在本教程的后面学习更多这方面的知识。
响应
对用户的回应。在这里,我们看到的是静态短语,但如果我们利用参数或履行,它们也可以是动态的。
履行
当我们启用履行时,代理会通过我们定义的API调用给出一个动态响应(例如,如果用户想预订一个桌子,我们可以检查数据库并根据可用性做出响应)。同样,稍后会有更多关于这个的内容。
在进一步讨论之前,让我们先试试这个默认的意图。在右边,我们有一个Dialogflow模拟器来测试我们的代理。我们说一些类似于训练短语的东西,代理就会从响应列表中做出反应:
而且,如果我们说一些完全不同的东西,代理会触发一个默认的回退意图:
因此,如果代理未能找到正确的意图,它就会触发后备意图。很顺利,对吗?但是,让我们试着避免它,添加意图来进行整个对话。
添加常规意图
让我们试着创建下图所示的这个对话:
修改默认欢迎意图的响应,以问候和询问订单。像这样;"您好!你想点什么?"。
创建一个接受订单的意图,并将该意图命名为简单而有意义的东西,如接受订单。
现在,我们添加训练短语。用户可能会说,"我想喝两杯拿铁"。把这句话添加到训练短语的列表中,就足以让它与这个意图相匹配。但是,我们还需要限定的信息;"2 "和 "拿铁"。
为了提取它们,我们将在短语中添加几个参数 --数量 (@sys.number实体类型)和项目 (@sys.any实体类型):
添加更多的变化,使之更好。这里是我添加的一些:
一旦添加了富含参数的短语,这个表格就会自动填充。我们可以将它们标记为必填/可选,并在需要时设置一个默认值。此外,我们需要定义一个问题作为提示,强迫用户输入所需参数,如果第一次没有输入的话。
因此,这意味着,如果我们从列表中得到第一个短语,"我想订购",而项目(必填)缺失,提示将被触发以询问它。如果我们从列表中得到第二个短语;"我想订购摩卡",而数量(可选)不在那里,那么,我们将不提示用户输入数量,而只是设置默认值为1并使用它。
我们可以返回一个动态响应,利用参数 来重复订单,并询问他们是否想要一个附加组件:
要做到这一点,创建一个意图来处理用户对附加物问题的反应。
响应将是 "是 "或 "否"。这是引入后续意图的理想场景。顾名思义,这些意图是用来跟进父意图中的对话的。
DialogFlow为 "是"、"否 "或 "取消 "等常见的回答提供了许多预定义的后续意图。另外,如果你需要处理自定义回复,你可以创建你自己的自定义跟进。我们将在本教程中使用预定义的,但如果你想把你自己的东西放在上面,那就取决于你了。
在意向列表中,将鼠标悬停在TakeOrder 意向上,点击添加后续意向。选择**"是**",后续意图就被创建了,名为 "接单-是"。
你可能已经注意到有一个输入上下文**(TakeOrder-followup**)的添加。为了使后续行动发挥作用,它们需要与父意图相联系,这就是上下文的用武之地。
当我们创建一个后续意向时,一个输出上下文被自动添加到父意向中,一个相同名称的输入上下文被添加到后续意向中。
我们不需要担心训练短语的问题,因为它们已经到位了。现在,为这个意图添加一个动态响应。由于上下文已经到位,我们可以使用父意图中的参数,像这样。
同样地,创建一个预定义的后续意图来处理Noin response。这个意图将被命名为 "接单--不",在这里也添加一个动态响应。
代理人已经准备好进行对话了。我们总是可以在模拟器中测试,但让我们尝试不同的东西。点击侧边栏的集成 ,启用网络演示。打开那里提供的URL,开始对话。
代理人按预期工作得很好,但如果能显示订单的总账单金额,那不是很好吗?我们可以从服务器上获取价格并将计算出的金额返回给用户。
与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调用需要为两个后续意图启用。
一切看起来不错。现在让我们测试一下:
我们的聊天机器人已经设置好了
将DialogFlow聊天机器人集成到React应用程序中
有很多方法可以将聊天机器人集成到React应用程序中:
- 在React中从头开始构建聊天小部件。使用像Redux这样的库来处理传入和传出信息的状态,并修改Node服务器来处理来自React应用的调用,以及将它们发送到DialogFlow。这听起来确实很有趣,但要涵盖的内容很多,不在这篇文章的范围之内。
- 使用Kommunicate更轻松地将DialogFlow聊天机器人集成到React应用程序中。
在这个博客教程中,我们将使用Kommunicate选项。
DialogFlow ES:Kommunicate集成
遵循这些步骤。
进入免费试用,用你的谷歌账户注册。
点击Bot Integrations,选择DialogFlow ES。
使用下图中提到的说明,从DialogFlow云账户获取JSON密钥:
接下来,我们可以选择一个自定义的头像:
这就是我们需要为DialogFlow ES和Kommunicate集成所做的一切
将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;
在本地运行该应用,以测试集成:
就这样,我们将我们的聊天机器人添加到了我们的React应用程序中。访问这个仪表板,为颜色、图标 和通知声音等添加更多的定制内容。
结语
这篇博客就写到这里!我们学习了聊天机器人开发的几个方面;从用DialogFlow构建它,与NodeJS服务器连接,到最后将它整合到React应用中。我希望这对你来说是有意义的,你能够轻松地跟上这个教程。