La inteligencia de los primeros reside en la calidad de su programación. Su capacidad de respuesta está limitada a unos casos restringidos. Los chatbots basados en machine Learning, cada vez más numerosos cuentan con funcionalidades basadas en
Inteligencia Artificial, como
Natural Language Processing. Aprenden conforme el usuario interacciona con ellos. La comunicación suele ser más humana.
Los chatbots son un recurso cada vez más usados en la actualidad. ¿Qué promueve actualmente el desarrollo masivo de estas herramientas?
Antes de comenzar con la creación de bots, necesitamos satisfacer los siguientes requisitos en nuestro sistema:
- Tener instalado Node.js
- Instalar un editor de código como Visual Studio Code
- Descargar Bot Framework Emulator, para hacer uso de la misma como aplicación que permite testear los bots tanto en localhost como en remoto. En este breve tutorial nos centraremos en pruebas en local.
Una vez que tengamos todos los requisitos satisfechos, podremos lanzar nuestro primer bot de manera rápida y sencilla.
1. Abre un terminal con privilegios elevados.
2. Si aún no contamos con un directorio para bots en
JavaScript, creamos uno y nos situamos en el mismo.
md myJsBots
cd myJsBots
3. Nos aseguramos de que la versión de
npm está actualizada.
npm install -g npm
4. Después, instalamos
Yeoman y el generador para
JavaScript.
npm install -g yo generator-botbuilder
5. A continuación, podemos usar el generador para crear un bot de eco.
Yeoman solicitará alguna información adicional con la que crear el bot. Si optamos por los valores predeterminados, obtendremos una plantilla básica de Chatbot.
- Escriba un nombre para el bot. (a la elección del usuario)
- Escriba una descripción. (a la elección del usuario)
- Elija el idioma del bot. (JavaScript)
- Elija la plantilla que se va a usar. (Echo)
Si decide crear un bot Basic, necesitará un modelo de lenguaje
LUIS. No es lo que nos ocupa en este simple tutorial, por lo que nos restringiremos al tipo
Echo.
Gracias a la plantilla, el proyecto contiene todo el código necesario para crear el bot en esta guía de inicio rápido. Realmente no necesita escribir ningún código adicional.
El generador
Yeoman crea una aplicación web de tipo
restify. Si vamos a la guía de inicio rápido de restify en su documentación, verá una aplicación similar al archivo
index.js generado. A continuación se describe cómo se suele estructurar una aplicación de este tipo. Contamos con cuatro pilares principales: los archivos
package.json,
.env,
index.js,
bot.js y un fichero
.bot.
package.json
package.json especifica las dependencias del bot y sus versiones asociadas. Esto viene configurado por la plantilla y el sistema.

Figura 2. Ejemplo de fichero package.json
archivo .env
El archivo
.env especifica la información de configuración del bot, como el número de puerto, el identificador de aplicación y la contraseña, entre otras cosas. Si utiliza determinadas tecnologías o si usa este bot en producción, deberá agregar las claves o dirección URL específicas a esta configuración. Para este bot de eco, no obstante, no necesita hacer nada aquí ahora mismo; el identificador de aplicación y la contraseña se pueden dejar sin definir en este momento.
Para usar el archivo de configuración
.env la plantilla necesita incluir un paquete adicional. En primer lugar, obtenga el paquete
dotenv de npm:
npm install dotenv

Figura 3. Ejemplo de fichero .env
index.js
El archivo
index.js configura el bot y el servicio de hospedaje que reenviará las actividades a la lógica del bot.
Por lo general, en la parte superior del archivo index.js encontrarán la serie de módulos o bibliotecas que van a ser necesarios para el funcionamiento del mismo. Estos módulos le darán acceso a un conjunto de funciones que tal vez desee incluir en la aplicación.
La siguiente parte carga información del archivo de
configuración de bot.
En este fichero también se incluyen tanto el adaptador del bot como el
servidor HTTP y el e
stado del bot. En esta parte se configura el servidor y el adaptador que permiten al bot comunicarse con el usuario y enviar respuestas. El servidor escuchará en el puerto especificado en el archivo de configuración BotConfiguration.bot o volverá al
3978 (que resulta el puerto por defecto) para la conexión con el emulador. El adaptador actúa como el director del bot, dirige la comunicación entrante y saliente y la autenticación, entre otras.
También se crea un objeto de estado que usa
MemoryStorage como proveedor de almacenamiento. Este estado se define como
ConversationState, lo que significa simplemente que mantiene el estado de la conversación.
ConversationState almacenará en memoria la información que le interesa, que en este caso es simplemente un contador de turnos.
Dentro de este archivo o haciendo llamada a uno diferente, se define la lógica del bot con toda la estructura de los diálogos.
Archivo .bot
El archivo
.bot contiene información, incluidos el punto de conexión, el identificador de aplicación, la contraseña y las referencias a los servicios que el bot utiliza. Este archivo se crea automáticamente al iniciar la creación de un bot desde una plantilla, pero puede crear el suyo propio con el emulador u otras herramientas. Puede especificar el archivo .bot que se va a utilizar al probar el bot con el emulador.

