Primer chatbot con Dialogflow y Nodejs

Primer chatbot con Dialogflow y Nodejs

Después de estar unos días trasteando con Dialogflow y con Nodejs, he creado un pequeño chatbot que te dice la temperatura de cualquier lugar del mundo.

El ejemplo es muy sencillo pero muestra la gran potencia que tiene Dialogflow.

Comenzaremos creando un Agente, que para este ejemplo he usado el nombre de Curso01.

En este ejemplo usaremos para el nombre del Agente Curso01, pero podemos usar cualquier nombre.

Para el idioma y la zona horaria, usaremos la que nos corresponda. Finalmente para el proyecto Google, podemos crear uno nuevo (recomendado) o usar un proyecto existente.

A continuación crearemos un Intent, que llamaremos para esta prueba El tiempo

Ahora toca el momento de empezar añadir las frases para entrenar a nuestro agente.

En mi caso estoy haciendo una prueba de una aplicación para saber el tiempo de cualquier punto geográfico, así que usare diferentes formas de preguntar el tiempo.

Como en este ejemplo solo quiero que me diga la temperatura del día actual, únicamente usare como parámetro el nombre de la ubicación.

 

Dentro de acciones y parámetros nos vamos a centrar en 3 partes. La primera es parameter name, ese valor es el que nos interesa para este ejemplo ya que sera el que se

enviara en el JSON a nuestro fichero en el servidor. Entity es una herramienta potente que se utiliza para extraer los valores de los parámetros de entrada.

En la frase «¿Que temperatura hace en Madrid?, Madrid es nuestro parámetro y en este ejemplo uso del tipo any aunque lo correcto debería ser city, geo-city, etc..

Para finalizar el Value, aunque en este ejemplo no lo vamos a usar, este valor lo usamos dentro de response para personalizar la respuesta.

A continuación un ejemplo de como sería el flujo:

  • Pregunta: ¿Que tiempo hace en Londres?
  • Respuesta: En $any (Londres) hace una temperatura de…. (Aquí añadimos las diferentes respuesta que necesitemos para conseguir una apariencia lo mas humana posible)

Como decía antes, en este ejemplo no vamos hacer uso de responses desde Dialogflow, usaremos nuestro script externo (webhook).

Para poder gestionar desde fuera de dialogflow una respuesta es necesario añadir un webhook y activar en la parte de fulfillment el uso de webhook para este intent.

Integrando nuestro bot

Yo voy a usar Slack por comodidad

 

Accedemos a la parte de integración

Configuramos Slack para hacer la integración del bot

Ahora un video de como funciona el bot en Slack:

En el video uso varias localizaciones validas e invalidas para comprobar el comportamiento del bot.

Usando nuestro servicio externo

Ahora vamos a la parte de Fulfillment

 

En el campo URL añadiremos la url donde esta nuestro servicio que usara el JSON que envía dialogflow y que para este ejemplo sera el único campo que usemos.

 

Ahora el flujo de dialogflow:

  1. Añadimos una frase de prueba para comprobar que respuesta nos da dialogflow
  2. Nos muestra la frase que hemos usado
  3. Aparece la respuesta (en este caso de un servicio externo). Si usamos el propio dialogflow nos respondería con el texto ubicado en response
  4. El intent que esta usando
  5. El parámetro y el valor del parámetro (definido en la parte de parámetros y acciones)
  6. Mostrar el json que genera dialogflow
  7. Contenido del json

Hasta este punto todo lo que hemos hecho ha sido con dialogflow, ahora toca crear el código que gestionara esos datos y que en la parte de Fulfillment definimos.

Creando nuestro servicio con Node

Aunque la finalidad de este articulo es tener una idea básica de Dialogflow, voy a explicar de forma sencilla como montar una pequeña API REST con Node para poder ver el comportamiento de dialogflow con un servicio externo.

Para el servicio externo podemos usar cualquier lenguaje de lado del servidor (Node, PHP, Java, etc…).

