lunes, 27 de mayo de 2019

mensaje enriquecidos con dialogflow


Mensajes enriquecidos (Rich Messages) con DialogFlow

Ya hemos visto una introducción a dialogflow y si no lo has hecho te sugiero revisarlo, ya que emplearemos el mismo ejemplo, pero ahora vamos a ver como enriquecer las respuestas que brinda nuestro bot con algo más visual.

Para entrar en contexto acerca de lo que hablamos, recordemos cual fue el resultado obtenido en la introducción a dialogflow.





Tipos de mensajes enriquecidos


Dialogflow cuenta con varios tipos de mensajes enriquecidos, pero no todos son compatibles para lo que podemos llamas respuestas simples, existen mensajes que solo funcionan acciones de google (ACTIONS ON GOOGLE) que permiten una mayor interacción con el usuario, las acciones de google son aquellas que con son empleadas con el asistente de google, aquel que podemos invocar la frase "Ok google" en nuestro teléfono android.


Vamos a ver la lista de mensajes enriquecidos de dialogflow y cuales funcionan solo con las acciones de google.


Tipo de mensaje ACTIONS ON GOOGLE
text No
image No
quickReplies No
card No
payload Si
simpleResponses No
basicCard Si
suggestions Si
linkOutSuggestion Si
listSelect Si
carouselSelect Si


Para más información acerca de los tipos de mensaje enriquecidos de dialogflow; consultar mensajes dialogflow


Para poder ejemplificar los mensajes enriquecidos en dialogflow pensaba mostrar información general de la ciudad que deseáramos, alguna imagen y quizás algunos links a la información, sin embargo me resulto difícil encontrar una API gratis que me pudiera proporcionar dicha información (Un resumen y alguna imagen), y no por que exista una API que brinde dicha información, si no porque su información era limitada y no se encontraban todas las ciudades, soló las más populares o de algunas zonas del planeta, lo que las volvía poco practicas para nuestro objetivo.

pero quiero mencionar que dentro de mi búsqueda encontré la API de Google Places la cual se e hizo muy interesante, si bien dicha API cuenta con una versión de prueba mediante un crédito de $300, pero requiere habilitar la facturación mediante una tarjeta de crédito, si bien ,no se realiza ningún cargo a menos de que se agote el crédito, esto puede resultar un inconveniente para algunos, y por ello mismo la he descartado.



Entonces la dinámica para nuestro agente sera enumerar los diferente tipos de mensajes, y pedir al bot que no muestre un ejemplo del mensaje en base a su numeración






Creando un Intento (Intent)

Vamos a utilizar el mismo agente creado en nuestro ejemplo anterior y vamos a crear un intento y lo llamaremos mensajee_intent y agregaremos las siguientes frases de entrenamiento (Training phrases)

  • Ejemplo numero
  • Dame el ejemplo numero
  • Muéstrame el ejemplo numero




Guardamos cambios, agregaremos un parámetro llamado opcion  de tipo @sys.number que nos indique el número de ejemplo que queremos ver y sera obligatorio (Riquiered)





Posteriormente seleccionamos la palabra numero y le asignamos el parámetro opcion en cada una de las frases de entrenamiento, para que de esta manera podamos acceder a el desde nuestro servicio en node








Como estamos usando el ejemplo anterior donde ya esta configurado el webhoook por lo que omitiré dicha parte, y solo les recordare que de deben habilitar el webhook en intento (Intent),  y pasaremos directamente al código que debe correr en node.






Con el fin de facilitar las cosas vamos a declarar un variables por cada tipo de mensaje con su estructura básica, empezando por la respuesta principal que envía nuestra aplicación a nuestro agente en dialogflow


function respuestaMensaje(){
this.fulfillmentText = '';
this.fulfillmentMessages = [];
}



y una vez agregados los tipos de mensajes obtendríamos el siguiente código


