
Google hackathon,以及为助手开发应用程序所需的全部。
Google组织了一次专门针对Google Actions技术的黑客马拉松。 这是一个获得经验并思考如何开始为我们的应用程序制作对话用户界面(CUI)的好机会。 因此,我们组成了一个由两个Android开发人员组成的团队: shipa_o , raenardev和设计师comradeguest,并参加了会议。
什么是Google上的操作?
Google行动(AoG)是一种将行动添加到助手的方法。
您可以使用4种工具进行此操作:
在黑客马拉松比赛中,我们取得了一项技能-一种扩展助手功能的应用程序,因此我们就此止步。
上诉后,“确定Google。 我想与${_}
进行交谈,助手将打开与用户进行对话的技能:

如何写技能?
您将需要两个技能:
-了解会话用户界面(CUI)以及设计它们的能力;
-能够使用自然语言处理(NLP),例如Dialogflow。
阶段1:设计
为了使您的技能拥有蛋白质对话者,现在最好考虑一下未来。 需求将是那些考虑了使用环境的需求。 当可以大声说话并与语音,带有语音的设备进行交互(而不是用手,眼睛和身体的其他部分)时,将使用对话界面。
语音接口是串行的。 如果可以在图形上显示下订单的全部形式,并且该人本人将选择首先要看的内容,然后再选择什么,那么您只能用语音一个接一个地问问题。 要提出一种流行且方便的应用程序,请找到用户需求与使用语音界面的能力(或无法使用其他界面)之间的交集。
首先想到的是盲人的语音助手,它有助于解决日常问题。 例如,在商店下订单,打车,打电话给亲戚。 第二本是一本家庭主妇用面粉说话的食谱书。 第三,需要解释一些游戏。
我们决定从一个简单的机器人开始,并开发了一个机器人,向人们推荐优秀的电影。 我们克服了语音合成器的不完善之处:我们的助手甚至没有假装自己是一个男人,并且以各种方式强调了他生动的电子个性。
Google就如何开发交互式界面撰写了出色的指南 。 我们将讨论如何设计我们的第一胎。
1.调用
首先,您需要打电话给助手。 该调用可以是显式(显式调用)和间接(隐式调用)。 人们已经知道应用程序时将使用显式处理。 间接地,Google Assistant可以在特定情况下推荐合适的应用程序。 正确选择的间接吸引选项-例如上下文广告中的正确关键字,只会更具“人性化”。
间接调用不要太笼统,这一点很重要。 就像内容相关广告中的常规关键字一样,它们只会阻止您找到合适的应用程序,并会在发行助手时降低应用程序的评级。
呼叫可能包含指向各个语音助手功能的深层链接。 例如,通常我们的电影机器人开始与他提供人选类型的事实进行交流。 但是,如果间接呼吁他“我想看一部有趣的喜剧”,则可以通过提供上述类型的有保证的好电影来开始对话。
2.第一次问候
第一个问候是应用程序在呼叫后立即告诉此人的内容。
首先,您需要让用户知道助手已经在这里:
嗨,一种生命的蛋白质。 我是红色热情的电影机器人。 我存在的目的是为优秀影片上的生物影片提供建议。
然后-建议下一步。 我们的机器人正在按类型寻找电影,因此我们建议人们进一步提出以下要求:
您想看到什么:喜剧,动作或恐怖?
新手和有经验的用户可以通过多种方式打招呼。 如果某人是第一次与您的助手交谈,您可以谈论一下自己。 如果不是第一个-长时间的问候会惹恼他。 因此,您可以立即开始做生意:
3.人类对话
教您的助手理解自然的言语并保持对话的进行。 最简单的方法是甚至在开发开始之前就与目标受众的人进行沟通。 此外,由于口头书面演讲比口头演讲更为稀少,因此口头而不是书面形式更可取。 扮演机器人的角色,并让对话者想象他正在使用您将来的应用程序。 在记录器上记录所有对话框,然后解密。 这将帮助您设计一个典型的对话图并查找分支出现的位置。
第二阶段:发展
有几种方法可以帮助您制定助手的行动:
- 使用Dialogflow。
- 使用Google SDK上的操作。
- 文本可以独立处理-例如,如果您有自己的自然语言处理解决方案(NLP-自然语言处理)。
以下是助手与您的技能的互动。
该对话框如下所示:
助手将语音转换为文本并将其发送给您的操作。
以上述方式之一处理文本。 在此图中,通过Dialogflow。
Dialogflow定义意图(用户的特定意图)并接收
来自它的实体(参数)。
(可选)Dialogflow可以调用相应的Webhook,在后端处理数据并获得响应。
Dialogflow就是答案。
助手说出答案,打开麦克风,然后听用户说。

