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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
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 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:
Espero les sea de utilidad este post.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)
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
- 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