How to integrate LUIS and QnA Maker services in single Node.js bot? - node.js

I'm developing a chatbot using Microsoft Bot Framework with Node.js SDK. I've integrated LUIS and QnA maker but I want to create this scenario if it's possible. Taking in example the following link and in particular this section:
There are a few ways that a bot may implement a hybrid of LUIS and QnA Maker:
Call LUIS first, and if no intent meets a specific threshold score, i.e., "None" intent is triggered, then call QnA Maker. Alternatively, create a LUIS intent for QnA Maker, feeding your LUIS model with example QnA questions that map to "QnAIntent."
Just an example:
I have my QnA KB in which I have a pair : " who are you?" / "Hi I'm your bot! " . Then I have my Luis app that recognize this intent called "common" .
So, if I write to my bot: " who are you?" it will answer "Hi I'm your bot! "
Instead, if I write " tell me who you are" it recognize the LUIS intent related to the question but it will not answer "Hi I'm your bot! " like I imagine.
So what I imagine is: I ask the question "Tell me who you are" --> the bot triggers the intent common (LUIS) --> then I want that the bot will answer me looking into the QnA KB --> "Hi I'm your bot! "
Is it possible?
Hope this code could help:
var intents = new builder.IntentDialog({ recognizers[luisRecognizer,qnarecognizer] });
bot.dialog('/', intents);
intents.matches('common_question', [
function (session, args, next) {
session.send('Intent common');
qnarecognizer.recognize(session, function (error, result) {
session.send('answerEntity.entity');
});
}
]);

You will have to forward the user message to QnaMaker from the method/dialog associated to the intent detected by LUIS. Take a look to this article (https://blog.botframework.com/2017/11/17/qna-maker-node-js-bots/) to find how implement QnAMaker in Node.js
Something like:
var recognizer = new cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: 'set your kbid here',
subscriptionKey: 'set your subscription key here'});
var context = session.toRecognizeContext();
recognizer.recognize(context, function (error, result) { // your code... }
You should explore the samples also and try to understand how everything works: https://github.com/Microsoft/BotBuilder-CognitiveServices/tree/master/Node/samples/QnAMaker
If you vary a lot your question; could be possible that QnA won't detect the question you expected and in that case you will have to train your KB more (like you do in LUIS with the utterances/intents)

I wrote this because I want more practice with node and this was an excuse to use node, but what Ezequiel is telling you is completely correct. I'll also post on your GitHub issue. This is a functioning node app that does what you need
var builder = require('botbuilder');
var restify = require('restify');
var cog = require('botbuilder-cognitiveservices');
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: "APP ID",
appPassword: "APP PASSWORD"
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector, function(session) {
session.send('Sorry, I did not understand \'%s\'. Type \'help\' if you need assistance.', session.message.text);
});
var recognizer = new builder.LuisRecognizer("https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/{LUIS APP ID}?subscription-key={LUIS KEY}&verbose=true&timezoneOffset=0&q=");
bot.recognizer(recognizer);
var qnaRecognizer = new cog.QnAMakerRecognizer({
knowledgeBaseId: 'QNA APP ID',
subscriptionKey: 'QNA SUBSCRIPTION KEY'
});
bot.dialog('Common', function(session) {
var query = session.message.text;
cog.QnAMakerRecognizer.recognize(query, 'https://westus.api.cognitive.microsoft.com/qnamaker/v2.0/knowledgebases/{QNA APP ID}}/generateAnswer', '{QNA SUBSCRIPTION KEY}', 1, 'intentName', (error, results) => {
session.send(results.answers[0].answer)
})
}).triggerAction({
matches: 'Common'
});

As of 2018 (BotBuilder V4)
You can now use the Dispatch Command Line tool to dispatch intent across multiple bot modules such as LUIS models and QnA.
So first you will get to LUIS app that will decide based on the score to redirect to another LUIS app or to a QnA.
Dispatch Tool
Example using LUIS as dispatch service

Related

disable qna recognizer when inside a dialog

I am using Luis and QnA maker, qna maker is now interupting a waterfall prompt. I have disabled the Luis prompt with code below, how can I to do same for the qna recognizer?
var recognizer = new
builder.LuisRecognizer(LuisModelUrl).onEnabled(function (context,
callback) {
var enabled = context.dialogStack().length == 0;
callback(null, enabled);
});
bot.recognizer(recognizer);
bot.recognizer(qnaRecognizer);
console.log(recognizer);
eg: What part of the toilet is broken? (1. Cistern, 2. Pipe, or 3. Seat)
Anything except an exact match gets picked up by qna sentiment which replaces the dialog stack
Thanks
You shouldn't have to disable either one for the code to work. I suspect the problem is in your dialog flow. Below is an example of how to construct the dialog portion of your bot. When I ran this, the logger middleware shows QnA is matching on the inputs I feed, but the bot is dictating the conversation because of the code.
var luisrecognizer = new builder.LuisRecognizer(LuisModelUrl);
var qnarecognizer = new cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
authKey: process.env.QnAAuthKey || process.env.QnASubscriptionKey,
endpointHostName: process.env.QnAEndpointHostName
});
var basicQnAMakerDialog = new cognitiveservices.QnAMakerDialog({
recognizers: [qnarecognizer],
defaultMessage: 'No match! Try changing the query terms!',
qnaThreshold: 0.3
});
bot.recognizer(luisrecognizer);
bot.recognizer(basicQnAMakerDialog);
bot.dialog('/', basicQnAMakerDialog);
bot.dialog('GreetingDialog',[
(session) => {
session.send('You reached the Greeting intent. You said \'%s\'.',
session.message.text);
builder.Prompts.text(session, "What is your name?");
},
(session, results) => {
session.userData.name = results.response;
session.send("Glad you could make it, " + session.userData.name);
builder.Prompts.text(session, "Ask me something!");
},
(session, results) => {
session.conversationData.question = results.response;
session.send(session.conversationData.question + " is an interesting topic!")
session.endDialog();
}
]).triggerAction({
matches: 'Greeting'
})
In the following image, LUIS brings me to the Greeting intent when I type "I'm happy [to be here]" which I have trained in the LUIS app. The bot dialog takes over asking me questions and storing the answers. Even though I'm making statements that QnA or LUIS should respond to neither is doing so. The conversation follows the code.
Had Qna taken over it would have responded to "What are you" with some text about QnA Maker. Similarly, "help" would have produced responses from either QnA or LUIS as I have topics/intents for both, respectively.