var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
var app = express();
//El cuerpo de solicitudes recibidas sera fomateado con formato JSON
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
function respuestaMensaje(){
this.fulfillmentText = '';
this.fulfillmentMessages = [];
}
function mensajeText(){
this.text = {
text: []
};
}
function Image(imageUri, accessibilityText){
this.image = {
imageUri : imageUri,
accessibilityText : accessibilityText
}
}
function mensajeQuickReplies(){
this.quickReplies = {
title : '',
quickReplies : []
}
}
function mensajeCard(){
this.card = {
title : '',
subtitle : '',
imageUri : '',
buttons : []
}
}
function Buttons(text, posback){
this.text = text;
this.postback = posback;
}
function Button(title, uri){
this.title = title;
this.openUriAction = {
uri: uri
}
}
function mensajeSimpleResponse(){
this.simpleResponses = {
simpleResponses : []
}
}
function SimpleResponses(textToSpeech, ssml, displayText){
this.textToSpeech = textToSpeech;
this.ssml = ssml;
this.displayText = displayText;
}
function mensajeBasicCard(){
this.platform = 'ACTIONS_ON_GOOGLE';
this.basicCard = {
title : '',
subtitle : '',
formattedText : '',
image : {},
buttons : []
};
}
function mensajeSuggestions(){
this.platform = 'ACTIONS_ON_GOOGLE';
this.suggestions = {
suggestions : []
};
}
function suggestion(title){
this.title = title;
}
function mensajelinkOutSuggestion(destinationName, uri){
this.platform = 'ACTIONS_ON_GOOGLE';
this.linkOutSuggestion = {
destinationName : destinationName,
uri : uri
};
}
function mensajelistSelect(title){
this.platform = 'ACTIONS_ON_GOOGLE';
this.listSelect = {
title : title,
items : []
};
}
function item(info, title, description, image){
this.info = info;
this.title = title;
this.description = description;
this.image = image;
}
function SelectItemInfo(key, synonyms){
this.key = key,
this.synonyms = synonyms
}
function mensajecarouselSelect(){
this.platform = 'ACTIONS_ON_GOOGLE';
this.carouselSelect = {
items : []
};
}


El siguiente paso es identificar el ejemplo solicitado en nuestro servicio en node y construir el ejemplo solicitado y responder a nuestro agente. para ello nuestro programa empleara un switch que evalué las opciones y responda según sea el caso, veamos el siguiente código terminado para ver el funcionamiento.


