在Google上执行的操作:在Dialogflow和Cloud Functions for Firebase上为Google助手编写一个简单的应用程序

上个月底,Google助手正式发布了俄语版本,因此该是时候弄清楚如何在标准Google技术堆栈上为Assistant编写自己的应用程序了。 在本文中,我们将考虑在Google的Actions中创建一个动作,分析从Dialogflow中的短语中提取实体和意图的过程,了解如何编写用于提取信息的处理程序以及如何在Cloud Functions for Firebase中使用网络。


1.助手的应用程序体系结构。

Assistant的开发相对较近才开始发展,因此该网络的资料很少,所使用的工具和技术的数量大大增加了进入门槛。 本文虽然没有解决,但至少有助于解决这些问题。 让我们从在标准Google技术堆栈上实现的Assistant的应用程序架构(图1)开始:

  • Google Actions是用于为Google Assistant创建应用程序的平台。
  • Dialogflow -NLU引擎(自然语言理解),负责处理自然语言和设计对话框。
  • 用于Firebase的云功能 (为方便起见,我们将使用Firebase Functions的缩写)-用于处理用户交互的复杂逻辑以及与第三方服务一起使用的云功能。 Firebase函数和Dialogflow通过Webhook进行交互,因此从技术上讲,可以使用任何其他服务器解决方案。 但是,Firebase Functions是一个不错的选择,有时甚至可以替代自己的后端。 它使您可以在Google的基础架构上创建和运行服务,而不必担心服务器的分配,扩展或管理。 一方面,这使您可以专注于服务开发和功能的产品组件,而不会浪费时间在基础架构任务和管理上。 但另一方面,通常来说,代表团意味着对局势的控制减弱了。

本文侧重于开发的技术方面;将不分析使用列出的服务的成本。


2. Google Assistant组件的交互(基于材料: Google Home和Google Assistant Workshop )。

在描述的堆栈中,动作逻辑如下所示(图2):

  • 用户访问Google助手应用程序,并通过特定操作启动对话。
  • 通过Google上的操作,Google助手可以在Dialogflow中以文本格式代理每个用户短语,并另外提供有关用户本人的信息 (经事先请求并得到用户的同意)和当前对话
  • Dialogflow处理接收到的短语,从中提取必要的信息,并基于ML来决定将生成哪个答案。
  • 在某些情况下,Dialogflow可以将响应的形成委派给Firebase Functions上的服务器,而后者又可以使用第三方服务来获取响应所需的信息。
  • 答案形成后,Dialogflow将其返回到Google上的“操作”,然后从那里进入Google Assistant应用程序。

主意


我们的操作将通过词组确定用户想要查看的gif格式,然后他将通过GIPHY API查找它们,并将其以卡片的形式返回给用户。 实施该操作时,我们将分析以下任务的解决方案:

  1. 在Google,Dialogflow和Firebase函数上设置并链接操作。
  2. 从用户短语中提取关键字(Dialogflow)。
  3. 脚本对话框(Dialogflow)。
  4. 使用对话上下文(Dialogflow)。
  5. 创建并连接一个Webhook以生成对用户短语的响应(Dialogflow,Firebase Function)。
  6. 在界面中显示卡轮播(Firebase功能)。
  7. 从第三方服务下载信息(Firebase功能)。

初始设定



3.创建一个Dialogflow代理。

首先,我们需要一个Google帐户。 让我们首先在Dialogflow中创建一个项目,为此,在控制台中,单击Create Agent按钮并填写必填字段(图3):

  • 默认语言是“俄语-ru”。
  • 时区:“(GMT + 3:00)欧洲/莫斯科”。
  • Google云端专案:将自动为您的Dialogflow代理创建新的GCP,或者您可以选择现有的GCP专案之一(如果有)。

然后单击右上角的“创建”按钮,并等待控制台配置新项目。


4.标准意图。

默认情况下,在创建Dialogflow代理时,将创建两个意图(图4):

  • “默认的欢迎意图”-负责问候用户;
  • “默认后备意图”-处理Dialogflow无法归因于任何其他意图的未知短语。

此处此处此处的文章中已经详细介绍了在Dialogflow中创建对话框,因此,我将不再关注其操作原理。


5.回答“默认欢迎意图”。

我们会在“默认欢迎意图”中添加一些欢迎解答,以帮助用户了解操作的目的以及他可以执行的功能。 在“响应”部分中,选择“ Google助手”选项卡,在“建议船”中,我们将编写短语示例来告诉用户如何与操作进行交流(图5)。

可以在手机和官方模拟器中的Google Assistant中调试操作。 要打开模拟器,您需要转到“集成”部分,在“ Google助手”卡中,单击“集成设置”按钮,然后单击“管理助手应用程序”。 在电话和仿真器中,都可以使用代码短语“ Ok Google,我想与我的测试应用程序对话”来触发操作。

