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 Azure Cognitive Services, 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 solution with the code that results from completing the steps in this exercise. You can use this solutions as guidance if you need additional help as you work through this exercise. Remember that for using it, you first need to run npm install
and complete the values of the LUIS Model in the .env
file.
The following software is required for completing this exercise:
- Latest Node.js with NPM
- A code editor like Visual Studio Code (preferred), or 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.
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’ll 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. If entities are the nouns, the intent is the verb.
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 bot. LUIS learns from these utterances and your bot 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. Copy and save for later use the Endpoint url generated.
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
app.js
file you've obtained from the previous exercise. Alternatively, you can open the file from the exercise2-TicketSubmissionDialog folder. -
Update the
.env
file adding the following line, complete the LUIS_MODEL_URL key with the value obtained from the previous task.LUIS_MODEL_URL=
-
Add the LuisRecognizer into your bot by adding this line after the bot initialization (
new builder.UniversalBot(...)
). Out of the box, the Bot Builder SDK comes with a LUISRecognizer class that can be used to call the machine learning model you’ve trained using the LUIS portal. That class has a function namedonEnabled
where you can conditionally enable/disable the recognizer. It is usefull when you know you will not need LUIS extract intents and entities, like when the bot prompts the user and is waiting for a response. You can check more info here aboutonEnabled
function. You also can use the onFilter function to filter the output from the recognizer.var luisRecognizer = new builder.LuisRecognizer(process.env.LUIS_MODEL_URL).onEnabled(function (context, callback) { var enabled = context.dialogStack().length === 0; callback(null, enabled); }); bot.recognizer(luisRecognizer);
NOTE: Intent recognizers interpret the user’s intent based on user input. Once the intent has been determined, recognizers will return a named intent that can be used to trigger additional actions and dialogs within the bot. Be aware that the recognizer will run for every message received from the user.
Now you will refactor the waterfall steps from exercise 2 into new dialogs that will be triggered by the LUIS intents. Dialogs help you encapsulate your bot's conversational logic into manageable components. A dialog can be composed with other dialogs to maximize reuse, and a dialog context maintains the stack of dialogs that are active in the conversation at any point in time. A conversation that comprises dialogs is portable across computers, which makes it possible for your bot implementation to scale.
-
Register a new empty dialog named
SubmitTicket
. Like the bot initialization, we can pass to the dialog the existing waterfall. Move the waterfall step in which the bot ask for severity, category and confirm the data entered and the last one which hit the ticket API. You must have a similar code block as follow.bot.dialog('SubmitTicket', [ ... ]) .triggerAction({ matches: 'SubmitTicket' });
NOTE: Notice that the
matches
value should match the name of the Intent in the LUIS app. -
Move all the Waterfall steps from the UniversalBot initialization into the
SubmitTicket
new Dialog. Replace the code with the following.var bot = new builder.UniversalBot(connector, (session) => { session.endDialog(`I'm sorry, I did not understand '${session.message.text}'.\nType 'help' to know more about me :)`); });
-
Now retrieve the entities values for category and severity from LUIS and store them in the
dialogData
for later use. Finally, if the severity is already saved, we call the next step, otherwise prompt the user to choose one. To do this, replace the first waterfall step with the following code.... (session, args, next) => { var category = builder.EntityRecognizer.findEntity(args.intent.entities, 'category'); var severity = builder.EntityRecognizer.findEntity(args.intent.entities, 'severity'); if (category && category.resolution.values.length > 0) { session.dialogData.category = category.resolution.values[0]; } if (severity && severity.resolution.values.length > 0) { session.dialogData.severity = severity.resolution.values[0]; } session.dialogData.description = session.message.text; if (!session.dialogData.severity) { var choices = ['high', 'normal', 'low']; builder.Prompts.choice(session, 'Which is the severity of this problem?', choices, { listStyle: builder.ListStyle.button }); } else { next(); } }, ...
-
Update the code receive and save the severity of the ticket. If the category is already understood the next step is called, otherwise the bot prompts the user to type it. To do this, replace the second and third waterfall steps with the following code. The fourth and fifth waterfall steps should remain unchanged.
... (session, result, next) => { if (!session.dialogData.severity) { session.dialogData.severity = result.response.entity; } if (!session.dialogData.category) { builder.Prompts.text(session, 'Which would be the category for this ticket (software, hardware, network, and so on)?'); } else { next(); } }, ...
-
In the new third waterfall (previously the fourth one), update the if statement as shown below.
... (session, result, next) => { if (!session.dialogData.category) { session.dialogData.category = result.response; } var message = `Great! I'm going to create a "${session.dialogData.severity}" severity ticket in the "${session.dialogData.category}" category. ` + `The description I will use is "${session.dialogData.description}". Can you please confirm that this information is correct?`; builder.Prompts.confirm(session, message, { listStyle: builder.ListStyle.button }); }, ...
-
Finally create a new Help dialog that will be executed when the user types help or hi.
bot.dialog('Help', (session, args, next) => { session.endDialog(`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_.`); } ).triggerAction({ matches: 'Help' });
-
Run the app from a console (
nodemon app.js
) and open the emulator. Type the bot URL as usual (http://localhost:3978/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.