从零到“ Google行动”英雄:您的代码

图片


第一部分中,我们了解了为Google Assistant设计和开发应用程序的基本原理。 现在是时候编写您自己的助手,以便用户最终可以选择当晚的电影。 Shipa_oraenardevComradeGuest设计师继续交谈。


写你的代码

让我们尝试写一些更复杂的东西。
假设我们的经纪人按类型推荐电影。
我们要求他“显示恐怖”,代理将解析该流派,按流派在收藏集中查找电影并将其显示在屏幕上。


首先,我们将电影收藏存储在一个变量中:
var json = { "filmsList": [ { "id": "1", "title": " :  ", "description": "   ", "genres": ["", "", ""], "imageUrl": "http://t3.gstatic.com/images?q=tbn:ANd9GcQEA5a7K9k9ajHIu4Z5AqZr7Y8P7Fgvd4txmQpDrlQY2047coRk", "trailer": "https://www.youtube.com/watch?v=RNksw9VU2BQ" }, { "id": "2", "title": " :  2 –  ", "description": "   ", "genres": ["", "", "", ""], "imageUrl": "http://t3.gstatic.com/images?q=tbn:ANd9GcTPPAiysdP0Sra8XcIhska4MOq86IaDS_MnEmm6H7vQCaSRwahQ", "trailer": "https://www.youtube.com/watch?v=vX_2QRHEl34" }, { "id": "3", "title": "", "description": "  ", "genres": ["", "", ""], "imageUrl": "https://www.kinopoisk.ru/images/film_big/386.jpg", "trailer": "https://www.youtube.com/watch?v=xIe98nyo3xI" } ] }; 

 exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); let result = request.body.queryResult; let parameters = result.parameters; let outputContexts = result.outputContexts; let intentMap = new Map(); //    agent  parameters,       intentMap.set('search-by-genre', searchByGenre.bind(this, agent, parameters)); agent.handleRequest(intentMap); }); function searchByGenre(agent, parameters) { let filmsList = json.filmsList; //     let filteredFilms = filmsList.filter((film) => { // ,             return film.genres.some((genre) => genre == parameters.genre); }); //       let firstFlim = filteredFilms[0]; //    agent.add(firstFlim.title); //       agent.add(new Card({ title: firstFlim.title, imageUrl: firstFlim.imageUrl, text: firstFlim.description, buttonText: ' ', buttonUrl: firstFlim.trailer })); //      agent.add([ "    ?", new Suggestion("  ?"), new Suggestion(""), new Suggestion("") ]); } 

现在答案变得更加有用。
我们显示文字,带有信息和提示的卡片:


显示电影信息

Dialogflow的一个很好的功能是,它开箱即用,适用于不同的设备。
如果设备有扬声器,那么将发送给我们发送给add方法的所有短语,如果没有屏幕,则不会显示CardSuggestion对象。


我们连接数据库

让我们使任务复杂化,并添加从数据库(DB)获取数据。
最简单的方法是使用Firebase实时数据库。
例如,我们将使用管理数据库API。


首先,您需要创建一个数据库并填充它。
您可以在为Cloud Functions创建的同一项目中执行此操作:


填充的Firebase实时数据库

数据库填满后,将其连接到实现:
 //   firebase-admin const firebaseAdmin = require('firebase-admin'); //  firebaseAdmin firebaseAdmin.initializeApp({ credential: firebaseAdmin.credential.applicationDefault(), databaseURL: 'https://<ID->.firebaseio.com' }); //  ,      function getFilmsList() { return firebaseAdmin .database() .ref() .child('filmsList') .once('value') .then(snapshot => { const filmsList = snapshot.val(); console.log('filmsList: ' + JSON.stringify(filmsList)); return filmsList; }) .catch(error => { console.log('getFilmsList error: ' + error); return error; }); } 

访问数据库需要多线程。 firebase数据库API设计为使用Promise.once('value')方法返回一个Promise。 然后,我们在then()块中获取数据,并随函数执行的结果一起返回Promise。
将这个Promise返回到handleRequest()方法很重要,否则代理将退出我们的回调而无需等待响应和处理结果。


使用数据库按流派按版本搜索电影:
 'use strict'; const functions = require('firebase-functions'); const firebaseAdmin = require('firebase-admin'); const { WebhookClient } = require('dialogflow-fulfillment'); const { Card, Suggestion } = require('dialogflow-fulfillment'); firebaseAdmin.initializeApp({ credential: firebaseAdmin.credential.applicationDefault(), databaseURL: 'https://<ID->.firebaseio.com' }); exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); let result = request.body.queryResult; let parameters = result.parameters; let outputContexts = result.outputContexts; let intentMap = new Map(); intentMap.set('search-by-genre', searchByGenre.bind(this, agent, parameters)); agent.handleRequest(intentMap); }); function getFilmsList() { return firebaseAdmin .database() .ref() .child('filmsList') .once('value') .then(snapshot => { const filmsList = snapshot.val(); console.log('filmsList: ' + JSON.stringify(filmsList)); return filmsList; }) .catch(error => { console.log('getFilmsList error: ' + error); return error; }); } function searchByGenre(agent, parameters) { return getFilmsList() .then(filmsList => { let filteredFilms = filmsList.filter((film) => { return film.genres.some((genre) => genre == parameters.genre); }); let firstFlim = filteredFilms[0]; agent.add(firstFlim.title); agent.add(new Card({ title: firstFlim.title, imageUrl: firstFlim.imageUrl, text: firstFlim.description, buttonText: ' ', buttonUrl: firstFlim.trailer })); agent.add([ "    ?", new Suggestion("  ?"), new Suggestion(""), new Suggestion("") ]); }) .catch(error => { console.log('getFilmsList error' + error); }); } 

