Recognize the user input inside the handler function using dialogflow.ai/api.ai and Microsoft Bot Builder - botframework

I am building a bot using the given technology stacks:
Microsoft Bot Builder
Node.js
Dialogflow.ai (api.ai)
We used waterfall model to implement a matched intent Dialog, which includes a couple of handler functions and prompts. After following this scenario I need to identify the entities inside an inner handler function, for a user input.
eg:
Bot : Where do you want to fly?
User: Singapore. (For this we added entities like SIN - Singapore,SIN(Synonym), So I need to resolve the value as SIN)
Any help on this scenario is much appreciated.

Here is a post Using api.ai with microsoft bot framework you can refer for your reqirement, and with a sample at https://github.com/GanadiniAkshay/weatherBot/blob/master/api.ai/index.js. The weather api key leveraged in this sample is out of date, but the waterfall and recognizer api key is still working.
Generally speaking:
Use api-ai-recognizer
Instantiate the apiairecognizer and leveraged builder.IntentDialog to include the recognizer:
var recognizer = new apiairecognizer("<api_key>");
var intents = new builder.IntentDialog({
recognizers: [recognizer]
});
In IntentDialogs, use builder.EntityRecognizer.findEntity(args.entities,'<entity>'); to recognize the intent entities.

Related

How do I use capture groups in Regular Expressions in Bot Composer

I am attempting to build a Microsoft Teams bot using the Bot Framework Composer. What I would like to do is create an integration with ServiceNow. The goal would be that if anyone posts a record number (ex. REQ0123456, INC0123456, KB0123456) into the group or direct chat (with the bot), the bot would look up that record and provide a card or a short summary of the record to the chat.
To avoid creating a completely separate intent for each record type, I was hoping to use RegEx to gather the match into 2 capture groups; one for the tbl_code and one for the number.
Here is the entry for the user input:
> add some example phrases to trigger this intent:
- look up {conversation.sn_record.tbl_code=REQ}{conversation.sn_record.number=0123456}
- lookup {conversation.sn_record.tbl_code=REQ}{conversation.sn_record.number=0123456}
- {conversation.sn_record.tbl_code=REQ}{conversation.sn_record.number=0123456}
- lu {conversation.sn_record.tbl_code=REQ}{conversation.sn_record.number=0123456}
> entity definitions:
# regex sn_record tbl_code, number = /([a-z]{2,4})([0-9]{7})/mi
The Issue I'm Having
I don't know how to get the values back from the individual capture groups. I would like to have them separate so that I can determine which table needs to be queried. I could probably just use the entire match and the search API in ServiceNow for the whole record string, but I would still like to know how to use capture group values.
I'm currently using turn.recognized.text, but I don't think this is the best method for what I'm looking to do. This returns the entire regex match.
I'm very new to this framework, so please be gentle. :) Let me know if there is more information I can provide.
Thanks all.
Best Regards,
Josh
I was able to figure this one out using the examples in the ToDosSample bot.
The answer was to use named capture groups and then add them to a dialog property to use in the corresponding dialog.
For reference here are the changes I had to make:
New Regex
(?<sn_record>(?<tbl_code>[a-z]{2,4})(?<numbers>[0-9]{7}))
New Dialog Properties
dialog.sn_record = #sn_record
dialog.sn_tbl_code = #tbl_code
dialog.sn_numbers = #numbers
New response
- Okay, looking up ${dialog.sn_tbl_code}${dialog.sn_numbers}

New intent in LUIS for multi turn dialogs