基本方案:GIF搜索


创建一个新的搜索意图,该意图将从用户的短语中提取关键字,并将其通过webhook服务器发送到Firebase Functions。 反过来,服务器使用GIPHY API将找到相应的gif,并将结果以卡的形式返回给用户。


6.添加训练短语。

首先,我们将在“训练短语”部分中添加用于训练的典型短语(图6):

  • “我想看跳舞的长颈鹿。”
  • “找到animashki。”
  • “出示印章。”
  • 给我看看GIF。
  • “给我找动画的大象。”
  • “显示熊猫Gif。”
  • “带有浣熊条纹的礼物。”
  • “你有海豹。”
  • “找到有趣的瀑布。”


7.从文本中提取参数。

对于添加的短语,请注意搜索参数,Dialogflow应该从文本中选择该参数。 在这种情况下,最合适的参数类型是@sys.any ,因为几乎任何语言构造都可以用作搜索查询参数。 我们调用此参数query并将其标记为必需(图7)。


8.主要问题清单。

在“提示”小节中,我们将写下澄清问题,以确保Dialogflow将询问其是否无法从短语中提取关键字(图8)。

接下来,转到页面底部的“实现”部分(不要与左侧菜单中的同名部分混淆)。 单击“启用完整文件”按钮,然后启用“为此意图启用webhook调用”设置。 这将使Dialogflow在达到目的时委派Firebase Functions响应的形成。

现在转到左侧菜单中的“实现”标签,然后打开“内联编辑器”,我们将在其中编写新创建的“搜索意图”的逻辑。 要通过关键字搜索GIF,我们将使用请求https://api.giphy.com/v1/gifs/search ,该请求将根据规范以JSON格式返回找到的对象的列表。 从GIPHY接收到的回复将以浏览轮播的形式显示-带有图像的卡片轮播,单击后将打开网页。 在我们的情况下,当您单击卡片时,用户将转到GIPHY服务页面,其中包含该动画和类似动画的列表。

下面介绍实现上述功能的代码。

 'use strict'; const GIPHY_API_KEY = 'API_KEY'; const SEARCH_RESULTS = [ '-,    .', ',   .', ',   !' ]; // Import the Dialogflow module from the Actions on Google client library. const { dialogflow, BrowseCarouselItem, BrowseCarousel, Suggestions, Image } = require('actions-on-google'); // Import the firebase-functions package for deployment. const functions = require('firebase-functions'); // Import the request-promise package for network requests. const request = require('request-promise'); // Instantiate the Dialogflow client. const app = dialogflow({ debug: true }); function getCarouselItems(data) { var carouselItems = []; data.slice(0, 10).forEach(function (gif) { carouselItems.push(new BrowseCarouselItem({ title: gif.title || gif.id, url: gif.url, image: new Image({ url: gif.images.downsized_medium.url, alt: gif.title || gif.id }), })); }); return carouselItems; } function search(conv, query) { // Send the GET request to GIPHY API. return request({ method: 'GET', uri: 'https://api.giphy.com/v1/gifs/search', qs: { 'api_key': GIPHY_API_KEY, 'q': query, 'limit': 10, 'offset': 0, 'lang': 'ru' }, json: true, resolveWithFullResponse: true, }).then(function (responce) { // Handle the API call success. console.log(responce.statusCode + ': ' + responce.statusMessage); console.log(JSON.stringify(responce.body)); // Obtain carousel items from the API call response. var carouselItems = getCarouselItems(responce.body.data); // Validate items count. if (carouselItems.length <= 10 && carouselItems.length >= 2) { conv.data.query = query; conv.data.searchCount = conv.data.searchCount || 0; conv.ask(SEARCH_RESULTS[conv.data.searchCount % SEARCH_RESULTS.length]); conv.data.searchCount++; conv.ask(new BrowseCarousel({ items: carouselItems })); } else { // Show alternative response. conv.ask('      ,   - ?)'); } }).catch(function (error) { // Handle the API call failure. console.log(error); conv.ask(',     .'); }); } // Handle the Dialogflow intent named 'Search Intent'. // The intent collects a parameter named 'query'. app.intent('Search Intent', (conv, { query }) => { return search(conv, query); }); // Set the DialogflowApp object to handle the HTTPS POST request. exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app); 

依存关系
 { "name": "dialogflowFirebaseFulfillment", "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase", "version": "0.0.1", "private": true, "license": "Apache Version 2.0", "author": "Google Inc.", "engines": { "node": "~6.0" }, "scripts": { "start": "firebase serve --only functions:dialogflowFirebaseFulfillment", "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment" }, "dependencies": { "actions-on-google": "2.0.0-alpha.4", "firebase-admin": "^4.2.1", "firebase-functions": "^0.5.7", "dialogflow": "^0.1.0", "dialogflow-fulfillment": "0.3.0-beta.3", "request": "^2.81.0", "request-promise": "^4.2.1" } } 