Figura 4. Ejemplo de fichero .bot
Un archivo .bot no es un requisito para crear bots con el SDK Bot Builder. También puede usar appsettings.json, web.config, env, un almacén de claves o cualquier mecanismo que le convenga para realizar un seguimiento de las referencias de servicio y las claves de las que depende el bot. Sin embargo, para probar el bot con el emulador, necesitará un archivo .bot. La buena noticia es que el emulador puede crear un archivo .bot para las pruebas. Si no haces uso de un asistente como
Yeoman, puedes fabricarte fácilmente tu propio fichero .bot.
Funcionamiento de los bots. Detalles HTTP, avisos en cascada y enriquecimiento de contenido
Detalles HTTP
Las actividades llegan al bot desde Bot Framework Service mediante una solicitud
POST HTTP. El bot responde a la solicitud POST entrante con un código de estado
HTTP 200. Las actividades que se envían desde el bot al canal se envían en una solicitud
POST HTTP independiente a Bot Framework Service. Esta se confirma de vuelta con un código de estado
HTTP 200.
Diálogos y avisos de cascada
Dada la naturaleza de la interacción solicitud-respuesta, para implementar un aviso se requieren al menos dos pasos en un diálogo de cascada: uno para enviar el aviso y otro para capturar y procesar la respuesta. Si tiene un aviso adicional, en ocasiones puede combinar estos pasos mediante una única función para procesar primero la respuesta del usuario y luego iniciar el siguiente aviso.
Un diálogo de cascada se compone de una secuencia de pasos de cascada. Cada paso es un delegado asincrónico que toma un parámetro de contexto de paso de cascada (también llamado
step).
Se puede controlar el valor devuelto desde un diálogo, por ejemplo dentro de un paso de la cascada de un diálogo. Dentro de un paso de la cascada, el diálogo proporciona el valor devuelto en la propiedad result del contexto del paso.
Una cascada avanza paso a paso en la secuencia con que las funciones están definidas en la matriz. La primera función dentro de una cascada puede recibir los argumentos que se pasan al diálogo. Cualquier función dentro de una cascada puede usar la función
next para avanzar al paso siguiente sin pedirle al usuario que intervenga.
Incorporación de datos adjuntos de tarjetas enriquecidas a mensajes
Varios canales, como Skype y Facebook, admiten el envío de tarjetas gráficas enriquecidas a los usuarios con botones interactivos en los cuales el usuario hace clic para iniciar una acción. El SDK proporciona varias clases de generadores de tarjetas y mensajes que se pueden usar para crear y enviar tarjetas. El servicio Bot Framework Connector representará estas tarjetas con un esquema nativo para el canal, compatible con la comunicación multiplataforma. Si el canal no es compatible con las tarjetas, como SMS, Bot Framework hará todo lo posible para representar una experiencia razonable a los usuarios.
Veremos a continuación un ejemplo de gestión de contenidos muy sencillo para aprender a definir este tipo de tarjetas enriquecidas.
Weather Chatbot Afortunadamente, hay miles de módulos que se pueden agregar a nuestro proyecto y que pueden hacer casi cualquier cosa. En este proyecto, usaremos los módulos de
BotBuilder (esta es la biblioteca de Microsoft que le permite crear robots),
Restify (le permite crear una API REST en solo unas pocas líneas de código) y, finalmente,
Request (que le permite crear hacer peticiones HTTP a otros sitios) para poder obtener el tiempo en el momento actual haciendo llamada tanto a las APIs de
OpenWeatherMap como de
AEMET.