助手动作图
对话流程
我们不会详细介绍Dialogflow的基础知识-Google发布了不错的教程视频。
- 意图 -关于识别意图,Dialogflow如何准确理解用户的要求或他想执行的操作。
- 实体 -关于短语内参数的识别。 例如,在推荐电影的情况下,这是特定类型。
- 对话控制 -关于上下文机制(在下文中进行了解)和实现:关于如何通过访问后端来处理用户的请求本身,以及如何返回比文本响应更有趣的内容。
我们假设您已经看过视频并找到了Dialogflow控制台。 让我们看一下在实现过程中每个部分中出现的问题,并且可以注意到有趣的地方。
还要牢记在进行实施时建立良好对话的规则-这将影响一堆意图,实体集及其在答案中的使用,上下文的使用以及其他所有内容。
意向
有一些建议 -为新用户做更详细的问候,而让其他用户更简洁。 如何实现呢?
在Dialogflow控制台中,无法确定此逻辑。 可以在实现过程中完成以迎接您的意图。 换句话说,您将需要用手完成此操作。
这也适用于错误处理。 例如,第一次您可以简单地再次询问,而第二次您可以告诉您您期望用户做出什么样的响应。
您不会通过回答来做到这一点-将选择一个随机答案。 您可以通过实现来实现,也可以通过将它与上下文相关联来进行一些棘手的操作(更多内容请参见下文)。
实体
“允许自动扩展”和sys.Any
如果该短语在结构上相似,则当“允许自动扩展”作为公认的实体打开时,可能会进入某些无法盈利的过程。
例如,您的应用程序可以识别短语“从<genre>向我建议”,以获取电影提示。 如果您说“建议我从食物中提些建议”,那么作为参数“体裁”,您将无法获得预期的结果。 如果您在后端上不断地动态更新这组类型,并且在那里处理了用户的响应,但是您没有时间更新实体,则这是有意义的。
另一个解决方案是使用sys.Any。 在这种情况下,他将传达预期设计之后的所有内容,这可能是整个建议。 这比自动扩展提供了更多的机会,但是也有更多的缺点。
单数和复数,案例,性别
让我们举一个简单的例子。 我们将讨论您最喜欢的水果,应用程序将回答它们有多酷。 在下面的示例中,P是用户,A是我们的行动。
警:菠萝是最美味的。
答:是的,菠萝是最酷的水果!
在这个例子中,我们有实体:菠萝
但是,如果用户说“菠萝”怎么办? 我们可以添加一个同义词。
病人:我喜欢菠萝。
答:是的,菠萝是最酷的水果!
默认值为参考表格-即 它是原始形式,而不是其同义词。 这就是为什么应用程序不显示“是的,菠萝是最酷的水果!”的原因。
在编写答案时也值得考虑。 如果我们回答“是的,$水果是最酷的!”,那么我们将得到以下信息:
病人:我喜欢菠萝。
答:是的,菠萝是最酷的!
并且不要忘记案例:
P:我要戏剧
-答:这是一个很酷的戏剧...
因此,您应该以一致的形式写出实体的初始值和答案。 例如,我们可以对复数形式的水果进行任何原始实体形式处理,对任何形式的同义词进行处理。
在主格的情况下,我们将以俄语进行体裁,但数量不限(戏剧,恐怖片)。
当我们找到答案时,我们不会写“是的,$水果是最酷的水果!” 和“是的,$水果是最酷的!” -水果总是复数。
以俄语为例,值得进一步思考一下如何写一个一致的答案-我们不仅有案例(戏剧/戏剧),而且还有性别(戏剧/西方)。
警:我要西餐
A:这是一个很酷的西方...
警:我要西餐
A:西方? 好的,这是一部很酷的电影...
但是如何准确返回用户所说的实体形式呢?
在类型方面,“科幻小说”的同义词可以是“外星人”。 然后,如果用户说“外星人”,则“科幻小说”将作为参数返回。
如果我们要以用户所说的形式获取实体,那么值得选择$ entity.original的值。

