One of the key problems in human-computer interactions is the ability of the computer to understand what a person wants. LUIS is designed to enable developers to build smart applications that can understand human language and react to user requests.
In this exercise you will learn how to add natural language understanding abilities to the help desk bot to make it easier for users to create a ticket. To do this, you will use LUIS (Language Understanding Intelligent Service), part of the Azure Cognitive Services offering, which allow developers to build language models to allow a bot to understand commands and act accordingly. For instance, while in the previous exercise the user had to enter the severity and category, in this one, both "entities" will try to be recognized from the user message.
Inside this folder you will find a Visual Studio solution with the code that results from completing the steps in this exercise. You can use this solution as guidance if you need additional help as you work through this exercise.
The following software is required for completing this exercise:
- Visual Studio 2017 Community or higher
- An Azure subscription
- The Bot Framework Emulator (make sure it's configured with the
en-US
Locale) - An account in the LUIS Portal
In this task you will create an app in the LUIS portal.
NOTE: If you are already familiar with LUIS, you can import the file
luis_model.json
located under the assets folder of this exercise into your account, train and publish the model and continue on task 4. However, if you are new to LUIS, we recommend you work through creating the model from scratch for learning purposes.
-
Navigate to the LUIS Portal and sign in. Open the My apps tab.
-
Click New App. In the dialog box, type an application name (for example HelpDeskBot). Select the English Culture, if not already selected.
-
Choose a Key to use. If you don't select any, a BoostrapKey will be created by default.
-
Click Create. You should see an empty LUIS app dashboard.
-
Save the App ID for later.
-
Navigate to the My keys menu in the top of the page. Once you are there, save for later use the Programmatic API key you will find there.
In this task you will add entities to the LUIS app. This will allow the bot to understand the ticket category and severity from the issue description entered by the user. Entities are 'nouns' in your application’s domain. An entity represents a class including a collection of similar objects (places, things, people, events or concepts).
For the purposes of this lab, you will be using the List entity type. This allows you to create what's commonly called a "closed list", meaning that no machine learning will be applied to the terms, but rather a direct match will be used. This is extremely useful when trying to normalize terms, or to ensure certain keywords are always picked up as entities.
-
In the LUIS portal, click Entities in the left panel.
-
Click Add custom entity.
-
In the dialog that opens, type category as the Entity name. Select List as the Entity type. Click Save.
-
A new page is displayed in which you can add the possible values. To make this process faster, click the Import Lists link.
-
Browse for the categories.json file in the assets folder on the root of this hands-on lab. Once enabled click the Import button.
-
Repeat this process with a new entity named severity and populate it using the file named severities.json from the same location.
-
Now click on Train & Test in the left panel.
-
Click Train Application and wait a few seconds to complete. Whenever you make updates in your current model, you will need to train your app before testing and publishing it.
Intents are the intentions or desired actions conveyed through the utterances (sentences). Intents match user requests with the actions that should be taken by your bot. So, you must add intents to help your bot understand user requests and react to them properly.
Utterances are sentences representing examples of user queries or commands that your bot is expected to receive and interpret. You need to add example utterances for each intent in your app. LUIS learns from these utterances and your app is able to generalize and understand similar contexts. By constantly adding more utterances and labeling them, you are enhancing your bot's language learning experience.
You can read more information about intents here and utterances here.
-
In the LUIS portal, click Intents in the left panel. You will notice there is already a None intent present.
-
Click on Add Intent and a popup is shown. Type SubmitTicket as the Intent name and click Save.
-
Now, let's add the following utterances in the text box. Press enter after each one. When the user types these sentences or similar ones, the LUIS app will assume the user is trying to submit a ticket. In the Bot Framework language, this is called Intent.
- I can't log in, I'm blocked.
- I cannot print and I need to do it urgently.
- I need to request a new RAS token.
- I need to reset my password ASAP.
- I cannot open a web page and my deadline is at risk.
NOTE: You can add as many utterances as you want. More utterances you add, the better your app will recognize the intent of the users. In this particular case, the utterances that can trigger the SubmitTicket are quite diverse (ranging from hardware to software problems), so it would be ideal that the bot is trained with a considerable amount of utterances before releasing it to production.
-
Following the same steps as above, add a new
Help
Intent with the utterances help, hi and hello.NOTE: It's a good practice to add some utterances to the "None" Intent, even if it is different from other intents. Giving it training examples won't limit what text will trigger the "None" intent, but it will help the other Intents fire more accurately.
-
Train the app again as explained previously.
-
Open the Intents menu and click on the SubmitTicket intent. Check that the utterances have been recognized with the entities values.
-
Now you will publish the LUIS app to be able to use it from the bot. Click Publish App from the left menu.
-
Make sure an Endpoint key is selected. Leave the default Production slot.
-
Click on the Publish button. After a new confirmation message appears, the LUIS's app is now published.
Notice that the output of a LUIS app is a web service with an HTTP endpoint that you reference from your bot to add natural language understanding to it.
NOTE: The BoostrapKey has 1,000 transactions per month.
In this task you will update the bot code to use the LUIS app created previously.
-
Open the solution you've obtained from the previous exercise. Alternatively, you can open the Exercise2.sln solution from the exercise2-TicketSubmissionDialog folder.
-
Open the Dialogs\RootDialog.cs file.
-
Add the
Microsoft.Bot.Builder.Luis
andMicrosoft.Bot.Builder.Luis.Models
using statements.using Microsoft.Bot.Builder.Luis; using Microsoft.Bot.Builder.Luis.Models;
-
Add the
LuisModel
attribute to the class as follows. Replace the{LUISAppID}
with the App ID you have saved from the LUIS Portal and the{LUISKey}
with the Programmatic API Key you have saved from My Keys section.[LuisModel("{LUISAppID}", "{LUISKey}")]
-
Replace the implementation of interface
IDialog
to derive fromLuisDialog<object>
. Remove theStartAsync
,MessageReceivedAsync
andDescriptionMessageReceivedAsync
methods since these will not be called anymore.public class RootDialog : LuisDialog<object>
-
Create the
None
method that will execute when your LUIS model does detect any intent. Use theLuisIntent
attribute and pass the intent name as parameter.[LuisIntent("")] [LuisIntent("None")] public async Task None(IDialogContext context, LuisResult result) { await context.PostAsync($"I'm sorry, I did not understand {result.Query}.\nType 'help' to know more about me :)"); context.Done<object>(null); }
-
Add the following code to handle the
Help
intent responding to the user with a message.[LuisIntent("Help")] public async Task Help(IDialogContext context, LuisResult result) { await context.PostAsync("I'm the help desk bot and I can help you create a ticket.\n" + "You can tell me things like _I need to reset my password_ or _I cannot print_."); context.Done<object>(null); }
-
Add the following code which adds the method that handlers the intent SubmitTicket. The
TryFindEntity
method is used to determine if the entity is present in the utterance and extract it.[LuisIntent("SubmitTicket")] public async Task SubmitTicket(IDialogContext context, LuisResult result) { EntityRecommendation categoryEntityRecommendation, severityEntityRecommendation; result.TryFindEntity("category", out categoryEntityRecommendation); result.TryFindEntity("severity", out severityEntityRecommendation); this.category = ((Newtonsoft.Json.Linq.JArray)categoryEntityRecommendation?.Resolution["values"])?[0]?.ToString(); this.severity = ((Newtonsoft.Json.Linq.JArray)severityEntityRecommendation?.Resolution["values"])?[0]?.ToString(); this.description = result.Query; await this.EnsureTicket(context); }
-
Next, create the
EnsureTicket
method which will validate if any entity was identified, and if not, prompt the user to enter the missing entities.private async Task EnsureTicket(IDialogContext context) { if (this.severity == null) { var severities = new string[] { "high", "normal", "low" }; PromptDialog.Choice(context, this.SeverityMessageReceivedAsync, severities, "Which is the severity of this problem?"); } else if (this.category == null) { PromptDialog.Text(context, this.CategoryMessageReceivedAsync, "Which would be the category for this ticket (software, hardware, networking, security or other)?"); } else { var text = $"Great! I'm going to create a **{this.severity}** severity ticket in the **{this.category}** category. " + $"The description I will use is _\"{this.description}\"_. Can you please confirm that this information is correct?"; PromptDialog.Confirm(context, this.IssueConfirmedMessageReceivedAsync, text); } }
-
Update the
SeverityMessageReceivedAsync
andCategoryMessageReceivedAsync
to call back toEnsureTicket
method as follows.private async Task SeverityMessageReceivedAsync(IDialogContext context, IAwaitable<string> argument) { this.severity = await argument; await this.EnsureTicket(context); }
private async Task CategoryMessageReceivedAsync(IDialogContext context, IAwaitable<string> argument) { this.category = await argument; await this.EnsureTicket(context); }
-
Run the app and open the emulator. Type the bot URL as usual (
http://localhost:3979/api/messages
). -
Type hi. Notice how the Help intent is recognized and executed.
-
Type one of the utterances you used to train the bot. For example, I can't log in, I'm blocked. Notice that the ticket category and severity are automatically understood from the user message. Type yes to save the ticket.
-
Now try typing something that the bot was not trained for. For example: My computer is making a grinding noise. Notice that the severity is not understood, but the category was because of the presence of the entity computer.
-
If you type something that the LUIS cannot recognize, LUIS will return the None intent and the bot framework will execute the default dialog handler.
Once your application is deployed and traffic starts to flow into the system, LUIS uses active learning to improve itself. In the active learning process, LUIS identifies the utterances that it is relatively unsure of, and asks you to label them according to intent and entities. In the LUIS portal, within an Intent, you will find the Suggested Utterances section, where you can do this.
If you want to continue working on your own you can try with these tasks:
- Add a cancel event handler to the
SubmitTicket
dialog through the use ofcancelAction
. - Add a custom dialog for providing help to the user when in
SubmitTicket
through the use ofbeginDialogAction
. - Use the
onEnabled
event to ensure theSubmitDialog
completes once started, unless cancel is called. - Add the ability to the bot to ask for the status of a ticket. You would need to add a status property to the ticket and a new Intent in the LUIS app that invokes a new dialog.