Desarrollo de un chatbot multilingüe para pedir unas tapas a través de WhatsApp

March 09, 2021
Redactado por
Revisado por

Crea un chatbot multilingüe para pedir unas tapas a través de WhatsApp

Este blog complementa este artículo de Dominik, en el que describe cómo construir un sistema para pedir café usando Autopilot. Por el momento, Twilio Autopilot sólo admite inglés, por lo que explicaremos cómo implementar el sistema para recibir pedidos tanto en inglés como en español usando Google Dialogflow.

Requisitos previos

Si aún no tienes tu cuenta Twilio, sigue este enlace para crear una gratuita. ¡Tardarás menos de 2 minutos!

En tu cuenta Twilio usaremos:

Además, necesitarás una cuenta para Google Dialogflow en la plataforma Google Cloud.

Diagrama de funcionamiento

El flujo que vamos a implementar es el siguiente:

Diagrama de funcionamiento Whatsapp y Twilio Functions
Diagrama de funcionamiento

Veamos en detalle los pasos:

  1. El cliente envía un mensaje vía WhatsApp
  2. En cuanto Twilio recibe el mensaje, se invoca la API de Google Translate para identificar el idioma
  3. Si el mensaje es en español, se enviará a Dialogflow identificando el flujo como [es]
  4. Si el mensaje es en inglés, se enviará a Dialogflow identificando el flujo como [en]
  5. Una vez que haya identificado, procesaremos la respuesta en la plataforma Twilio y enviaremos un mensaje respuesta al usuario (siempre vía WhatsApp)

Activación de WhatsApp en la cuenta Twilio

Para recibir pedidos usaremos WhatsApp. En particular, el sandbox de WhatsApp que se puede activar en cualquier cuenta, incluso en modo prueba (trial). Si quieres conseguir tu propio número de WhatsApp puedes hacerlo siguiendo este procedimiento (ten en cuenta que puede tardar entre dos y cuatro semanas).

En la consola de Twilio, abre esta página: después de aceptar crear una sandbox, en el centro de la página encontrarás un número de teléfono y una frase (normalmente "join xxx-xxxxx").

Configuración de la Sandbox de Whatsapp - paso 1

Usa WhatsApp en tu teléfono para enviar la frase especificada al número dado. Una vez hecho esto, deberías ver el siguiente mensaje:

Configuración de la Sandbox de Whatsapp - paso 2

Al mismo tiempo, deberías haber recibido un mensaje de respuesta en su Whatsapp. Ahora todo está listo para intercambiar mensajes con tu cuenta de WhatsApp a través de la API de Twilio.

El siguiente paso es agregar una función de manejo de mensajes. Para hacer esto, usaremos las Twilion Functions:

  • Visita esta página en la consola de Twilio
  • Haz click en "Create Service"
  • En el pop-up que se abre escribe tapas-service y da al "Next"
  • En la página que se abre haz clic en "Add" y "Add Function"
  • Para el "PATH" escribe "/inbound-message"
  • En la parte del código, copia y pega el siguiente código de
function detectLanguage() {
   // Function for detecting language of incoming message
  return Promise.resolve('es')
}

exports.handler = function(context, event, callback) {
   detectLanguage()
   .then(language => {
       let response = ''
       switch (language) {
           case 'es':
           // Gestisci richieste in italiano
           response = 'Estamos procesando su pedido'
           break;
           case 'en':
           // Handle requests in english
           response = 'We are handling your order'
           break;
       }
       callback(null, response);
   })
   .catch(error => {
       callback(error, null);
   })
};
  • Haz clic en el botón "Save"
  • Selecciona "Public" como opción para la función para que se pueda acceder desde Internet
  • Haz clic en el botón "Deploy All" para implementar la función en producción
  • Copia la ruta de la función (botón junto al path de la función - "Copy URL")

Ahora debemos conectar esta función al evento de llegada de nuevos mensajes en WhatsApp:

Configuración de los Webhooks de Whatsapp
  • Haz clic en el botón "SAVE" para guardar la configuración.

Ahora intente enviar cualquier mensaje a través de WhatsApp al número anterior. Si la configuración se ha realizado correctamente, debería recibir el siguiente mensaje de respuesta:

Test de mensaje en castellano

Ahora veamos cómo desarrollar la función para identificar el idioma y en consecuencia elegir la versión del flujo para el reconocimiento. Primero necesitamos crear un nuevo proyecto en Google Cloud

Crear un nuevo proyecto en Google Cloud