但是然后可能会有数字不一致(尤其是案件不一致)的问题。 真的有必要吗? 如果是这样,请为单数,复数和大小写创建一个实体。 响应还必须与其使用的实体形式一致。
语境
也许有了这个最大的问题。
输入上下文
这是特定意图绑定到的上下文。 多个意图可以响应相同的短语,而具有活动输入上下文的意图最有可能起作用。
因此,例如,您可以为特定问题附加是/否答案,这可以使用Dialogflow中的后续意图来完成
输出上下文
这是在触发意图时激活的上下文。 这是在Dialogflow控制台中激活上下文的方式(也可以在实现中完成)。 我们指出对话将进行的轮数,在重置计数器后或20分钟后将其停用。 这意味着该上下文中的数据将不再可用,并且为其输入的意图将不起作用。
另一个窍门与此相同:您可以使用一种意图激活上下文,而使用另一种意图手动将其停用,只需将其作为第二个意图的输出上下文,答案数为0。
如果您不想编写代码来实现,则可以通过这种方式来实现有趣的逻辑,例如,使用上下文作为计数器,并在助手不理解用户时实现错误处理。
对话流程提示
无需使用助手预览来重新启动页面-对dialogflow代理进行更改后,可以等待其培训完成,然后立即在模拟器中重复无法识别的短语。 Dialogflow可以看作是助手所引用的后端。
使用预先构建的代理-您可以在其中看到如何实现典型方案。
闲聊部分要小心。 在对话结束时使用麦克风不会关闭麦克风,因此此类答案通常不包含号召性用语。 您不会引导用户进行下一轮对话,并且尚不清楚他应该进一步说什么。 因此,您很有可能无法通过审核。 如果可以将它们输入对话框中,则最好为此单独制定意图。
请勿同时编辑同一意图。 现在,不支持多个人同时工作-尚不清楚谁的更改将被覆盖。
如果有必要并行进行意图并行处理,则可以在单独的项目中进行,然后只需选择必要的项目并进行传输即可。 还可以导入和导出json / xml中的实体,以及导入/导出意图。
立即值得考虑为特定语言编写动作。 用俄语写答案还有其他细微差别。 因此,对操作进行本地化似乎比使用移动应用程序GUI更具挑战性。
考虑语音接口的设计规则-它们不仅影响副本集,还影响整个结构。 您正在建立对话,因此每个答案都应发出号召性用语,以便用户理解该说些什么。
在一切准备就绪并开始测试之后,不要害怕放弃对话或问题表格的各个分支。 也许在测试阶段,您将了解如何连接意图以及为易于使用而缺少的内容。
服务器连接
要连接服务器,您需要使用实现。 有两种选择:
- Webhook客户端 。 支持多种语言。
- 用于Firebase的Cloud Functions(node.js)的嵌入式编辑器 。
让我们考虑最简单的一个-内联编辑器。
我们不假装自己是node.js的专家;欢迎修复注释中的错误。
重要的是要注意Dialogflow API的版本。
最新版本v2。 版本v1编写的所有内容均无法使用。
在此处阅读有关迁移的更多信息。
有用的链接:
解析标准模板
当您打开Fulfillment部分时,以下代码将显示在index.js文件/标签中: 'use strict'; const functions = require('firebase-functions'); const {WebhookClient} = require('dialogflow-fulfillment'); const {Card, Suggestion} = require('dialogflow-fulfillment'); process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers)); console.log('Dialogflow Request body: ' + JSON.stringify(request.body)); function welcome(agent) { agent.add(`Welcome to my agent!`); } function fallback(agent) { agent.add(`I didn't understand`); agent.add(`I'm sorry, can you try again?`); } // // Uncomment and edit to make your own intent handler // // uncomment `intentMap.set('your intent name here', yourFunctionHandler);` // // below to get this function to be run when a Dialogflow intent is matched // function yourFunctionHandler(agent) { // agent.add(`This message is from Dialogflow's Cloud Functions for Firebase editor!`); // agent.add(new Card({ // title: `Title: this is a card title`, // imageUrl: 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png', // text: `This is the body text of a card. You can even use line\n breaks and emoji! `, // buttonText: 'This is a button', // buttonUrl: 'https://assistant.google.com/' // }) // ); // agent.add(new Suggestion(`Quick Reply`)); // agent.add(new Suggestion(`Suggestion`)); // agent.setContext({ name: 'weather', lifespan: 2, parameters: { city: 'Rome' }}); // } // // Uncomment and edit to make your own Google Assistant intent handler // // uncomment `intentMap.set('your intent name here', googleAssistantHandler);` // // below to get this function to be run when a Dialogflow intent is matched // function googleAssistantHandler(agent) { // let conv = agent.conv(); // Get Actions on Google library conv instance // conv.ask('Hello from the Actions on Google client library!') // Use Actions on Google library // agent.add(conv); // Add Actions on Google library responses to your agent's response // } // // See https://github.com/dialogflow/dialogflow-fulfillment-nodejs/tree/master/samples/actions-on-google // // for a complete Dialogflow fulfillment library Actions on Google client library v2 integration sample // Run the proper function handler based on the matched Dialogflow intent name let intentMap = new Map(); intentMap.set('Default Welcome Intent', welcome); intentMap.set('Default Fallback Intent', fallback); // intentMap.set('your intent name here', yourFunctionHandler); // intentMap.set('your intent name here', googleAssistantHandler); agent.handleRequest(intentMap); });
文件/选项卡“ package.json”中的此类依赖项: { "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" } }
首先,将alpha和beta依赖项更新为最新的稳定依赖项。
这是目前的最新版本。 { "dependencies": { "actions-on-google": "^2.2.0", "firebase-admin": "^5.2.1", "firebase-functions": "^0.6.2", "dialogflow": "^0.6.0", "dialogflow-fulfillment": "^0.5.0" } }
现在,让我们仔细看一下代码。
上面完成了导入依赖项 // Cloud Functions Firebase library const functions = require('firebase-functions'); // const {WebhookClient} = require('dialogflow-fulfillment'); // const {Card, Suggestion} = require('dialogflow-fulfillment');
实现的全部要点是覆盖回调-一个`dialogflowFirebaseFulfillment` exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers)); console.log('Dialogflow Request body: ' + JSON.stringify(request.body)); // . const agent = new WebhookClient({ request, response }); // let result = request.body.queryResult; // action entities https://dialogflow.com/docs/actions-and-parameters let action = result.action; let parameters = result.parameters; // https://dialogflow.com/docs/contexts let outputContexts = result.outputContexts; // let intentRequest = request.body.originalDetectIntentRequest; });
将针对您激活其功能的那些意图调用此回调。
现在重新定义意图的答案 exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); function welcome(agent) { // agent.add(`Welcome to my agent!`); } function fallback(agent) { agent.add(`I didn't understand`); agent.add(`I'm sorry, can you try again?`); } // , : // key - intent-. // value - , . let intentMap = new Map(); intentMap.set('Default Welcome Intent', welcome); intentMap.set('Default Fallback Intent', fallback); agent.handleRequest(intentMap); });
同时,该代码完全替换了“响应”部分中的意图响应。
仅当回调失败时才调用响应,因此您可以在那里进行错误处理。
我们从回调中删除了意图处理功能。
闭包中的欢迎和回退功能。
要从回调中删除它们,您将必须通过`bind`添加函数和参数上下文的传递。 exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); let intentMap = new Map(); // set Map. intentMap .set('Default Welcome Intent', welcome.bind(this, agent)) .set('Default Fallback Intent', fallback.bind(this, agent)); agent.handleRequest(intentMap); }); function welcome(agent) { agent.add(`Welcome to my agent!`); } function fallback(agent) { // 2 add agent.add([ `I didn't understand`, `I'm sorry, can you try again?` ]); }
, , Google Assistant. , .