How can I properly get my Luis chatbot working?

I have to connect Luis to node.js and create my first chatbot that, as a first stage should handle simple requests.
I have checked the following links :
https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-recognize-intent-luis
https://github.com/Microsoft/BotBuilder-Samples/tree/master/Node/intelligence-LUIS
but getting started has proven to be difficult, what I've done as a first stage is:
var restify = require('restify');
var builder = require('botbuilder');
var http = require('http');
var recognizer = require ('recognizer');
// 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);
});
// Create chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
appId: MY_APP_ID,
appPassword:MY_PASSWORD
});
var bot = new builder.UniversalBot(connector, function (session,args) {
}
});
var recognizer = new builder.LuisRecognizer(LUIS_ENDPOINT_URL);
bot.recognizer(recognizer);
and not sure how to move forward from here.
What I have in a Luis intent is: calendar.add
what I have as entities is: calendar.location and calendar.subject
what I want the user to say in the bot framework channel emulator:
add a business meeting schedule in Paris.
What the bot should say: Understood the location is Paris and subject is business meeting.
It seems that the utterance add a business meeting schedule in Paris. doesn't match the Calendar.Add intent. So you can try to manually add this utterance in the intent in your LUIS application.
Go to your LUIS application, click Intents list, click Calendar.Add into the edit page.
Type the utterance add a business meeting schedule in Paris. in the box, type enter add the utterance into the list.
Click the business and meeting letters into a big square brackets, select Calendar.Subject in dropdown list, the same click Paris and select Calendar.Location. Finishing the actions, it should looks like:
Click Save to save the edition. Then Train and publish your LUIS application.
Then your bot should match the utterance.

Use multiple QnA services in one bot

I have multiple QnA services running, each with it's own knowledgeBaseId and subscriptionKey. I want to use them both in a single chatbot and I've written a piece of code which takes the user input and assigns the correct Knowledgebase details. However, I'm unable to get the 2nd QnA service to become active and the bot is linking only to the first service. What might be going wrong here?
Sample code:
var knowledgeId = "FIRST_QNA_SERVICE_KNOWLEDGE_ID";
var subscriptionId = "FIRST_QNA_SERVICE_SUBSCRIPTION_ID";
var bot = new builder.UniversalBot(connector);
var qnarecognizer = new cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: knowledgeId,
subscriptionKey: subscriptionId,
qnaThreshold:0.3,
top:1});
var intentrecognizer = new builder.IntentDialog();
var intents = new builder.IntentDialog({ recognizers: [intentrecognizer, qnarecognizer] });
bot.dialog('/', intents);
intents.matches('qna', [
function (session, args, next) {
args.entities.forEach(function(element) {
session.send(element.entity);
}, this);
}
]);
intents.matchesAny([/hi/i, /main menu/i], [
function (session) {
builder.Prompts.choice(session, "Hi, What would you like to ask me about?", ["Service1","Service2"],{ listStyle: builder.ListStyle.button});
},
function (session, result) {
var selection = result.response.entity;
switch (selection) {
case "Service1":
knowledgeId = "FIRST_QNA_SERVICE_KNOWLEDGE_ID";
subscriptionId = "FIRST_QNA_SERVICE_SUBSCRIPTION_ID";
session.send('You can now ask me anything about '+selection+'. Type \'main menu\' anytime to choose a different topic.')
session.endConversation();
return
case "Service2":
knowledgeId = "SECOND_QNA_SERVICE_KNOWLEDGE_ID";
subscriptionId = "SECOND_QNA_SERVICE_SUBSCRIPTION_ID";
session.send('You can now ask me anything about '+selection+'. Type \'main menu\' anytime to choose a different topic.')
session.endConversation();
return
}
}
])
You never updated the knowledgeId/subscriptionId on the QnAMakerRecognizer... that's the reason you are always seeing the first QnA service being called.
See if there is a way to update those values in the QnAMakerRecognizer.
Check out if my answer is valid to your purpose on this question:
Multiple QnA Maker services for a single bot
Hope you find it useful.