Lo primero que necesitamos es instalar Node y npm.

Una vez instalado podemos comprobar si funciona usando los siguientes comandos, node -v para ver la versión de node y npm -v para comprobar la versión de npm.

Creamos la carpeta donde vamos a crear el script y ejecutamos el siguiente comando: npm init -y, con este comando estamos generando el fichero package.json con los valores por defecto.

El fichero package.json es un fichero que contiene la configuración del proyecto en Node

Para este ejemplo necesitamos los siguiente paquetes y que instalaremos de la siguiente forma

  • npm i -D express
  • npm i -D body-parser
  • npm i -D request
  • npm i -D google-translate-api

Comenzamos con el código del servicio en Node:

servicio.js

'use strict';

// Libreria express para crear un api rest
const express = require("express");
const bodyParser = require("body-parser");

// Para hacer peticiones http de forma simple
const request = require('request');

// Para usar express dentro de Node
const app = express();

// Definimos el puerto
const port = process.env.PORT || 8899;

// Traducción en tiempo real
const translate = require('google-translate-api');

// Middleware de análisis del cuerpo de Node.js 
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// Métodos de ruta (VERBOS HTTP: POST, GET, PUT, DELETE, etc...). Endpoint
app.post("/api/tiempo", (req, res) => {

    // JSON QUE ENVIA DIALOGFLOW
    let ubicacion = req.body.result.parameters["any"];

    // Valor de kelvin para hacer la transformación a centígrados
    let kelvin = 273.15;

    // URL del API para la consulta de la temperatura por la posición geográfica
    let url = `http://api.openweathermap.org/data/2.5/forecast?q=${ubicacion}&APPID=apikey`;

    // Realizamos la petición
    request(url, function(error, response, body) {
        // Convertimos a JSON, la respuesta del servicio
        let _body = JSON.parse(body);

        // Que no de error el servicio externo
        if (_body.cod === '200') {

            // Pequeñas conversiones
            let meses = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
            let mesTxt = meses[parseInt(_body.list[0].dt_txt.split(" ")[0].split("-")[1]) - 1];
            let fecha = `${_body.list[0].dt_txt.split(" ")[0].split("-")[2]} de ${mesTxt} de ${_body.list[0].dt_txt.split(" ")[0].split("-")[0]}`;
            let temperatura = _body.list[0].main.temp - kelvin;

            // Formamos la respuesta que enviaremos a Dialogflow
            let _response = new Object();

            // DEFAULT RESPONSE EN DIALOGFLOW
            _response.speech = `La temperatura prevista para el día ${fecha} (${_body.list[0].dt_txt.split(" ")[1]}) en ${_body.city.name} es de ${temperatura.toFixed(1)} grados `;
            _response.displayText = _response.speech;
            _response.source = "webhook";

            // Enviamos la respuesta 
            res.status(_body.cod).send(_response);
        } else {
            // ERROR!!!
            translate(_body.message, { to: 'es' }).then(resTra) => {
                let _response = new Object();
                _response.speech = resTra.text;
                _response.displayText = resTra.text;
                _response.source = "webhook";
                res.status(200).send(_response);
            }).catch(err) => {
                console.error(err);
            });
        }
    });
});

// Escuchando nuestro servidor Node
app.listen(port, () => {
    console.log(`API REST en el puerto: http://localhost:${port}`);
});

Cuando este creado el fichero con el código, solo quedaría ejecutar node servicio.js. 

En ese momento deberíamos tener en el puerto 8899 nuestro servidor corriendo a la espera de los datos.

Nuestro primer bot funcionando

Para poder ejecutar este ejemplo y que se puede comprobar su funcionamiento, además de la integración con slack voy a crear una integración web para  ejecutar directamente desde esta entrada.

Aunque casi todo el trabajo esta hecho en dialogflow, como siempre dejo el enlace a github con el código.

Recordad que para este ejemplo únicamente he usado dos tipos de variaciones para solicitar la temperatura, que tiempo hace…. y que temperatura hace…