Asistentes de Voz 101: Amazon Alexa

En Solid GEAR somos expertos en el desarrollo de software, esto nos obliga y anima a estar al día tanto con lenguajes de programación, frameworks, integraciones, etc. Para ello dedicamos parte de nuestro tiempo a investigar e innovar para así poder incluir en nuestros proyectos mejoras de forma periódica además de sugerir a nuestros clientes nuevas funcionalidades que aporten un valor adicional a sus productos, nos gusta sorprender a nuestros clientes. Hoy vamos a explorar un tema interesante que está en boca de todos (literalmente): Asistentes de voz y en particular, Amazon Alexa.

Nos dicen qué hora es, hacen la lista de la compra, el tiempo, encienden las luces y la calefacción en casa y una infinidad de cosas más, así que en Solid GEAR nos hicimos la siguiente pregunta: ¿Y si pudiéramos integrar un asistente de voz en nuestras aplicaciones?

Y nos pusimos manos a la obra, a investigar sobre asistentes de voz y cómo integrarlos en nuestro servidor en Node.js

Alexa, calienta que sales

Alexa es el asistente de voz de Amazon. Para poder integrar nuestras apps con Alexa tenemos que seguir crear una Skill en el portal de desarrollador de Amazon Alexa.

¿Qué es una Skill de Alexa?

Es una interfaz de uso que nos va a permitir usar nuestro propio servidor a través de comandos de voz que son procesados por Alexa.

O sin buzzwords: Es el equivalente a una aplicación, en la que el usuario envía y recibe información.

En el flujo de uso, el usuario le diría a Alexa que abriera la Skill, que posteriormente toma el control de la lógica de la conversación y gestiona qué tiene que hacer en cada momento.

Muy bien, ahora que sabemos lo que es una Skill de Alexa y como funciona vamos a configurarla.

Crea una Skill Alexa

Para crear una Skill tenemos que entrar en la Amazon Alexa Developer Console si no tienes una cuenta de desarrollador de Amazon, te pedirá que crees una (ten en cuenta que la Skill que vamos a crear tendrá estado En Desarrollo y solo podrá ser usada en dispositivos en los que el usuario en uso sea el mismo que sea el propietario de la Skill).

Una vez dentro de la consola, haremos click en el botón de Create Skill, te pedirá un nombre y un idioma por defecto; también te pedirá un modelo, nosotros usaremos Custom; después te pedirá un método para alojar los recursos del servidor de tu Skill, seleccionaremos Provision your own, ya que vamos a crear un servidor Node.js en local, volvemos a hacer click en Create Skill.

Create Skill

Una pequeña guía

En el menú lateral hay varias secciones, la que vamos a usar es Custom, donde vamos a encontrar diferentes subsecciones. Las que nos interesan son Interaction Model, que va a ser sobre lo que nos vamos a focalizar y Endpoint que va a ser donde vamos a configurar el recurso de nuestro servidor al que va a atacar la Skill cuando se ejecute alguna acción.

¿Cómo se llama mi asistente?

Una vez tenemos creada la Skill, lo primero que vamos a tener que configurar es el nombre de invocación de la Skill (el famoso Alexa, abre x). Para configurarlo entramos a la sección Custom, y dentro de esta a la subsección Invocation, en Skill Invocation Name escribimos como vamos a querer que active mi Skill siguiendo las instrucciones.

Invocation Name

Una vez tenemos el nombre le damos a Save Model.

Qué va a hacer nuestra Skill

Para definir las acciones que nuestra Skill va a hacer en cada momento utilizaremos Intents, se encuentran ubicados dentro de Interaction Model.

Built-in Intents

Verás que hay 4 Intents creados por defecto, estos Intents tienen que existir obligatoriamente, pero no por ello hay que configurarlos, aunque es muy recomendable ya que son acciones básicas que el usuario va a querer hacer en nuestra Skill. Si quieres saber más sobre Built-in Intents aquí tienes la documentación.

Para configurar un Intent vamos a tener que proporcionar al modelo ejemplos de la entrada de voz que va a tener por parte del usuario, cuantos más ejemplos le proporcionemos, más intuitivo va a ser, y por tanto más cómodo para el usuario. Para añadir ejemplos, tenemos un campo de texto en la sección Sample Utterances, ahí podemos ir añadiendo frases, solamente escribiéndolas y dando a la tecla ⏎.