En este proyecto usaremos los siguientes servicios de Google Cloud:

  • Dialogflow: en este tutorial usaremos la edición estándar que es gratuita
  • APICloud Translation: en este tutorial usaremos el API de reconocimiento de idiomas. Este servicio tiene un coste, pero se pueden utilizar métodos alternativos para lograr el mismo resultado, como un número de teléfono diferente para cada idioma.

Comencemos creando un nuevo proyecto llamado "TapasBar". Para ello, visite esta página y después de registrarse (o iniciar sesión si ya tiene una cuenta), haga clic en el botón "Crear proyecto" y dé el nombre "TapasBar". Una vez hecho esto, tendra que completar estos dos pasos:

  • Activar la API Translate y generar una clave API para el acceso (opcional)
  • Habilitar el API Dialogflow

Reconocimiento del idioma (opcional)

Primero necesitamos habilitar la API Translate en el proyecto que acabamos de crear:

  • Visita esta página ("API & Services")
  • Haz clic en "+ ENABLE APIS AND SERVICES"
  • Busca "Cloud Translation API" y confirma
  • Haz clic en "Enable" en la página que se abre

En este punto, necesitamos generar una clave de API que se utilizará para usar la API Translate desde nuestro backend:

  • Visita esta página ("Credentials")
  • Haz clic en "+ CREATE CREDENTIALS"
  • Selecciona "API Key"
  • Copia la clave API y guárdela en un lugar seguro
  • Haz clic en el botón "Close"

Agrega la implementación de la función detectLanguage() en la función creada anteriormente:

  • Abre la página de configuración del servicio tapas-service
  • Haz clic "Environment Variables"
  • Agrega una nueva variable que se llamará "TRANSLATE_API_KEY" (todo en mayúsculas) y como valor pega la clave API que obtuviste en la consola de Google:

Translation service API Key and environment variables
  • Agrega el paquete node-fetch en la lista "Dependencies". En el cuadro vacío que aparece escribe "node-fetch"y haz clic en el botón "Add".

Adding node module dependencies
  • Ahora abre la función que creaste anteriormente (es decir, /inbound-message) y reemplázala con el siguiente código:
const fetch = require('node-fetch');

function detectLanguage(text) {
   body = {
       q: text
   }

   return fetch('https://translation.googleapis.com/language/translate/v2/detect?key=' + process.env.TRANSLATE_API_KEY, {
       method: 'POST',
       body:    JSON.stringify(body),
       headers: { 'Content-Type': 'application/json' },
   })
   .then(response => response.json())
   .then(resJson => Promise.resolve(resJson.data.detections[0][0].language));
}

exports.handler = function(context, event, callback) {
   detectLanguage(event.Body)
   .then(language => {
       console.log(language)
       let response = ''
       switch (language) {
           case 'es':
           // Gestisci richieste in italiano
           response = 'Estamos procesando su pedido'
           break;
           case 'en':
           // Handle requests in english
           response = 'We are handling your order'
           break;
       }
       callback(null, response);
   })
   .catch(error => {
       callback(error, null);
   })
};
  • Además de implementar la función detectLanguage() también hemos cambiado la forma en que llamamos a esta función en la línea 18: hemos agregado una variable, que en nuestro caso será el contenido del mensaje recibido a través de WhatsApp (es decir, event.Body).
  • Ahora guarda la función haciendo clic en el botón "Save" y haga "Deploy All".

Ahora intentemos enviar los siguientes mensajes vía WhatsApp: "Me gustaría pedir unas patatas bravas por favor" y "May I have some tapas?". Si todo funciona correctamente, debería recibir las siguientes respuestas:

Test de mensajes en castellano y en inglés

Ahora estamos listos para agregar motores de reconocimiento de idioma.

Creación de un nuevo agente en Dialogflow

En este apartado veremos cómo crear un agente en Dialogflow que interprete las solicitudes en italiano. Para crear un nuevo agente en Dialogflow:

  • Accede a la consola de Dialogflow
  • Haz clic en la flecha en la parte inferior del menú de la izquierda y seleccione "Create new Agent".

Configuración de Dialogflow - paso 1
  • Dé el nombre "Camarero" al nuevo agente.
  • Seleccione español como idioma principal.

Configuración de Dialogflow - paso 2
  • Haz clic en el botón "CREATE".

A continuación se explica cómo programar nuestro chatbot:

  • Haz clic en el "+" a la derecha de "Intents" en el menú de la izquierda.
  • Como nombre de la intención, escriba "pedido"
  • Haz clic en "Add training phrases"
  • Como "Training Phrases" escriba lo siguiente: Me gustaría dos pinchos de atún.
  • Haz doble clic en la palabra dos: se mostrará un menú en el que seleccionará @ sys.number. Esto le dice a Dialogflow que esperamos un número en este punto de la oración.