var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
var app = express();
//El cuerpo de solicitudes recibidas sera fomateado con formato JSON
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
function respuestaMensaje(){
this.fulfillmentText = '';
this.fulfillmentMessages = [];
}
function mensajeText(){
this.text = {
text: []
};
}
function Image(imageUri, accessibilityText){
this.image = {
imageUri : imageUri,
accessibilityText : accessibilityText
}
}
function ImageCarousel(imageUri, accessibilityText){
this.imageUri = imageUri;
this.accessibilityText = accessibilityText;
}
function mensajeQuickReplies(){
this.quickReplies = {
title : '',
quickReplies : []
}
}
function mensajeCard(){
this.card = {
title : '',
subtitle : '',
imageUri : '',
buttons : []
}
}
function Buttons(text, posback){
this.text = text;
this.postback = posback;
}
function Button(title, uri){
this.title = title;
this.openUriAction = {
uri: uri
}
}
function mensajeSimpleResponse(){
this.simpleResponses = {
simpleResponses : []
}
}
function SimpleResponses(textToSpeech, ssml, displayText){
this.textToSpeech = textToSpeech;
this.ssml = ssml;
this.displayText = displayText;
}
function mensajeBasicCard(){
this.platform = 'ACTIONS_ON_GOOGLE';
this.basicCard = {
title : '',
subtitle : '',
formattedText : '',
image : {},
buttons : []
};
}
function mensajeSuggestions(){
this.platform = 'ACTIONS_ON_GOOGLE';
this.suggestions = {
suggestions : []
};
}
function suggestion(title){
this.title = title;
}
function mensajelinkOutSuggestion(destinationName, uri){
this.platform = 'ACTIONS_ON_GOOGLE';
this.linkOutSuggestion = {
destinationName : destinationName,
uri : uri
};
}
function mensajelistSelect(title){
this.platform = 'ACTIONS_ON_GOOGLE';
this.listSelect = {
title : title,
items : []
};
}
function item(info, title, description, image){
this.info = info;
this.title = title;
this.description = description;
this.image = image;
}
function SelectItemInfo(key, synonyms){
this.key = key,
this.synonyms = synonyms
}
function mensajecarouselSelect(){
this.platform = 'ACTIONS_ON_GOOGLE';
this.carouselSelect = {
items : []
};
}
//función donde se procesan las solitudes de clima
app.post('/mensajes', function (req, res) {
var opcion = req.body.queryResult.parameters.opcion;
console.log('Opción número: ' + opcion);
var respuesta = new respuestaMensaje();
switch(opcion){
case 1: //Text
var texto = new mensajeText();
texto.text.text.push('Opción uno, mensaje enriquecido Text 1 de 2');
texto.text.text.push('Opción uno, mensaje enriquecido Text 2 de 2');
respuesta.fulfillmentMessages.push(texto);
break;
case 2://Image
var imagen = new Image('https://assistant.google.com/static/images/molecule/Molecule-Formation-stop.png', 'Imagen de prueba');
respuesta.fulfillmentMessages.push(imagen);
break;
case 3://quickReplies
var respuestas = new mensajeQuickReplies();
respuestas.quickReplies.title = 'Opción 3 QuickReplies, selecciona una opción';
respuestas.quickReplies.quickReplies.push('Unos');
respuestas.quickReplies.quickReplies.push('Dos');
respuesta.fulfillmentMessages.push(respuestas);
break;
case 4://card
var tarjeta = new mensajeCard();
tarjeta.card.title = 'Opción 4, Card';
tarjeta.card.subtitle = 'Subtitulo, opción 4';
tarjeta.card.imageUri = 'https://assistant.google.com/static/images/molecule/Molecule-Formation-stop.png';
tarjeta.card.buttons.push(new Buttons('Boton uno', ''));
tarjeta.card.buttons.push(new Buttons('Boton dos', ''));
respuesta.fulfillmentMessages.push(tarjeta);
break;
case 5://simpleResponses
var respuestaSimple = new mensajeSimpleResponse();
respuestaSimple.simpleResponses.simpleResponses.push(new SimpleResponses('Opción 5, simpleResponses', 'Opción 5, simpleResponses', 'display text'));
respuesta.fulfillmentMessages.push(respuestaSimple);
break;
case 6://basicCard
var tarjetaBasica = new mensajeBasicCard();
tarjetaBasica.basicCard.title = 'Opcion 6, basic card';
tarjetaBasica.basicCard.subtitle = 'subtitulo';
tarjetaBasica.basicCard.formattedText = 'Texto con formato';
tarjetaBasica.basicCard.image = new Image('https://assistant.google.com/static/images/molecule/Molecule-Formation-stop.png', '').image;
tarjetaBasica.basicCard.buttons.push(new Button('Boton uno', 'https://www.google.com/'));
tarjetaBasica.basicCard.buttons.push(new Button('Boton dos', 'https://es.wikipedia.org'));
respuesta.fulfillmentMessages.push(tarjetaBasica);
break;
case 7://suggestions
var sugerencias = new mensajeSuggestions();
sugerencias.suggestions.suggestions.push(new suggestion('Opción 7, sugerencia uno'));
sugerencias.suggestions.suggestions.push(new suggestion('Opción 7, sugerencia dos'));
respuesta.fulfillmentMessages.push(sugerencias);
break;
case 8://linkOutSuggestion
var linkSugerencias = new mensajelinkOutSuggestion('Google', 'www.google.com');
respuesta.fulfillmentMessages.push(linkSugerencias);
break;
case 9://listSelect
var lista = new mensajelistSelect('Ejemplo 9, listselect');
lista.listSelect.items.push(new item(new SelectItemInfo('1', ['Uno', 'One']), 'Ejemplo 9',
'listSelect', new Image('https://assistant.google.com/static/images/molecule/Molecule-Formation-stop.png', 'Google'))
);
respuesta.fulfillmentMessages.push(lista);
break;
case 10://carouselSelect
var carousel = new mensajecarouselSelect();
carousel.carouselSelect.items.push(new item(new SelectItemInfo('1', ['Uno', 'One']), 'Ejemplo 9',
'Carousel 1', new ImageCarousel('https://assistant.google.com/static/images/molecule/Molecule-Formation-stop.png', 'Google'))
);
carousel.carouselSelect.items.push(new item(new SelectItemInfo('2', ['Dos', 'Two']), 'Ejemplo 9',
'Carousel 2', new ImageCarousel('https://assistant.google.com/static/images/molecule/Molecule-Formation-stop.png', 'Wikipedia'))
);
respuesta.fulfillmentMessages.push(carousel);
break;
default:
respuesta.fulfillmentText = 'Opción ivalida, ingresa una del 1 al 10';
break;
}
//Variable tipo JSON para guardar la respusta a enviar al agente
res.json(respuesta);
});
//Bucle indefinido escuhando el puerto 3000, a la espera de peticiones
app.listen(3000, function () {
console.log('App escuchando puerto 3000');
});