由于用户可以多次访问同一意图,因此建议对其返回各种答案。 为此,我们使用了Conversation.data JSON对象,该对象在再次访问该意图以及引用其他对话脚本时都保留其值。


9.初始化对话(在左侧),优化搜索参数并进一步显示结果(在中间),显示新查询的搜索结果(在右边)

注意:要通过Firebase功能使用第三方服务的API,您需要启用计费功能,否则,当尝试使用网络时,将发生错误:
“未配置结算帐户。 无法访问外部网络,并且配额受到严格限制。 配置帐单帐户以消除这些限制。”
为此,请单击左侧菜单中的“付费帐户”,然后在建议的关税计划中选择Flame(每月25美元)或Blaze(使用时付款)。 我选择了最后一个选项,因为作为测试应用程序开发的一部分,对我来说,这似乎更有利可图。

高级方案:分页


在大多数情况下,对于搜索查询,GIPHY会发现远远超过十个的GIF,因此允许用户查看整个搜索结果(即搜索结果)是正确的。 添加分页。

在Dialogflow控制台中,将鼠标悬停在“搜索意图”单元上。 右侧将出现几个按钮,单击“添加后续意图”。 这将使我们能够根据“搜索意图”创建一个对话分支。 在下拉列表的元素中,我们选择“更多”-用于启动其他信息显示的标准点火器。


10.目的的上下文是“搜索目的-更多”。

让我们转到新创建的意图,然后对“上下文”部分进行更改。 由于用户可以要求连续显示多次GIF,因此应该可以递归调用此意图。 为此,在传出上下文中,有必要注册传入中指示的同一行(图10)。 在“完整信息”部分,您还应该启用“为此意图启用webhook调用”设置。

现在,从侧面菜单返回“填充”,在此处初始化“搜索意图-更多”的处理程序。 我们还将offset参数添加到search功能中,该功能将在GIPHY API中用于分页。

 const SEARCH_RESULTS_MORE = [ '   !', ',    .', ',   .  ,    .' ]; function search(conv, query, offset) { // Send the GET request to GIPHY API. return request({ method: 'GET', uri: 'https://api.giphy.com/v1/gifs/search', qs: { 'api_key': GIPHY_API_KEY, 'q': query, 'limit': 10, 'offset': offset, 'lang': 'ru' }, json: true, resolveWithFullResponse: true, }).then(function (responce) { // Handle the API call success. console.log(responce.statusCode + ': ' + responce.statusMessage); console.log(JSON.stringify(responce.body)); // Obtain carousel items from the API call response. var carouselItems = getCarouselItems(responce.body.data); // Validate items count. if (carouselItems.length <= 10 && carouselItems.length >= 2) { conv.data.query = query; conv.data.offset = responce.body.pagination.count + responce.body.pagination.offset; conv.data.paginationCount = conv.data.paginationCount || 0; conv.data.searchCount = conv.data.searchCount || 0; // Show successful response. if (offset == 0) { conv.ask(SEARCH_RESULTS[conv.data.searchCount % SEARCH_RESULTS.length]); conv.data.searchCount++; } else { conv.ask(SEARCH_RESULTS_MORE[conv.data.paginationCount % SEARCH_RESULTS_MORE.length]); conv.data.paginationCount++; } conv.ask(new BrowseCarousel({ items: carouselItems })); conv.ask(new Suggestions('')); } else { // Show alternative response. conv.ask('      ,   - ?)'); } }).catch(function (error) { // Handle the API call failure. console.log(error); conv.ask(',     .'); }); } // Handle the Dialogflow intent named 'Search Intent - more'. app.intent('Search Intent - more', (conv) => { // Load more gifs from the privious search query return search(conv, conv.data.query, conv.data.offset); }); 


11.搜索gif时的分页。

结果


该操作的视频如下所示。



Github上提供了项目代码和助手转储。

有关安装项目和导入转储的说明
  1. 转到Dialogflow控制台并创建一个新代理,或选择一个现有代理。
  2. 单击设置图标,转到“导出和导入”部分,然后单击“从ZIP还原”按钮。 从存储库的根目录中选择ZIP文件。
  3. 从左侧导航菜单中选择“实现”。
  4. 打开“内联编辑器”设置。
  5. 将文件内容从functions目录复制到“实现”中的相应选项卡上。
  6. index.js标签中输入GIPHY API访问密钥。
  7. 转到Firebase控制台,然后将计划更改为Flame或Blaze。 免费资费计划不支持通过网络使用第三方服务。

Source: https://habr.com/ru/post/zh-CN419261/


All Articles