增加不可预测性

我们的技能每次都会以一个顺序制作电影。 相同的答案会首先使用户烦恼,然后他将停止与我们的机器人交谈。


使用shuffle-array数组混合库修复此问题


将依赖项添加到package.json文件:


 "dependencies": { // ... "shuffle-array": "^1.0.1" // ... } 

添加数组的混合:
 //   const shuffle = require('shuffle-array'); function searchByGenre(agent, parameters) { return getFilmsList() .then(filmsList => { let filteredFilms = filmsList.filter((film) => { return film.genres.some((genre) => genre == parameters.genre); }); //    shuffle(filteredFilms); let firstFlim = filteredFilms[0]; agent.add(firstFlim.title); agent.add(new Card({ title: firstFlim.title, imageUrl: firstFlim.imageUrl, text: firstFlim.description, buttonText: ' ', buttonUrl: firstFlim.trailer })); agent.add([ "    ?", new Suggestion("  ?"), new Suggestion(""), new Suggestion("") ]); }) .catch(error => { console.log('getFilmsList error' + error); }); } 

现在每次发行新电影。
以相同的方式,您可以添加不同短语的输出:创建一个包含短语的数组,将其混合并获取数组的第一个。


处理上下文

我们要求代理商:


显示幻想

代理商向我们展示了电影《指环王》。
然后我们问:


他在说什么

人们没有说:“电影是什么电影《指环王》”是不自然的。 因此,我们需要保存有关所显示电影的信息。 这可以在以下情况下完成:


 //    ,       agent.setContext({ name: 'current-film', lifespan: 5, parameters: { id: firstFlim.id } }); 

然后我们可以像这样阅读有关电影的信息:
 function genreSearchDescription(agent) { //   current-film const context = agent.getContext('current-film'); console.log('context current-film: ' + JSON.stringify(context)); //  id    const currentFilmId = context.parameters.id; //    return getFilmsList() .then(filmsList => { //    id const currentFilm = filmsList.filter(film => film.id === currentFilmId); agent.add(currentFilm[0].description); agent.add([ ' ?', new Suggestion(''), new Suggestion(' ') ]); }) .catch(error => { console.log('getFilmsList error:' + error); }); } 

同样,我们可以过滤已经显示的电影列表。


电报整合


文档和有用的链接:



要与Telegram集成,几乎不需要任何功能,但是需要考虑几个功能。


1)如果显示了卡片或建议,则在电报中它们也将起作用。
但是有一个错误 :要快速答复,您必须指定标题,否则“ Telegram”中将显示“ Choose a item”。
到目前为止,我们还没有成功解决标题显示为完整的问题。


2)如果意图使用Google助手的建议芯片


例子

那么可以通过两种方式实现Telegram的相同功能:


快速回复


设置快速回复

自定义有效载荷
在这里,您可以使用主键盘实现快速解答:


键盘截图

  { "telegram": { "text": "      :", "reply_markup": { "keyboard": [ [ "", "", "", "", "" ] ], "one_time_keyboard": true, "resize_keyboard": true } } } 

和内置键盘:


内置键盘截图

  { "telegram": { "text": "      :", "reply_markup": { "inline_keyboard": [ [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }] ] } } } 

主键盘将发送一条将存储在历史记录中的消息,而内置键盘则不会。


重要的是要记住,主键盘不会随着时间消失。 Telegram API中对此有特殊要求 。 因此,您需要确保用户始终具有相关的提示。


3)如果您需要Telegram和Google Assistant不同的逻辑,则可以这样做:


 let intentRequest = request.body.originalDetectIntentRequest; if(intentRequest.source == 'google'){ let conv = agent.conv(); conv.ask('    ?'); agent.add(conv); } else { agent.add('    ?'); } 

4)发送音频文件可以实现如下:


 { "telegram": { "text": "https://s0.vocaroo.com/media/download_temp/Vocaroo_s0bXjLT1pSXK.mp3" } } 

5)Dialogflow中的上下文将存储20分钟。 设计Telegram机器人时,您需要考虑这一点。 如果用户分心20分钟,那么他将无法从同一位置继续。


例子


我们将很快发布技能源代码。 发布后立即。


PS。 黑客马拉松发生了什么。


图片


忙了两天。
最初有教育讲座,下午我们开始实施我们的项目。
第二天是积极修改项目并准备演示文稿。


谷歌一直以来都在为我们提供帮助,并回答了工作中不可避免出现的一系列问题。 这是一个很好的机会,可以学到很多东西,并在铁杆仍然很热的时候留下反馈。


感谢所有参与者,Google的组织者和专家们,他们在整个hackathon上做了演讲并为我们提供了帮助!


顺便说一下,我们获得了第二名。


如有疑问,可以写:
希帕
拉纳尔代夫
战友


还有一个专门讨论语音接口的电报聊天,请转到:
https://t.me/conversational_interfaces_ru

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


All Articles