De cero a héroe "Acciones en Google": su código

imagen


En la primera parte, descubrimos los principios básicos de diseño y desarrollo de aplicaciones para el Asistente de Google. Ahora es el momento de escribir su propio asistente para que los usuarios finalmente puedan elegir una película para la noche. Shipa_o , raenardev y el diseñador ComradeGuest continúan hablando.


Escribe tu codigo

Intentemos escribir algo más complicado.
Digamos que nuestro agente recomienda películas por género.
Le pedimos que "muestre el horror", y el agente analizará el género, buscará una película en la colección por género y la mostrará en la pantalla.


Para empezar, almacenaremos la colección de películas en una variable:
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("") ]); } 

Ahora la respuesta se ha vuelto más informativa.
Mostramos texto, una tarjeta con información y consejos:


Mostrar información de la película

Una buena característica de Dialogflow es que está listo para usar y está adaptado para diferentes dispositivos.
Si el dispositivo tiene altavoces, todas las frases que enviamos al método de agregar se expresarán, y si no hay pantalla, los objetos Card y Suggestion simplemente no se mostrarán.


Conectamos la base de datos

Vamos a complicar la tarea y agregar la obtención de datos de la base de datos (DB).
La forma más fácil es usar la base de datos en tiempo real de Firebase.
Por ejemplo, utilizaremos la API de la base de datos de administración.


Primero necesita crear una base de datos y llenarla.
Puede hacer esto en el mismo proyecto que se creó para Cloud Functions :


Base de datos en tiempo real basada en firebase

Después de que la base de datos esté llena, conéctela al cumplimiento:
 //   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; }); } 

El acceso a la base de datos requiere subprocesos múltiples. La API de la base de datos de Firebase está diseñada para usar Promise . El método .once('value') nos devuelve una Promesa. Luego obtenemos nuestros datos en el bloque then() y devolvemos Promise con ellos como resultado de la ejecución de la función.
Es importante devolver esta Promesa al método handleRequest() , de lo contrario, el agente saldrá con nuestra devolución de llamada sin esperar una respuesta y procesar el resultado.


Versión de búsqueda de películas por género utilizando la base de datos:
 '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); }); } 

Añadir imprevisibilidad

Nuestra habilidad producirá películas en una secuencia cada vez. Las mismas respuestas molestarán al usuario al principio, y luego dejará de hablar con nuestro robot.


Solucione esto con la biblioteca de mezcla de matriz shuffle-array


Agregue la dependencia al archivo package.json :


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

Agregue la mezcla de la matriz:
 //   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); }); } 

Ahora cada vez se emitirá una nueva película.
De la misma manera, puede agregar la salida de diferentes frases: crear una matriz con frases, mezclarla y tomar la primera de la matriz.


Trabajando con contexto

Le preguntamos al agente:


Mostrar fantasía

El agente nos muestra la película "El señor de los anillos".
Entonces preguntamos:


De que esta hablando?

La gente no dice: "¿Qué es la película" El señor de los anillos? "No es natural. Por lo tanto, necesitamos guardar información sobre la película mostrada. Esto se puede hacer en el contexto de:


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

Entonces podemos leer la información sobre la película así:
 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); }); } 

Del mismo modo, podemos filtrar la lista de películas ya mostradas.


Integración de telegramas


Documentación y enlaces útiles:



Para integrarse con Telegram, no se requiere casi nada, pero hay varias características que deben considerarse.


1) Si está completo para mostrar la Tarjeta o Sugerencia, entonces en Telegram también funcionarán.
Pero hay un error : para las respuestas rápidas, debe especificar un título; de lo contrario, "Elegir un elemento" se mostrará en Telegram.
Hasta ahora no hemos logrado resolver el problema de indicar el título en su totalidad.


2) Si la intención usa chips de sugerencias para el asistente de Google


Ejemplo

entonces la misma funcionalidad para Telegram se puede implementar de dos maneras:


Respuestas rápidas


Configurar respuestas rápidas

Carga personalizada
Aquí puede implementar respuestas rápidas utilizando el teclado principal:


Captura de pantalla del teclado

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

y teclado incorporado:


Captura de pantalla del teclado incorporado

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

El teclado principal enviará un mensaje que se almacenará en el historial, mientras que el teclado incorporado no lo hará.


Es importante recordar que el teclado principal no desaparece con el tiempo. Hay una solicitud especial para esto en la API de Telegram. Por lo tanto, debe asegurarse de que el usuario siempre tenga consejos relevantes.


3) Si necesita una lógica diferente para Telegram y el Asistente de Google, puede hacer esto de la siguiente manera:


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

4) El envío de un archivo de audio se puede implementar de la siguiente manera:


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

5) El contexto en Dialogflow se almacenará durante 20 minutos. Debe tener esto en cuenta al diseñar un bot de Telegram. Si el usuario se distrae durante 20 minutos, no podrá continuar desde el mismo lugar.


Ejemplos


En breve publicaremos el código fuente de la habilidad. Inmediatamente después de su lanzamiento.


PS. Lo que sucedió en el hackathon.


imagen


Fueron 2 días ocupados.
Al principio hubo conferencias educativas, y por la tarde comenzamos a implementar nuestros proyectos.
El día siguiente estaba revisando activamente proyectos y preparando presentaciones.


Los chicos de Google todo este tiempo nos ayudaron y respondieron un montón de preguntas que inevitablemente surgen en el trabajo. Fue una gran oportunidad para aprender mucho y dejar comentarios mientras la plancha todavía está caliente.


¡Gracias a todos los participantes, los organizadores de Google y los expertos que dieron conferencias y nos ayudaron durante todo el hackathon!


Por cierto, tomamos el segundo lugar.


Si tiene preguntas, puede escribir:
shipa_o
raenardev
camarada


Y también hay un chat de Telegram dedicado a la discusión de interfaces de voz, vaya:
https://t.me/conversational_interfaces_ru

Source: https://habr.com/ru/post/es420111/


All Articles