Sample Utterances

Custom Intents

A diferencia de los Built-in Intents, en los Custom Intents vamos a poder añadir parámetros a nuestras frases, estos son conocidos como Slots.

Para poder añadir un parámetro en nuestras frases de ejemplo, seleccionamos el texto que queremos parametrizar, nos saldrá un popup en el que podremos añadir un nuevo Slot que podremos editar posteriormente en la parte en la que pone Intent Slots.

Intent Slots

Necesitamos especificar el tipo de parámetro que será, en el desplegable Slot Type, podemos seleccionar uno de los que Amazon nos provee o crear uno nuevo en el que vamos a poder definir valores para ese parámetro y sinónimos.

Si el tipo de parámetro no va a ser de un tipo fijo, Amazon nos da un tipo que se llama Amazon.SearchQuery, que va a identificar cualquier valor.

Slot Types

Una vez tenemos definido el tipo de parámetro ya podemos guardarlo.

Para guardar y entrenar el modelo después de configurar un Intent, hacemos click en Build Model.

Para comprobar que el mapeo de Intents y Slots es correcto podemos hacer click en Evaluate Model y usar una de las frases de ejemplo que hemos usado para configurar el Intent, veremos que al usar la frase, resuelve el nombre del Intent y mapea los Slots con sus valores.

Evaluate Model

Genial, pero por ahora tenemos a nuestra asistente muda, y eso no suele gustarle a casi nadie, así que vamos a ver como podemos darle voz y conectarlo con nuestro servidor para que responda dinámicamente.

Alexa, dime algo please

Como nuestro servidor va a ser local y Amazon nos obliga a que la URL a la que ataca tenga certificado SSL, vamos generar una con ayuda de ngrok.

Para ello lo primero que tenemos que hacer es descargar ngrok

npm install -g ngrok

Después, lanzaremos ngrok sobre el puerto que queramos de la siguiente forma:

ngrok http [PUERTO]

Esto generará una URL con http y otra con https, usaremos la https.

Para poder conectar nuestra Skill a un servidor vamos a tener que especificar el recurso nuestra API al que vamos a atacar, así que en la subsección Endpoint seleccionaremos HTTPS bajo Service Endpoint Type, y en el campo Default Region pondremos la ruta completa, que será la URL que hemos creado previamente con ngrok y un endpoint, que en mi caso va a ser /alexa, os debería quedar algo así: https://486a3467.ngrok.io/alexa, por otro lado, en SSL Certificate Type, seleccionaremos “My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority”. Hacemos click en Save Endpoints.

Y con esto finalizamos la configuración básica de la Skill, ahora toca crear nuestro servidor y especificar las acciones que tiene que realizar con cada Intent que le llegue.

Para ahorrar tiempo, dejo aquí un pequeño servidor Node.js creado con Express en el que hay ya un endpoint para recibir las llamadas de la Skill ([POST] /alexa)