Figura 5. Lógica del bot de consulta de tiempo creado para el tutorial Creamos un archivo
app.js en el directorio de su proyecto, este archivo será el punto de entrada de nuestra aplicación. Contendrá toda la lógica de nuestro robot. Abra el archivo que acaba de crear en el modo de edición, ¡es hora de comenzar a programar! Lo primero que debe hacer es cargar los módulos Node.js que necesitaremos
var request = require('request');
var restify = require('restify');
var builder = require('botbuilder');
//------------------------------------------------------
// Configuration
//------------------------------------------------------
// Loading your environment variables is a one-liner:
require('dotenv').config();
Luego, debemos definir algunas variables de configuración. Para ello, leeremos todos los datos que tenemos almacenados en el fichero .env. Lo que nos interesa es OPEN_WEATHER_MAP_APP_ID y AEMET_APP_ID para poder hacer llamadas a las APIs de los servicios de meteorología.
// OpenWeatherMap API Key
var openWeatherMapAppId = process.env.OPEN_WEATHER_MAP_APP_ID;
// AEMET API Key
var aemetApiKey = process.env.AEMET_APP_ID;
const inMemoryStorage = new builder.MemoryBotStorage();
Dijimos que el robot era en realidad una API web simple, por lo que necesitaremos un servidor HTTP: Ha llegado el momento de crear el robot al indicar el conector que tendrá que usar. El conector es lo que permite al robot interactuar con los usuarios. Hay varios tipos de conectores, incluido el ConsoleConnector que le permite interactuar con el robot directamente desde la consola. Aquí, usaremos el ChatConnector, que permite conectar el robot al Conector de Bot de Microsoft (Skype, Facebook, Slack, etc.) y al Emulador de canales de Framework de Bot (herramienta para simular un servicio de mensajería para realizar pruebas).
//-------------------------------------------------------
// Setup
//-------------------------------------------------------
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
Luego tenemos que decirle a nuestro servidor HTTP que es el robot el que responderá las llamadas REST.
// Create connector and bot
const connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
// Listen for messages from users
server.post('/api/messages', connector.listen());
Para seleccionar las acciones que se realizarán en función de los mensajes que se le envíen, nuestro robot tendrá que determinar qué intención está oculta detrás de cada mensaje. Aquí empezamos a describir la estructura conversacional que queremos que siga nuestro bot.
// Declaring the bot and manually setting the memory storage
const bot = new builder.UniversalBot(connector, [
(session, results, next) => {
session.preferredLocale('es', (err) => {
if (err) {
session.error('error');
}
session.send("Bienvenido al weatherBot de LUCA");
next();
});
},
(session, results) => {
builder.Prompts.text(session, 'Por favor, indique para qué ciudad desea conocer el tiempo en este momento');
//next();
},
(session, results, next) => {
openweathermap(results.response, function(success, previsions) {
if (!success) return session.send('Se ha producido un error en la llamada a la API de OpenWeatherMaps. Por favor, inténtelo de nuevo.');
var message = 'Actualmente en ' + previsions.city + ' tenemos ' + previsions.description + ' con:nn' +
'Temperatura : ' + previsions.temperature + '°Cnn' +
'Grado de humedad : ' + previsions.humidity + '%nn' +
'Velocidad del viento : ' + previsions.wind + 'km/h';
session.send(message);
//session.endConversation()
});
next();
},
(session, results) => {
aemetapi(function(success, weatherMapUrl){
if (!success) return session.send('Se ha producido un error en la llamada a la API de AEMET. Por favor, inténtelo de nuevo.');
//console.log('URL en el diálogo -->' + weatherMapUrl)
const msg = new builder.Message(session).addAttachment(createHeroCard(session, weatherMapUrl));
session.send(msg);
session.endConversation()
});
}
]).set('storage', inMemoryStorage);
En el fragmento de código que se está viendo, deberías haber notado que usamos las funciones
openweathermap y
aemetapi , que aún no están declaradas. Estas funciones se usarán para recuperar el clima de una ciudad usando la API de Open Weather Map y el mapa de isobaras más reciente del Atlántico Norte, aquí está su código:
//-------------------------------------------------------
// Call to OpenWeatherMap API
//-------------------------------------------------------
var openweathermap = function (city, callback){
//console.log(openWeatherMapAppId)
var url = 'https://api.openweathermap.org/data/2.5/weather?q=' + city + '&lang=es&units=metric&appid=' + openWeatherMapAppId;
request(url, function(err, response, body){
try{
var result = JSON.parse(body);
if (result.cod != 200) {
callback(false);
} else {
var previsions = {
description : result.weather[0].description,
temperature : Math.round(result.main.temp),
humidity : result.main.humidity,
wind: Math.round(result.wind.speed * 3.6),
city : result.name,
};
callback(true, previsions);
}
} catch(e) {
callback(false);
}
});
}
//-------------------------------------------------------
// Call to AEMET API
//-------------------------------------------------------
var aemetapi = function (callback){
//console.log(aemetApiKey)
var url = 'https://opendata.aemet.es/opendata/api/mapasygraficos/analisis/?api_key=' + aemetApiKey;
request(url, function(err, response, body){
try{
var result = JSON.parse(body);
//console.log(result)
if (result.estado != 200) {
callback(false);
} else {
var weatherMapUrl = result.datos;
//console.log('URL del mapa --> ' + weatherMapUrl)
callback(true, weatherMapUrl);
}
} catch(e) {
callback(false);
}
});
}
La imagen devuelta por la api de AEMET en forma de URL puede embeberse fácilmente ne una HeroCard. El ejemplo de código que se muestra a continuación es muy intuitivo y permite familiarizarse con el manejo de este tipo de recurso fácilmente:
//-------------------------------------------------------
// Definition of a HeroCard with buttons for redirecting to Weather Map from AEMET
//-------------------------------------------------------
function createHeroCard(session, url_map) {
return new builder.HeroCard(session)
.title('Mapas y Gráficos de Europa y Atlántico Norte')
//.subtitle('Using a Chart as Image service...')
//.text(statusLabel)
.buttons([
builder.CardAction.openUrl(session, url_map, 'Presión en superficie en Europa y Atlántico Norte'),
builder.CardAction.openUrl(session, 'https://opendata.aemet.es/dist/index.html?#!/mapas-y-graficos/Mapas_de_an%C3%A1lisis_%C3%9Altima_pasada', 'Detalles sobre la API de AEMET')
]);
}
Con los recursos presentados en este post, os animamos a crear vuestros chatbots. Como haber visto, con un dos llamadas a dos APIs y un diálogo en cascada muy sencillo se ha podido obtener un chatbot capaz de ofrecer una gran cantidad de información personalizada para el usuario.