Usar QnAMaker y LUIS en el mismo bot

Ya te he contado varias veces en el pasado cómo usar LUIS (Language Understanding Intelligence Service) y hace no tanto QnAMaker, el servicio que te permite que tu bot sea capaz de contestar todas las preguntas frecuentes de tu empresa de forma sencilla. En este post me gustaría contarte que también es posible utilizar ambos servicios en un bot para que LUIS llegue a aquellas intenciones más elaboradas, a las que QnAMaker no es capaz.

Para verlo con un ejemplo, en el evento Women TechMakers hice un taller donde utilizaba las preguntas frecuentes de la página de Desigual para mostrar lo sencillo que era utilizar el QnAMaker para dicho escenario. En el ejemplo, las preguntas frecuentes que se podían contestar eran las siguientes:

QnAMaker – Preguntas pedidos desigual

Sin embargo, imagínate que además de conocer las respuestas a todas esas preguntas quisieras saber además el estado de tu pedido. Para ello, sería necesario tener un modelo de LUIS que reconociera dicha intención y que además la respuesta del bot tuviera la lógica suficiente para consultar en la base de datos de los pedidos y así poder devolver una respuesta más personalizada al usuario en cuestión, más allá de contestar con una respuesta enlatada.

QnAMaker- Estado de mi pedido – No match

Para ello crea un contexto de LUIS en https://luis.ai y agrega el intent EstadoPedido.

Microsoft Cognitive Services – LUIS – EstadoPedido

Añade algunas frases que se correspondan con dicha intención.

LUIS – EstadoPedido – Utterances

Guarda el modelo, entrénalo y publícalo para poder usarlo desde tu bot. El código para poder utilizar ambos servicios a la vez sería el siguiente:

'use strict';

const restify = require('restify'),
    builder = require('botbuilder'),
    cognitiveservices = require('botbuilder-cognitiveservices'),
    luisRecognizer = new builder.LuisRecognizer('LUIS_URL'),
    intents = new builder.IntentDialog({ recognizers: [luisRecognizer] }),
    recognizer = new cognitiveservices.QnAMakerRecognizer({ knowledgeBaseId: 'KB_ID', subscriptionKey: 'YOUR_SUBSCRIPTION_KEY' });

//Configure server
let server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
    console.log('%s listening to %s', server.name, server.url);
});

const connector = new builder.ChatConnector({
    appId: process.env.MICROSOFT_APP_ID,
    appPassword: process.env.MICROSOFT_APP_PASSWORD
});

let bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());

//intents
intents.matches('EstadoPedido', (session, args, next) => {
    session.endDialog('EstadoPedido intent');
});

intents.onDefault((session, args) => {
    session.endDialog('Lo siento, no te he entendido :(');
});

const QnAMakerDialog = new cognitiveservices.QnAMakerDialog({
    recognizers: [recognizer],
    defaultMessage: 'No match!',
    qnaThreshold: 0.3
});

QnAMakerDialog.invokeAnswer = (session, qnaMakerResult, threshold, noMatchMessage) => {
    if (qnaMakerResult.score >= threshold) {
        console.log('QnAMaker responds');
        session.endDialog(qnaMakerResult.answer);
    }
    else {
        console.log('LUIS responds');
        session.beginDialog('/fallbackLUIS');
    }
};

bot.dialog('/', QnAMakerDialog);
bot.dialog('/fallbackLUIS', intents);

De esta manera todas aquellas preguntas o intenciones que QnAMaker no sepa contestar serán reconocidas por LUIS y redirigidas al diálogo que corresponda.

¡Saludos!