I am going to create a multi-turn dialog. I didn't get how it should be connected with LUIS models. I checked out documentation, but there are samples with only one turn dialogs. Also, I use Virtual Assistant template.
I want to do something like this.
User: I want to book a flight
Bot: What is the destination?
User: London
Bot: When?
User: 21st of September.
Bot: The ticket was bought.
The questions are what happens on the second step? Should I check out dispatcher? Should I add all possible phrases for all steps inside the intent?
General LUIS stuff
For your LUIS model you will need your intents - BookFlight and None. Under your BookFlight intent you will have your Utterances - all the phrases you want to be able to trigger the BookFlight intent.
MyLuisApp
--BookFlight
----I want to book a flight
----Book a flight
----I need a plane ticket
----etc
--None
----Utterances that don't match any of your intents
The none intent is VERY important as per this documentation.
Adding this functionality to a new bot or the core bot template
There are a couple different samples provided on how you could achieve this, but the best way is using Dialogs. What you want is a Waterfall Dialog. Inside this Dialog you can define each stage in the waterfall e.g. Ask for destination, ask for date etc.
In order to trigger the BookFlight waterfall you would have a MainDialog that handled every request, and checks with the LUIS dispatcher link1 and link2 to find out the users intent as per this example. If the intent is BookFlight then you would start the BookFlightDialog which contains the book flight waterfall.
...
// Check dispatch result
var dispatchResult = await cognitiveModels.DispatchService.RecognizeAsync<DispatchLuis>(dc.Context, CancellationToken.None);
var intent = dispatchResult.TopIntent().intent;
if (intent == "BookFlight")
{
// Start BookFlightDialog
await dc.BeginDialogAsync(nameof(BookFlightDialog));
}
General Waterfall Dialog stuff
You'd define your steps as something like:
var waterfallSteps = new WaterfallStep[]
{
AskDestinationAsync,
AskDepartureDateAsync,
ConfirmStepAsync,
FinishDialogAsync,
};
For your scenario there is actually a sample that has already been created with the BookFlight intent available here. There is a full guide on how to get this setup and working in the official documentation. So you can test to see how everything works then modify it as you need.
Other interesting links:
Custom prompt sample - roll your own.
Multi-turn sample - waterfall dialog.
Virtual Assistant stuff
Once you understand how the above works you will be able to modify the Virtual Assistant template to handle the BookFlight intent by taking the following actions:
Adding a BookFlight intent to your existing LUIS DISPATCH app that is connected to your VA template.
Adding utterances to the BookFlight intent.
Save and train your LUIS app.
Publish your LUIS app.
Running the update_cognitive_models.ps1 script as per step 3 of the instructions here which will pull down the changes (your new intent and utterances).
.\Deployment\Scripts\update_cognitive_models.ps1 -RemoteToLocal
NOTE: This command must be run using PowerShell Core and from the root of your project Directory, i.e. inside your Virtual Assistant folder.
The result of running this script should be a bunch of files created locally, as well as the DispatchLuis.cs file being updated to include your new intent. You should also check the Summary.html file that is created to see that your new intent is there. You will now have to update the VA code to actually do something when your new intent is triggered - add another if/case statement inside the RouteAsync method of the MainDialog.cs file - see here for an example.
Something like this:
MainDialog.cs
protected override async Task RouteAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
{
// Call to dispatch to get intent
if (intent == DispatchLuis.Intent.bookflight)
{
// Start BookFlightDialog
await dc.BeginDialogAsync(nameof(BookFlightDialog));
}
...
}

How to add a Dialog in a bot using SDK4.0

I am trying to implement a bot which uses Qna services and Azure search.
I am taking help of the C# QnA Maker sample github code.
It is using a BotServices.cs class which is taking a QnA service in its constructor. This Botservice object is being passed to the QnABot class constructor.
I want to use Dialog set in QnABot's constructor which need accessors to be added. I really didn't understand how to add accessor class and use them in the startup.cs
I tried to copy some code from other samples but didn't work.
Please help me to add an accessor to the BotServices constructor so that I can use dialog sets inside of it.
I would like to extend the QnA sample for my purpose.
Can you tell us why you want to pass a dialog set tot the botservices class? this class is only used to reference external services such as QnAMaker and LUIS. If you want to start a Dialog, do so in the OnTurnAsync method of the QnABot.cs class. keep in mind that the this method as it is created in this specific sample will send a response on every message the user sends even if they are working through a dialog. You could change the OnTurnAsync in such a way that the first step in the dialog is to check the QnAMaker. See the enterpriseBot sample to see how to start a dialog as well as adding an accessor to a child Dialog. see the following snipped from the MainDialog.cs class how they added the accessor:
protected override async Task OnStartAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
{
var onboardingAccessor = _userState.CreateProperty<OnboardingState>(nameof(OnboardingState));
var onboardingState = await onboardingAccessor.GetAsync(innerDc.Context, () => new OnboardingState());
var view = new MainResponses();
await view.ReplyWith(innerDc.Context, MainResponses.Intro);
if (string.IsNullOrEmpty(onboardingState.Name))
{
// This is the first time the user is interacting with the bot, so gather onboarding information.
await innerDc.BeginDialogAsync(nameof(OnboardingDialog));
}
}

LUIS - Can I have 2 languages (Chinese and English) in same App, and still have good result?