El servidor está preparado para exponerse en el puerto 4500, si quereis lanzarlo en otro, podeís cambiarlo en el archivo configuration/config.json (hay que tener en cuenta que el puerto tiene que ser el mismo que habeís usado para lanzar ngrok.

Para lanzar el servidor:

npm install && npm start

Cómo funcionan los Intent

En el momento en el que se hace la invocación de la Skill de Alexa, el servidor deberá tener registrados unos Handlers que mapeen las acciones de Alexa (Intents) a funciones en el servidor, esencialmente es un objeto con dos funciones:

  • canHandle: es ejecutada y decide si este es el Handler que ha de usarse, por lo que si esta función devuelve true, este será el Handler usado. Por lo general, en este método comprobaremos el nombre del intent y/o el tipo de la request (aquí tienes los tipos de request que hay), en nuestro caso necesitaremos que devuelva true si el tipo de la request es de tipo IntentRequest y el nombre del Intent que hayamos configurado previamente en nuestra Skill.

  • handle: es la función que se ejecuta si este es el Handler seleccionado.

Ambas funciones reciben como parámetro el body de la petición que nuestra Skill hace a nuestro servidor.

Por lo general añadiremos tantos de estos objetos como Intents haya en nuestra Skill.

En el momento de la invocación de la Skill, el código de los Handler busca desde el primer Intent registrado hasta el último hasta que encuentra uno en el que la función canHandle devuelve true, por lo que si tenemos dos o más Intents en los que la función canHandle devuelve true sólo va a llegar al primero.

Vamos con la parte técnica

En la función canHandle usaremos el SDK de Alexa para Node.js, para ello necesitaremos instalarlo en nuestro proyecto:

npm install --save ask-sdk

Posteriormente necesitaremos importarlo a nuestro servidor

const Alexa = require('ask-sdk-core');

Para que Alexa nos conteste necesitaremos el objeto que se pasa como parámetro la función handle, este mismo objeto tiene un responseBuilder que nos permitirá devolver una respuesta hablada mediante el método .speak().

const response = handlerInput.responseBuilder
    .speak('Hola!')
    .getResponse();

Por otro lado, mediante el atributo shouldEndSession de la respuesta que acabamos de crear, indicaremos a nuestra Skill si después de esta respuesta, Alexa debe finalizar la conversación o por otro lado debe quedarse escuchando al usuario a la espera de otra petición, posteriormente devolvemos la respuesta.

response.shouldEndSession = false;
return response;

Aquí os dejo dos ejemplos de Intents, uno comprobando sólo el tipo de la request y otro comprobando también el nombre

// Comprueba tipo
const LaunchRequestHandler = {
    canHandle: handlerInput => handlerInput.requestEnvelope.request.type === 'LaunchRequest',
    handle: handlerInput => {
        const response = handlerInput.responseBuilder
            .speak('Bienvenido a tu skill customizada ¿Qué deseas hacer?')
            .getResponse();
        response.shouldEndSession = false;
        return response;
    }
};

// Comprueba tipo y nombre
const HelloWorldHandler = {
    canHandle: handlerInput =>
        handlerInput.requestEnvelope.request.type === 'LaunchRequest' &&
        handlerInput.requestEnvelope.request.intent === 'Custom_HelloIntent',
    handle: handlerInput => {
        return handlerInput.responseBuilder
            .speak('Hello World!')
            .getResponse();
    }
};

Después, creamos la Skill y registramos los Intents en nuestro servidor.

Para crear una Skill:

const customSkill = Alexa.SkillBuilders.custom();

Para registrar Intents en la Skill:

customSkill.addRequestHandlers(
    LaunchRequestHandler,
    HelloWorldHandler
);

Y por último invocamos la Skill en el momento en el que se recibe la petición en el endpoint /alexa (dentro del endpoint, en el que tenemos acceso a los parametros req y res de la petición).

const response = await customSkill.invoke(req.body);
return res.send(response);

Con esto podríamos probar que funciona en nuestra pestaña de test en la Alexa Developer Console.

Conclusiones

Como veis, no es nada difícil crear una Skill de Amazon Alexa y puede aportar un punto diferenciador a nuestras aplicaciones.

Los siguientes pasos serían Certificar y Distribuir la Skill en el market de Skills de Alexa, para ello podéis seguir las instrucciones que os da Amazon.

Espero que os haya servido de ayuda y que podáis aprovechar esta funcionalidad en vuestros proyectos. ¿Tenéis alguna idea adicional sobre cómo integrar Amazon Alexa o alguna mejora? 

¡Un saludo!

Acerca de Luis Revilla

Full Stack Developer / DevOps enamorado de las arquitecturas CLEAN. Me encanta automatizar y Dockerizar 🐋 todo. Probablemente ando por las nubes ⛅️⛅️ (Google Cloud, AWS y Azure)

Deja un comentario

Responsable » Solidgear.
Finalidad » Gestionar los comentarios.
Legitimación » Tu consentimiento.
Destinatarios » Los datos que me facilitas estarán ubicados en los servidores SolidgearGroup dentro de la UE.
Derechos » Podrás ejercer tus derechos, entre otros, a acceder, rectificar, limitar y suprimir tus datos.

¿Necesitas una estimación?

Calcula ahora