From theoretical to practical bot: building out a LUIS vocabulary
This is the second article in a two-part series. Please read Part 1 before going on.
Now that I’ve introduced you to LUIS and shown how quickly and easily you can integrate LUIS and AtBot to create more intelligent skills for your Microsoft Teams bot, it’s time to take LUIS to the next level.
In that article, we kept the LUIS App simple and emphasized the flexibility you gain when adding intelligence to your bot Skills. LUIS provides a larger net to catch your users’ wide-ranging vocabulary, which makes AtBot more approachable and easier to use. More human, if you will.
Now we’re going to take the next step with LUIS and start building out some serious smarts. I’ll then show how to apply those smarts in AtBot. By the end of this article you should understand how LUIS can provide not only flexibility but also improved insights into user requests. I’ll also go over how your LUIS App can be maintained as it gets used. Just like you and me, LUIS works best when he’s constantly learning!
You will see quickly, this is a fairly big jump from the previous article. In particular, we’ll be adding a lot to our LUIS App and the Flow we build will be quite a bit more complex. Thankfully the AtBot configuration is still quite simple because, well, we built him that way on purpose.
Building multiple meanings with multiple Intents
Previously, we defined our single Intent named NewTicket in our LUIS App. This was designed to be a generic catch-all for dealing with user problems. We don’t try to identify what problem they are having; we just recognize they are having a problem.
Let’s add some more targeted Intents related to reporting problems. I chose three common help desk ticket types and defined Intents around them:
- ReportProblem_Access: Access control issues, password reset issues.
- ReportProblem_Computer: Hardware and software issues.
- ReportProblem_Facilities: Office, furniture, facilities-type issues.
The Intents themselves can be named anything, but it’s the Utterances we provide for each Intent that help LUIS understanding when parsing user messages. For example, my ReportProblem_Computer Intent has the Utterances shown below.
There are more utterances I could page through, but you get the idea. These are statements users would give to the bot when reporting an issue related to hardware or software. Contrast this with the utterances trained to the ReportProblem_Facilities Intent, shown below.
They are very different from the Utterances of ReportProblem_Computer, yet still the types of things a user might report to the help desk. I’m leveraging the Intent mechanism provided by LUIS to define classifications of the types of problems I expect to have reported. By splitting the Utterances into their three respective Intents, I can quickly identify what type of problem a user is having.
To round it out, my ReportProblem_Access Intent contains the Utterances shown below.
All three were created the same way and you can start to see the pattern here for basic design of Intents within a LUIS App. Now let’s take a look at the other part of LUIS we have not discussed yet; Entities.
Connecting the dots with Entities
You’ll notice in all three Intents, there are words highlighted in blue. These are the entities I have defined and trained LUIS to recognize in Utterances. For this demo — and a simple technique I like to use for a first pass at a LUIS App — I start with simple utterances that have to do with a single thing. Usually the statement’s direct object, though sometimes the subject (depending on sentence structure). These are great candidates for Entities.
To make this association, we first define Entities in LUIS. Going back to the Build tab in our LUIS App and clicking Entities in our left navigation, we see a similar looking interface to where we created our Intents in the previous article.
These are all Simple entity types (you can see the Type column above). A simple entity type is really nothing more than a name. You are giving a name to the type of thing you map the entity to in the Utterances. Click Create new entity and give the entity a name.
Let’s look at the ReportProblem_Computer Intent Utterances again.
Notice the toggle in the off position labeled Tokens View. If I switch the toggle on, it changes to Entities View.
Now, all the highlighted words have been replaced with the corresponding entity I flagged them as. In the case of a computer problem, I am training LUIS to recognize ComputingDevice (I probably should have used ‘Hardware’) and Software. This way I can classify a computer problem as either a hardware or software problem. This will be used by AtBot to know which ticket queue to submit to.
Let’s add one more utterance to the Intent and assign the entity so you can see how that happens. First, I provide a new Utterance in LUIS.
Once I add this Utterance, I can then mouse over the word ‘phone’ and assign it an Entity classification.
Once the entity assignment has been made, I then train and publish my LUIS App. Entity assignments require retraining just like changes to Intents, so retrain your App. Now, when we test our App in LUIS, we can try the phrase we just added and assigned an entity to.
As a tip, LUIS gets better with the more Utterances you provide and train against. For example, if I try the following you will see it detects it as a computer problem, but does not detect the phone as an Entity.
Impressively, LUIS gets the Intent correct. But it needs help identifying Entities. The more examples you give, the better LUIS gets at Entity detection. After adding the above utterance and training, a similar statement now works. We’re satisfied with the performance, so now we publish.
So, to recap what was done in LUIS:
- We added three new Intents that identify the main three types of problems users report;
- We added a minimum of ten sample Utterances in each of our three new Intents;
- We added two new Entities per new Intent (total of six) to be able to classify the reported problem within the given type;
- We assigned Entities to all utterances in all three of our Intents; and
- We trained, tested, and published our LUIS App.
Go with the Flow
Because we now have multiple Intents, we need AtBot’s Skill to acknowledge the different tasks associated with those intents. So it’s time to update our AtBot Skill in Power Automate.
We’ve got quite a bit more information coming from LUIS now, so we will be adding logic to our Flow to help identify which Intent was triggered and which type of entity was returned.
You have two options when integrating AtBot with a LUIS App. You can either define AtBot Skills (the Flows) per LUIS Intent, or you can provide a catch-all ‘*’ [asterisk] wildcard to AtBot and the Skill will fire if any of the Intents from the associated App match. In our earlier article, we configured the Skill to match the NewTicket Intent.
I now create a separate skill that handles reported computer problems.
I then detect if the entity coming back from LUIS is either Hardware or Software and submit the help desk ticket accordingly. Alternatively — and for this example — I want to handle all three Intents to keep my ticket submission logic to a single skill.
Using the wildcard (*) above, any type of reported issue will go through this skill. Let’s walk through what the Power Automate does. In particular, I will show how to parse the JSON received from LUIS and how to use conditional logic in Power Automate to identify the type and content of the help desk ticket.
After our initial AtBot trigger identifying the LUIS App and wildcard (*) Intents, we need to define three variables. These always have to be defined early on in a flow. All three variables are of type String.
We will use these variables later, populating them with values based on identified Intent and Entity.
Next, we parse the LUIS JSON sent to the Flow by AtBot using the Parse JSON action in Power Automate.
The trick is defining the schema. This is where we use the links on the Publish tab in our LUIS App. From the listing of Resources and Keys, click on the link in the Endpoint column.
That will take you to a page where you can see the JSON output from LUIS when accessing your App programmatically. The initial output looks like this:
{“query”:null,”intents”:[],”entities”:[]}
The reason for that is the default Endpoint link from that Publish tab does not include any sample utterance. You are essentially making a call against your newly trained LUIS App and sending nothing as your utterance. Put your cursor in the address bar of the page and put it at the end of the URL. You’ll see the following which represents your empty utterance.
Now, let’s add our utterance to the end of that. It should read something like this. You can use normal spaces (as opposed to %20) when typing it out. The browser will encode it.
&q=My computer will not boot up
Hit Enter and now you should get back something like this:
Copy this JSON text and click on the link in the Parse JSON action Use sample payload to generate schema. Paste in the JSON you copied and click Done.
Your schema will show up in the Power Automate action. For this example, all three Intents I’m using have the same schema. I am only defining simple Entities in all three Intents, so the “entities” collection above will always be structured the same.
As you build out LUIS Apps and Intents you may find that the Entities in each Intent start to become more unique and tailored to the Intent. That is one good reason to break out each Intent to its own Flow. You could also conditionally parse the JSON based on the Intent via a switch statement in Power Automate.
Now that we’ve got the JSON parsed, we immediately start pulling data out of it. First, we need to identify the top scoring intent. You can see it in the JSON you created for the schema earlier. LUIS always sends you what it considers as the top scoring intent based on the Utterance sent to LUIS. But you need the intent property of the topScoringIntent JSON object. We want to take that intent string and run a switch statement to do different things based on which Intent it is.
Setting up the switch statement, it does get a little tricky to identify the correct JSON parameter to use. Looking at this screen:
Notice the list to the right shows two entries labeled intent. Take another look at the JSON schema text we used.
You can see that a property named intent exists at two scopes in the JSON schema: once within the topScoringIntent object and another within the intents array as a property of each object in that array.
The visual interface in Power Automate currently flattens the property values so it can be hard to know which one to use. One great thing about Power Automate is it is very easy to save and test with AtBot. So you can try something and see how it works. If it doesn’t work as intended, you know you choose poorly.
Alternatively, there is an expression syntax you can use to make sure you are using the correct intent property. To get at the one you want, click the Expression tab and type in the following expression:
body(‘Parse_JSON’)?[‘topScoringIntent’].intent
Take the time to understand the expression. Knowing how to type these out can be very useful. Power Automate is constantly being improved so this will most likely improve at some point. In the meantime, knowing how to traverse a parsed JSON object as an expression is super handy.
Click OK beneath the text box you put the expression in and you will see a reference to your expression now stored in the switch On field.
Since we are switching on Intent, this means we are handling each type of problem differently. In my Flow, I’ve got a different track for each LUIS Intent. If fact, since LUIS is sending me the name of the Intent (the name I give it), I can perform my switch against that exact text.
You can only see two of the switch conditions here, Computer Problem and Access Problem, but know there is also a Facilities Problem condition and a Default condition. Inside of each condition, I have a scope block that has the rest of my logic around which support queue to submit to. The brown scope block above is a great way to collapse my logic and make it easier to traverse.
Looking at when it’s a Computer Problem, when I expand my scope block, you see I’m doing two things:
- We now know to assign the ticket to the ‘Devices and Hardware’ queue; and
- We check the type of the Entity identified in the Utterance.
This is where we are extracting the Entity identified by LUIS. We already know it’s a computer problem. The Entity tells us whether it’s a hardware or software problem.
The following expression syntax you see above in the Switch Computer Problem Type switch statement is:
body(‘Parse_JSON’)?[‘entities’][0][‘type’]
It targets the type attribute in the first object in the entities array in the JSON below.
Then, as you can see we assign the IssueSubType variable a simple string value of the type of Entity found. Now we’ve got all of the data we need to submit a ticket.
We know what the problem is and we know who is having the problem, so it’s time to report the problem on behalf of the user and give them feedback that we’ve been able to do that for them.
Power Automate makes it super easy to incorporate your help desk tool of choice. I’m using a simple SharePoint list to show how easily you can transfer information from your Flow variables to an intake mechanism like a SharePoint list. But Power Automate has built-in support for ServiceNow, ZenDesk, and many others. All of the same concepts apply.
Finally, we want to send a note back to the user that their ticket has been submitted. Remember, this whole process was triggered when the user reported an issue to AtBot. We were able to get quite a bit of information from the initial message they sent us — thanks to LUIS — and submit the ticket immediately without having to ask any more questions. Power Automate gives us a powerful workflow engine to handle complex tasks, and AtBot makes it feel like magic to the user.
And that’s it! You have now seen how to take LUIS to the next level and start handling complex workloads to create simple, elegant experiences for your end users. Working with LUIS is great and once you’ve got AtBot and Power Automate wired up to your LUIS app, you start to get value out of the time spent training LUIS immediately.
Optimizing your LUIS performance
Here are some final tips around next steps with LUIS that you will want to start thinking about, especially once you start making AtBot skills like this available to your end users.
Note these tips line up with the Improve app performance section in your LUIS app.
Care and feeding of your LUIS App
I hint at this throughout the article, but everyone has different ways they convey the same thing. You will inevitably have a situation where a user will report an issue, but say it in a way you did not anticipate. Not to worry: LUIS captures Utterances that did not match an Intent and gives you the opportunity to map the Utterances to Intents and retrain the LUIS app.
The above Utterances are phrases from users that are not directly trained in my LUIS App. You can see some have nothing to do with reporting issues, while others are not only valid reported issues, but Entities are highlighted.
I can see which Intent LUIS thinks each Utterance should be aligned to and I have a chance to adjust the educated guess LUIS is making. Once I’m ready, I check the box for each row I want to train and click Add selected.
Train your app and publish. The app is now that much smarter! Make a habit of doing this once a week and you will start seeing benefits quickly.
Build phrase lists
When we were building out and training our Intents, the sample utterances I gave for the ReportProblem_Computer Intent included flagging of Entities like computer, laptop, and phone. Obviously, there are many different things that should be considered hardware in a work setting. Fortunately, you don’t need to write ‘My <DEVICE> isn’t working’ 100 times for each type of device that could stop working. This is what Phrase Lists are for in LUIS.
I start by creating a new Phrase List and supplying a few example things. In this case the computer, laptop, and phone I was using during my Intent training.
LUIS starts recommending other types of related things to me on the right and I add them to the left. Before I know it, I’ve got a much more comprehensive list of things that could be reported by end users. Once I save this Phrase List and train my LUIS App, I can now interchangeably use these different words instead of the three I did all of my training with.
With Phrase Lists, my LUIS App is much more capable of detecting Entities in Utterances. To my end users, AtBot just got that much smarter; that much more conversational. Combined with routine Utterance review and retraining, your AtBot skills will get better and better at serving your end users with less and less effort on your end.
Happy training!
AtBot brings AI within reach
AtBot is the premiere bot-as-a-service solution for the Microsoft cloud. Built completely within Azure, AtBot is your out-of-the-box, easy-to-configure bot for Teams, SharePoint, or the web. Teach AtBot tasks using Power Automate, make him your corporate source of knowledge with QnA Maker, help him understand almost anything your colleagues could ask thanks to LUIS, and manage his features with the AtBot Admin Portal. Get going with AtBot Free or start your free trial of AtBot Premium today.