Google Dialogflow 平台 (dialogflow.com) 的前身是业内赫赫有名的 api.ai,为我们提供了对话式交互技能建设能力。Dialogflow 提供了非常出色的端到端对话体验,可以帮助开发人员构建一个引人入胜的人机交互体验。我们可以基于 Dialogflow 建设自己的语音或者文本对话应用程序,用户可以在 Google Assistant、Amazon Alexa、Facebook Messenger 等其他设备或平台上使用。此外,Dialogflow 还支持英语、法语、西班牙语、汉语等多种语言。
首先我们来一起了解下 Dialogflow 平台上的几个基本概念,然后我会详细介绍如何基于 Dialogflow 创建一个天气技能,并在此基础上,构建一个完整的天气对话服务,并完成对创建好的天气对话服务的测试验证。
1.1 Agent
Dialogflow 上定义的 Agent 概念,可以理解为完整的 NLU 处理模块。开发者可以在自己的应用、产品或服务中使用。Agent 的作用是把用户的请求转换为可理解的参数,如下图所示。其中,黄色部分就是一个 Agent。
上图描述了 Dialogflow 中的一个 Agent 的完整工作流程。用户通过文字或者语音向 Agent 发起一个请求,Agent 解析用户请求后,输出 Intent(意图)的结果,然后 Dialogflow 中的 Fulfillment 部分会根据 Intent 的结果去请求相应的内容服务或者直接进行快速回复,并将结果封装成一个可执行的数据,通过语音或者文字展示给用户。
1.2 Intent
Intent 是 Dialogflow 的核心概念,它是 Agent 的一个组件,代表用户请求的真实意图。开发者可预先定义多个 Intent 来处理用户的不同请求。同时,Dialogflow 也预置了多种 Intent 供开发者使用,开发者可通过控制台的 Prebuilt Agents 导入你想要的 Intent。
Agent 处理用户的请求,经过意图分类、属性抽取等流程后,会匹配出一个 Intent,并抽取相应的 parameter。为了能让算法得出更优的结果,开发者应该尽可能多的提供例句,这些例句也尽可能的囊括到所有可能性的问法。
1.3 Entity
Agent 需要抽取出用户请求中有用的名称信息,这些信息就是实体。例如在请求问句“帮我订一张从北京飞往深圳的机票”中,“北京”和“深圳”就是地点 / 位置实体。Dialogflow 的实体分为三类:系统实体、开发者实体、用户实体,系统实体是 Dialogflow 预置的一些通用的实体,如sys.music-artist、sys.language等。开发者可以在 Dialogflow 中增加开发者实体,来满足行业相关或者程序相关的理解;同样地,用户实体跟用户相关,每个用户都可以通过接口创建自己的实体列表。举个自定义实体的例子,用户可以自建一个“广谱抗生素”实体,其词典包含“阿莫西林”、“美洛西林”、“头孢尼西”、“头孢克洛”、“阿奇霉素”等。
1.4 Actions & Parameters
一个 Intent 对应一个 Action,当用户请求被处理后,需要把请求中的关键数据抽取出来,放到 Action 的 Parameters 里面,Action 的名称和 Parameter 名称都在 Dialogflow 的 Action 的编辑区域修改。
1.5 Contexts
Context 代表用户当前请求的上下文,开发者需要在 Intent 页面定义输入 Context 和输出 Context,Agent 会将 Context 中 Parameters 的值在对话间做传递。例如,当用户询问:“今天北京天气怎么样?”,用户期待的结果为“city:北京,date:今天”的天气,紧接着用户会询问“明天呢?”,用户期待的应该是“city:北京,date: 明天”的天气。
1.6 Dialogs
Dialogflow 支持两种类型的对话,分别是线性对话和非线性对话。
线性对话是指对话函数需要收集到全部的必选 Parameter 才能进行下一步。我们来看查询天气的例子,查询天气必需的两个参数是city和date。当从用户的请求中未能抽取到相对应的实体时,对话函数要根据当前缺失的词槽,向用户发起提问,以补全词槽。
非线性对话需要根据上述的 Context,配置不同的 Intent 来处理多样性的对话。举个例子,当需要用户对宾馆住宿体验做个星级评价时,我们需要对用户说的非常差,差,好,非常好等请求,走不同的对话逻辑分支,做出不一样的处理。
1.7 Fulfillment
Fulfillment 页面有两块区域,Webhook 和 Cloud Functions for Firebase。其中,Webhook 需要我们提供一个 Web 服务的 URL,Dialogflow 会将对话生成的 Intent 及其他参数,发送给这个 URL 对应的 Web 服务,该服务会处理这个 Intent 并返回相应结果。Cloud Functions for Firebase 是 Google 提供的函数式编程组件,我们可以在其上编写对话函数代码,平台会自动将其部署到 Google Cloud,并生成 URL 自动填充到 Webhook 中供其调用。
2 技能建设
2.1 Prebuilt Agents 使用
Dialogflow 提供了多种不同的 Prebuilt Agents 供开发者直接使用或者基于此做进一步修改。在开发我们自己的天气对话服务之前,我们先一起领略下 Dialogflow 预置的天气对话。从 Dialogflow 平台页面左侧的菜单点击Prebuilt Agents进入预置技能页面,然后通过在搜索框输入Weather进行搜索,最后导入该预置技能。如下图所示:
导入后,点击Intents可以看到如下图所示中的多个 Intents,在右侧测试框中我们可以输入查询语句,如 "what is the weather tomorrow in Beijing",查看右侧对话结果可以看到,Contexts 的值是weather,Intent 匹配到的名称是weather,Action 的值是weather,从问句中抽取到的具体参数,赋值给date-time和location两个词槽。
从上图的 Intents 列表中,我们可以看到还有很多其他的预置意图。我们先来看weather - context:weather - comment: location这个意图。我们接着发起提问 "what about Shanghai" 时,因为前面已经有了天气的上下文,对话函数可以从中知道当前用户的意图是查询上海的天气。我们再来看weather.outfit这个意图,该意图不单单是询问天气状况,还要根据天气情况,告知用户今天该穿那种衣服合适。在右侧测试区域输入 "should I wear my coat",如果这是对话的第一轮,我们需要用户补齐要询问的时间和地点,如果不是第一轮,我们可以从前几轮对话中获得词槽值,并用文字或者语音告知用户结果。请参考上图。
一个完整的对话不仅仅需要理解用户意图,还需要根据具体的意图去查询相应的内容服务并生成具体的结果,这一过程就是对话函数的处理逻辑。Google Cloud 提供了函数式编程的 Serverless 执行平台,我们可以使用 Google Cloud 搭建简单的对话服务。下面让我们一起从零开始,基于 Dialogflow 创建一个完整的对话服务。
2.2 从零打造一个天气对话技能
Agent 创建成功后会默认生成两个 Intent,如下图所示:
Default Fallback Intent可以看做是一个兜底的意图,在不匹配任何其他意图时会进到该 Intent 中。你可以在右侧测试区域输入 "hello" 测试一下。
Default Welcome Intent是 VUI 默认的欢迎界面。可在该 Intent 中编辑一些向导型的对话例句,表示已经进入天气对话服务。点击进入该 Intent 并在 User says 中输入”hello weather“,保存成功后,右侧测试可看到如下图所示:
下面我们来创建自己的对话程序。在 Intents 页面,点击Create Intent按钮进入 Intent 编辑页面。在该页面输入我们想要的 Intent 名称,并输入例句。在实际的项目中,开发者应该尽可能的多提供例句,这样才有更多的样本,保证更好的效果。如下图所示,本例添加了 3 个例句,并标识出具体的 Entity,Dialogflow 将不同类型的 Entity 标记为不同颜色。在 Action 中输入名称,并设置 Entity 的名称,添加回复,在 Text Response 中,我们添加一个回复$date $city's weather is …,其中$date和$city分别对应前面抽取到具体的词槽。修改完成后,点击保存并在右侧区域输入测试请求 "what is the weather tomorrow in Beijing",可看到相应的参数,点击SHOW JSON查看具体的返回参数。
在当前 Intent 中,加入查询天气服务必需的时间和地点两个词槽,我们可以勾选 Paramter 前面的”REQUIRED“选项,可看到在当前表格中增加了一列 "PROMPTS",点击Define prompts…定义具体的 Slot Filling 问句,如下图所示:
点击保存并在右侧区域进行测试,如输入 "today's weather",因为输入中不包含city词槽,因此必须把city补全,才能进行下一步的操作。如图所示,当前回复为我们定义的缺槽提示。因此,在右侧输入框中输入 "Beijing" 既可把city补全,并进行下一步对话。
若当前对话中用户询问 "What is the weather tomorrow in Beijing?",我们可以告知用户北京明天的天气状况,后续如果用户继续提问 "Is it nice enough to go swimming" 时,我们希望我们的对话服务有能力理解用户的输入,并做出正确的回复。为满足以上需求,我们需要设置 Contexts。在当前 Intent 中,添加输出 Context 为 "weather",并设置相应的 Lifespan,LifeSpan 我们可以理解为 Context 的生命周期,该参数的默认值为 5 轮,保存退出。
我们需要创建一个 Intent 来匹配用户的询问。系统 Entity 中没有用户要询问的活动名称实体,因此我们需要首先创建这个 Entity。如下图所示,点击 Entities,并点击Create Entity按钮,添加具体内容,同时开发者也可以为 Entity 中的词条可添加同义词,修改完后保存并退出。如下图所示:
如下图所示,创建一个 Intent,在 Context 中添加weather,在 User says 中添加对应例句,标记具体的 Entity。当前 Intent 中标记了 3 个 Entity,其中 2 个为系统 Entity,分别是黄色的日期实体和粉色的地点实体,还有一个是标记为橙色的自定义 Entity,修改完毕保存并退出。
然后在网页右端,我们输入测试语句 "What is the weather tomorrow in Beijing",可看到结果中有 Contexts 为weather,继续输入 "Can I go climbing",查看结果并点击 "SHOW JSON" 查看详细的输出结果。如下图所示,Contexts 数组中有前一轮对话的结果,其中有时间和地点的信息,可根据该结果生成相对应的回复信息。
至此,一个对话流程的大致雏形已经有了。接下来,让我们一起基于 Google Cloud Function 搭建一个 Web 服务,并跟 Dialogflow 结合,完成一个真正意义上的智能天气对话程序。
2.3 Google Cloud Function
点击左侧菜单中的 Fulfillment,可以看到有两块区域 Webhook 和 Inline Editor。其中 Webhook 需要添加一个 URL,指定处理该该 Intent 结果的 Web 服务。Inline Editor 是 Google Cloud Function 服务的快捷方式,用户无需跳转到该服务页面,而是在 Intent 的编辑页面,通过输入框直接编辑对话函数代码。在用户完成函数撰写并点击DEPLOY按钮部署后,平台会自动生成一个 URL。读者也可以直接进入 Google Cloud Platform(https://console.cloud.google.com) 编辑函数并生成对应的 URL。这里需要说明的是,使用 Google Cloud 的函数服务,首先要配置 Google Cloud 的结算服务,否则函数服务不可用。 为了简单起见,本文使用 Inline Editor 直接编辑函数。首先 Enable 了 Inline Editor,Dialogflow 会默认生成基于 Google Assistant 的代码,从默认代码中我们可以看到该函数处理两个版本的 api,我们之前勾选了 V1 版本的 Agent,因此只需要在processV1Request方法中添加相应的处理逻辑即可。processV1Request中有两个参数request和response,其中request为用户的请求,开发者可以从request中获取 Dialogflow 返回的 NLU 结果,具体的结构可以在 Dialogflow 中输入一个具体的请求后,查看 "SHOW JSON"。response是要返回给用户的对话结果,在不同的场景,需要构建的返回结果是不一样的,本文构建的是一个基于 Google Assistant 的对话服务,因此需要构建一个 Google Assistant 的回复信息,具体参考变量 app,其中actionHandlers为具体的 Action 的请求处理逻辑,开发者请求内容服务并构建返回结果的具体实现在这里。示例代码如下:
function processV1Request (request, response) {
let action = request.body.result.action;
let parameters = request.body.result.parameters;
let inputContexts = request.body.result.contexts;
let requestSource = (request.body.originalRequest) ?
request.body.originalRequest.source : undefined;
const googleAssistantRequest = 'google';
const app = new DialogflowApp({request: request, response: response});
const actionHandlers = {
};
if (!actionHandlers[action]) {
action = 'default';
}
actionHandlers[action]();
}
在actionHandlers中我们需要处理不同的 Action,我们可以从request中获得具体的city和date的值,并调用函数callWeatherApi向天气服务接口发起请求,最终的结果会先判断当前请求的源头,如果来自 Google Assistant 就构建一个GoogleResponse,否则就构建一个普通的Response。示例代码如下:
const actionHandlers = {
'input.welcome': () => {
},
'input.unknown': () => {
},
'weather': () => {
let city = request.body.result.parameters['city'];
let date = request.body.result.parameters['date'];
callWeatherApi(city, date).then((output) => {
if (requestSource === googleAssistantRequest) {
sendGoogleResponse(output);
} else {
sendResponse(output);
}
...
通过以上代码我们得以一窥基于 Google Cloud 的对话函数的具体实现逻辑。此外,为了获得具体的天气,我们需要在callWeatherApi方法中请求真实的天气服务接口,并对返回的结果进行处理,拼接成最终结果返回给用户,callWeatherApi方法的具体实现代码如下所示:
const host = 'api.worldweatheronline.com';
const wwoApiKey = '';
function callWeatherApi (city, date) {
return new Promise((resolve, reject) => {
let path = '/premium/v1/weather.ashx?format=json&num_of_days=1' +
'&q=' + encodeURIComponent(city) + '&key=' + wwoApiKey + '&date=' + date;
console.log('API Request: ' + host + path);
http.get({host: host, path: path}, (res) => {
let body = '';
res.on('data', (d) => { body += d; });
res.on('end', () => {
let response = JSON.parse(body);
console.log('API response: ' + body);
let forecast = response['data']['weather'][0];
let location = response['data']['request'][0];
let conditions = response['data']['current_condition'][0];
let currentConditions = conditions['weatherDesc'][0]['value'];
let output = `Current conditions in the ${location['type']}
${location['query']} are ${currentConditions} with a projected high of
${forecast['maxtempC']}°C or ${forecast['maxtempF']}°F and a low of
${forecast['mintempC']}°C or ${forecast['mintempF']}°F on
${forecast['date']}.`;
console.log(output);
resolve(output);
});
res.on('error', (error) => {
reject(error);
});
});
});
}
代码编辑完成后,点击“DEPLOY”生成函数 URL,然后 Enable Webhook,会在输入链接的地方生成一个 URL,如果读者未看到或者是直接使用 Google Cloud 编辑的函数,需要进入 Google Cloud 查看具体的函数地址,并填写上去。设置完毕后,点击 Intents,然后进入到 "weather" 的 Intent 中,点击 Fulfillment 并勾选 Webhook。
3 技能测试
完成上述配置,一个查询天气的对话技能就建设完成了。接下来,我们一起验证下这个技能。点击如下图所示的右侧输入框,输入 "what is the weather tomorrow in Beijing",回车提交请求,可看到结果如下:
点击上图右上方的Google Assistant链接,可以进入模拟器里面,体验如何使用 Google Assistant 查询天气。如下图所示,输入同样的查询语句 "What is the weather tomorrow in Beijing",会显示两个结果,分别为语音结果和文本结果。
4 结语
本篇主要探讨了如何基于 Dialogflow 建设一个完整的天气对话服务,并在 Google Assistant 模拟器上测试了天气对话技能。希望读者能由此对 Dialogflow 有一个初步的了解,并在未来的实践中有更多的收获。由于篇幅所限,本篇没有覆盖 Dialogflow 中的 Trainning、Integrations、Analytics、Small Talk 等功能,读者可查看 Dialogflow 文档并进一步了解。
参考