Para probar el código recuerden correr el comando node app.js y tener en ejecución al aplicación de ngrok con el comando ngrok http 3000 recuerda que de ser necesario debes actualizar la url pública en el webhook del agente para que funcione correctamente.
si tienes duda en este ejemplo puedes ver la compilación  y ejecución del código del ejemplo anterior


Si hemos hecho todo bien hasta este momento, deberíamos obtener resultados como los siguientes.


Ejemplo 1 (Text)





Ejemplo 3 (QuickReplies)






Ejemplo 4(Card)




En el caso de los mensajes enriquecidos que deban ser establecidos como ACTIONS_ON_GOOGLE debemos cambiar como nuestro agente nos muestra la respuesta default como en la siguiente imagen





De tal forma que obtendríamos un ejemplo como el siguiente





Como pueden ver los mensajes enriquecido en dialogflow abren una nueva posibilidad para el desarrollo de nuestros bot(s) ya que nos permiten una mayor interacción con el usuario, ya que podemos mostrarle opciones y esperar una respuesta generando una verdadera interacción. sin duda la profundización de este tema para otro post, donde podamos pedir que elija entre varias opciones y en base a su respuesta poder continuar con el proceso para lo que este diseña el bot.


También quiero terminar recordandoles que la información de diagnostico (DIAGNOSTIC INFO) es muy útil para encontrar problemas cuando empleamos webhook en nuestro agente.


Por ejemplo la siguiente pantalla nos muestra que la ejecución del webhook fue correcta




En el caso de que hubiese un problema con nuestro servicio en node, la comunicación entre nuestro agente y el servicio que estamos corriendo o algún otro error, nos mostraría un error en esta parte.


Ademas de ello podemos evaluar el mensaje que recibe nuestro agente en la pestaña de FULFILLMENT RESPONSE donde podremos ver el mensaje y si esta bien estructurado y la información que contiene





En este ejemplo solo he empleado dos tipos de respuestas, Default y ACTIONS_ON_GOOGLE para indicar la plataforma de destino donde se empleara la respuesta, la cual puede ser una de las siguiente:



  • PLATFORM_UNSPECIFIED
  • FACEBOOK
  • SLACK
  • TELEGRAM
  • KIK
  • SKYPE
  • LINE
  • VIBER
  • ACTIONS_ON_GOOGLE
  • GOOGLE_HANGOUTS

De tal manera que podemos enviar un mensaje enriquecido dependiendo de la plataforma donde este implementado nuestro bot.




Espero les sea de utilidad este post.

No hay comentarios:

Publicar un comentario