I am currently using MS LUIS for Chatbot.
Our country usually talks and chats using 2 languages, English and Chinese.
However, in LUIS I can only define one culture.
As a result, when my culture is set to English, and when I imports Chinese text, the confidence level is very low (e.g. English - 0.88, Chinese - 0.1). The other way round is the same.
The situation is the same even after I did tokenize the Chinese text using library like JieBa or THULAC.
Therefore when I did testing, it is usually very easy to fall into an unrelated intent.
I would like to make LUIS recognize both English AND Chinese easily. Are there any way to solve this problem?
Thank you very much for your help.
I would like to make LUIS recognize both English AND Chinese easily. Are there any way to solve this problem?
Yes, the way is to separate your LUIS apps/projects, 1 for each language, and use a language detection before calling LUIS.
That's the official approach from LUIS docs (see here):
If you need a multi-language LUIS client application such as a chat
bot, you have a few options. If LUIS supports all the languages, you
develop a LUIS app for each language. Each LUIS app has a unique app
ID, and endpoint log. If you need to provide language understanding
for a language LUIS does not support, you can use Microsoft Translator
API to translate the utterance into a supported language, submit the
utterance to the LUIS endpoint, and receive the resulting scores.
For the language detection you can use Text Analytics API from Microsoft Cognitive Services for example to get the text language, and then with this result query the right LUIS app.
How to use it?
Documentation of Language detection in Text Analytics API here
Text analytics API: here
As Nicolas mentioned above you can use Multilanguage chat application with separate LUIS apps for each culture.
In order to have single LUIS application, you could use the Translator Text API to translate all incoming messages before they're sent to LUIS. In this case you'll want to use middleware to handle the translation before your LUIS Recognizer is called. You can also use middleware to translate your bot's response so you don't have to use additional localization inside of your bot
Tokenizing in LUIS is different for each language in LUIS.
In the zh-cn culture, LUIS expects the simplified Chinese character set instead of the traditional character set.
Here is one more sample where you can select a language from the bot and continue the consversation as required.
After investigation and several testings, I guess I found a way on how to do that properly:
First of all, I am currently using MS BotFramework - NodeJS (3.14.0) version to create my Bot.
And in botbuilder class, you have a function called IntentDialog, it accepts a list of recognizers. So I wrote something like this:
In luis.js
const builder = require("botbuilder")
// Setting for LUIS.ai
// Universal API key for both apps
let luisAPIKey = process.env.LuisAPIKey;
// First assign variables for Chinese LUIS app
let luisAppId_Chi = process.env.LuisAppId_Chi;
let luisAPIHostName_Chi = process.env.LuisAPIHostName_Chi || 'westus.api.cognitive.microsoft.com';
let LuisModelUrl_Chi = 'https://' + luisAPIHostName_Chi + '/luis/v2.0/apps/' + luisAppId_Chi + '?subscription-key=' + luisAPIKey + '&verbose=true';
// Then assign variables for English LUIS app
let luisAppId_Eng = process.env.LuisAppId_Eng;
let luisAPIHostName_Eng = process.env.LuisAPIHostName_Eng || 'westus.api.cognitive.microsoft.com';
let LuisModelUrl_Eng = 'https://' + luisAPIHostName_Eng + '/luis/v2.0/apps/' + luisAppId_Eng + '?subscription-key=' + luisAPIKey + '&verbose=true';
// Return an object with 2 attributes: Chi for Chinese LUIS and Eng for English LUIS
let luis = {};
luis.chi = new builder.LuisRecognizer(LuisModelUrl_Chi);
luis.eng = new builder.LuisRecognizer(LuisModelUrl_Eng);
module.exports = luis;
In app.js
const luis = require("./luis")
builder.IntentDialog({ recognizers: [luis.chi, luis.eng] });
And when I tested in botEmulator, it seems that it will first check LUIS Chi app, and then go to LUIS Eng app.
I don't know what is the criteria/threshold that this recognizer used to controls whether jumps to another app or not. But at present it works for me to certain extend. It is not accurate by at least a good(?) start. :D
No MS Text translation API need.
By the way, the code will look nicer if I can get the topIntent and LUIS path right in session variable.
Hope it helps someone.

MS BotBuilder : How can I set a combination of an intent and an entity to trigger a dialog?

I'm using the MS BotBuilder to create a bot language understanding bot. I have a dialog readProfile that's triggered on Read intent that is trained on LUIS.
bot.dialog('readProfile', [
function (session, args) {
var entities = args.intent.entities;
console.log("entities : ", entities)
]).triggerAction({
matches: 'Read'
}).cancelAction('cancelReadProfile', "Ok.", {
matches: /^(cancel|nevermind)/i
});
The LUIS model is trained to recognise entities like Profile and others so I do get the entity in console.
However, I wish to trigger the dialog only if the entity recognised is Profile. I can set some logic to work only when the entity in args is Profile but wondering if there's a builtin / more elegant way to do this.
Thanks for your input.
I think using a logic statement in the first step of the readProfile dialog is the best way to do this. If no Profile entity is found, end the dialog with a message like "It looks like you're trying to read a profile, but didn't I couldn't figure out what profile you're trying to read." This has the advantage of giving the user some feedback about their action and helping them figure out what they need to fix.
You could try to train the Luis model to have a strong correlation between having a Profile entity and the Read intent. Enter a few utterances that are really close to the Read intent but don't include a Profile and mark them with the None intent. That doesn't guarantee that it won't ever match a Read intent without a Profile, though, so I'd still recommend the above step.

Resources