Connecting LUIS to Microsoft Bot Framework

Over the holiday weekend, I've been trying to get a bot working using the Microsoft Bot Framework. I'm using version 3.9.1 of the botbuilder package for Node.js.
I've created an app and model at www.luis.ai. I have been able to successfully test my intents via the "Train & Test" feature. Then, in my actual Node code, I have the following:
let connector = new BotBuilder.ChatConnector({
appId: 'myId',
appPassword: 'myAppSecret'
});
let bot = new BotBuilder.UniversalBot(connector);
let luis = new BotBuilder.LuisRecognizer('myLuisAppUrl');
let intent = new BotBuilder.IntentDialog({ });
intent.recognizer(luis);
intent.matches('Intent.1', '/execute-report');
intent.matches('Intent.2', '/execute-batch-job');
intent.onDefault('/unknown');
bot.dialog('/', intent);
bot.dialog('/execute-report', [function(session, args, next) {
var result = ((Date.now() % 2) === 0) ? 'Report Ran!' : 'Failed';
session.send(result);
}]);
bot.dialog('/execute-batch-job', [function(session, args, next) {
var result = ((Date.now() % 2) === 0) ? 'Batch Job Ran!' : 'Unable to run Batch Job';
session.send(result);
}]);
bot.dialog('/unknown', [function(session, args, next) {
session.send('What did you ask for?');
}]);
When interacting with my bot, I always get "What did you ask for?". In other words, at this point, I know that:
I can successfully interact with my bot. However, the /unknown dialog is always being called, which is not the correct interaction.
My model in LUIS looks correct:
a. If I enter "Run Report" in the LUIS.ai Test app, the top scoring intent is "Intent.1"
b. If I enter "Execute Batch Job" in the LUIS.ai Test app, the top scoring intent is "Intent.2"
However, my bot is not sending the appropriate response. The /execute-report and /execute-batch-job dialogs are never used, even though they should be. I don't understand what I'm doing wrong. To me, I believe I've setup my bot correctly. I don't see what I'm doing wrong. Can someone please tell me what I'm doing wrong? Is there a way to see the response returned from LUIS in my Node code similar to what's seen in the "Test" app at LUIS.ai
If you go to line 89 of the LuisRecognizer and add the following on a new line: console.log(result); you will see the LUIS response object that your bot has received.
Your code looks correct to me, so the issue might be on the LUIS side. Have you published your app?

How can I integrate my MS text bot program written in NodeJS to skype bot?

I'd like to develop a skype bot that would take user name as input and say hello username in the opposite char case based on the user input. In brief, if the user types his name as james, my bot would respond to him as Hello JAMES. The program runs fine, however I am finding it ambiguous to integrate my textbot program to skype bot.
Here's my code:
var builder = require('botbuilder');
var helloBot = new builder.TextBot();
helloBot.add('/', [
function (session, args, next) {
if (!session.userData.name) {
session.beginDialog('/profile');
} else {
next();
}
},
function (session, results) {
session.send('Hello %s!', session.userData.name);
}
]);
helloBot.add('/profile', [
function (session) {
builder.Prompts.text(session, 'Hi! What is your name?');
},
function (session, results) {
if(results.response == results.response.toUpperCase())
{
//console.log("in if");
session.userData.name = results.response.toLowerCase();
}
else
{
//console.log("else");
session.userData.name = results.response.toUpperCase();
}
session.endDialog();
}
]);
console.log("Hi!");
helloBot.listenStdin();
The output would be like :
bot : Hi
user: Hello.
bot : What is your name?
user: james.
bot : Hello JAMES.
To create a chat bot compatible with Skype, use the UniversalBot type instead of TextBot. You can find sample code that demonstrates how to send different card types in BotBuilder-Samples/Node/cards-RichCards.
To configure your bot to work with Skype, login to the Bot Portal at https://dev.botframework.com and register your bot. After your bot is registered, go to 'My bots', click on your bot name, and you will see the 'Channels' section with Skype and WebChat enabled by default. Under 'Test link', click the 'Add to Skype' button. This will redirect you to the Skype website where it will ask you to confirm that you want to add the Skype bot to your Skype contacts.
For more information on Skype bots check out the Getting Started Guide.

Resources