Configuración de Dialogflow - paso 3
  • Haz doble clic en la palabra capuccini y en el menú elija @ sys.any. En este caso, estamos configurando Dialogflow para extraer la palabra que sigue al número.
  • Repita el mismo procedimiento para agregar la frase ¿Puedo tomar un café? (especificando @ sys.number y @ sys.any para "a" y "coffee" respectivamente)
  • Opcional: agregue otras frases para aumentar la probabilidad de que se reconozca la intención
  • Haz clic en "Guardar" para guardar la nueva intención
  • Espere hasta "Entrenamiento del agente completado" se muestra en la parte inferior derecha de la página.

Para este ejemplo, no estamos agregando ninguna respuesta a la intención, porque manejaremos la respuesta directamente en la función que creamos arriba. Antes de hacer esto, necesitamos crear un nuevo usuario en Dialogflow que usaremos para realizar solicitudes desde Twilio:

Para poder utilizar la API de Dialogflow, se debe activar en tu proyecto Google Cloud y generar una cuenta de servicio con los derechos de acceso necesarios. Sigue los pasos indicados en la documentación de Google Cloud.

Ahora estamos listos para conectar el agente de Dialogflow a Twilio.

Conectar el agente de Dialogflow con Twilio.

En este párrafo veremos cómo usar la API de Google para enviar el mensaje a Dialogflow y luego administrar la intención reconocida a través de Twilio. Primero agregamos el cliente de Google al entorno Twilio Runtime:

  • En la consola Twilio abre la página de configuración de las funciones
  • En la sección de dependencias agrega dialogflow y haz clic en "Guardar"

Añadir la API de Dialog flow como dependencia en la Twilio Function

Ahora agreguemos el código para administrar la llamada Dialogflow. Primero abre la función inbound_message. Una vez abierta hay tres adiciones que hacer.

Configuración de Dialogflow

Lo primero que hay que agregar (aparte de la biblioteca Node.JS de Dialogflow) es la información de autenticación de Dialogflow que descargamos en el archivo JSON anteriormente. Luego agrega el siguiente código en la parte superior de la función:

const dialogflow = require('dialogflow');

const projectId = '<Dialogflow JSON project_id>';
const dfConfig = {
   credentials: {
       private_key: '<Dialogflow JSON private_key>',
       client_email: '<Dialogflow JSON client_email>'
   }
}

Toda la información necesaria se incluye en el archivo JSON que descargaste de Dialogflow cuando creaste la cuenta de servicio.

Conector de Dialogflow

Esta función realiza la llamada a Dialogflow para el reconocimiento de intención:

async function detectIntent(query, lang) {
 // New session client
 const sessionClient = new dialogflow.SessionsClient(dfConfig);
 // The path to identify the agent that owns the created intent.
 const sessionPath = sessionClient.sessionPath(projectId, '123456');

 // The text query request.
 const request = {
   session: sessionPath,
   queryInput: {
     text: {
       text: query,
       languageCode: lang,
     },
   },
 };

 const responses = await sessionClient.detectIntent(request);
 return responses[0];
}

En la llamada, sessionClient.sessionPath() el segundo argumento debe ser una cadena que identifique de forma única la interacción. Para simplificar, hemos agregado una cadena estática, pero en producción sería mejor agregar un identificador único (e.g. el número de teléfono). Dialogflow permite la adición de un contexto a la operación de reconocimiento de intención (más información aquí). Esto te permite crear una conversación en la que el chatbot responde en función de las preguntas / respuestas anteriores. Para simplificar, no hemos incluido el contexto en nuestro ejemplo, pero puedes agregarlo al objeto de request. También pasamos el idioma con la variable lang

Transferir función a Dialogflow

La función detectIntent() definida anteriormente es llamada por una nueva función donde podemos agregar (por ejemplo) toda la información de depuración:

async function executeQuery(query, lang) {
   let queryResult = {}
   try {
     console.log(`Sending Query: ${query}`);
     intentResponse = await detectIntent(query, lang);
     console.log('Detected intent');
     console.log(
       `Intent Name: ${intentResponse.queryResult.intent.displayName}`
     );
     console
     // Use the context from this response for next queries
     queryResult.success = true
     queryResult.intent = intentResponse.queryResult.intent.displayName
     queryResult.parameters = intentResponse.queryResult.parameters;
   } catch (error) {
     console.log('executeQuery() error');
     console.log(error);
     queryResult.success = false
   }
   return queryResult
}

Esta función manipulará el resultado de Dialogflow para hacerlo más manejable dentro de la función principal para operar con el mensaje entrante. En el ejemplo, hemos creado un nuevo objeto llamado queryResult que incluye parte (pero no toda) de la información devuelta por Dialogflow. Para agregar otros datos (como contexto), puede consultar la documentación de Dialogflow.

Ahora agreguemos la llamada a esta función dentro del controlador para mensajes entrantes:

exports.handler = function(context, event, callback) {
   detectLanguage(event.Body)
   .then(language => {
       console.log(language)
       let response = ''
       switch (language) {
           case 'es':
               // es español
               executeQuery(event.Body, 'es')
               .then(result => {
                   if (result.intent === 'pedido') {
                       response = `¡Gracias! Estamos preparando  ${result.parameters.fields.number.numberValue} ${result. parameters.fields.any.stringValue}`;
                   } else {
                       response = 'Lo siento, no he entendido. ¿Podría decírmelo otra vez?';
                   }
                   callback(null, response);
               })
               break;
           case 'en':
           executeQuery(event.Body, 'en')
               .then(result => {
                   if (result.intent === 'pedido') {
                       response = `Thank you! We are getting  ${result.parameters.fields.number.numberValue} ${result. parameters.fields.any.stringValue} ready for you.`;
                   } else {
                       response = 'Sorry, I did not understand. Could you repeat again?';
                   }
                   callback(null, response);
               })
               break;

Puedes añadir aquí la gestión de otras intenciones (como el mensaje de bienvenida o la "Default Welcome Intent").

Observa también cómo en la respuesta usamos los parámetros reconocidos por Dialogflow, es decir, result.parameters.fields.number.numberValue para el número y result.parameters.fields.any.stringValue para el tipo de tapa. Puedes utilizar esta información para agregar el pedido a tu sistema de gestión de pedidos.

Puede encontrar aquí el código final de la función para copiar y pegar en tu función.

Ahora guarda la función y mantén esta página abierta. La usaremos en la siguiente sección para verificar los registros y verificar que todo esté funcionando bien.

Prueba la solución

Primero lo intentaremos con una solicitud en español. Ve a tu WhatsApp y envía el siguiente mensaje: ¿Puedes traerme dos tapas de patatas bravas?. Primero veamos lo que se muestra en los registros de funciones:

Los logs de la Twilio Function se muestran en consola

Como puedes ver, la intención se reconoció correctamente. Si ahora buscas en WhatsApp, deberías haber recibido la siguiente respuesta:

Tests finales en castellano con Dialogflow

Observa que la respuesta contiene el número "2" (y no la palabra "dos" como en la oración original). Esto se debe a que para crear la respuesta estamos utilizando el parámetro, result.parameters.fields.number.numberValue que es un parámetro numérico (y es más adecuado para ser utilizado como entrada para su sistema de gestión de pedidos).

Probemos ahora la solicitud en inglés. Enviando: I will have one serrano plate . En este caso, la frase utilizada es diferente a la utilizada para el entrenamiento del piloto automático. A pesar de esto, reconoce la intención correctamente y responde en consecuencia:

Tests finales en inglés con Dialogflow

¡Enhorabuena!

Ahora todo está listo para publicar tu solución y pedir unas tapas. En este repositorio en GitHub puedes encontrar algunos scripts útiles para configurar automáticamente tu proyecto con el código que se muestra en este artículo, usando Twilio Runtime.

Entre otras cosas puedes añadir:

  • Respuestas dinámicas a través Google Dialogflow
  • Gestión de mensajes de bienvenida y mensajes en caso de error
  • Agregar características específicas al pedido (por ejemplo, para administrar pedidos como "Tapas de calamares en su tinta" versus Tapas de calamares a la romana")

Las opciones son infinitas y estamos ansiosos por ver qué más puedes crear con Twilio.

Ana Andrés es Ingeniera de Telecomunicaciones y Doctora en Ciencias de la Computación. Forma parte del equipo de ingenieros de soluciones en Twilio. Su equipo se dedica a asesorar a clientes de Twilio en Europa sobre todo lo relacionado con tecnología y productos de Twilio. Nacida en Barcelona, antes de trabajar en Twilio, Ana creó y dirigió como CTO su propia start-up. En su tiempo libre le gusta apoyar actividades de educación y ciencia en las escuelas.