How to: Natural Language Processing and the Microsoft Bot Framework
Tags: ai, beginners, csharp, chatbots
Published: 2021-09-16 20:21:14

Hello and welcome to another one of my tutorials about the Microsoft Bot Framework.

This time we will be diving into Natural Language Processing (NLP) using luis.ai and integrating this with a chat bot built using the Microsoft Bot Framework v4.

You can find the code to the project on my [GitHub](https://github.com/Albert-Bennett/NLPBot)

**Introduction:**
I feel like the topic of Natural Language Process might need a small explainer before going any further. What NLP does is that it takes natural language, and derives elements from it. Elements such as entities, sentiment or in the case of this tutorial intent.

**Step 1:**
We are going to start setting up our environment. First we will need to create a new resource group in Azure or use an existing one. I had one already setup in my subscription so... I used that one.

Next we need to create the Cognitive service resource in luis.ai. This can be done by going to [luis.ai](https://www.luis.ai/) and clicking on "Choose a different authoring resource" after we have associated our subscription with luis.ai.
Next we are going to be doing the very exciting task of filling. out. the. form!
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fi06xzqlmxt5cxbxd4zw.png)
You should now be able to see a new resource in your resource group.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lg6fs1hir7hmusewv9bz.png)
I had a little trouble with setting this one up which is why the name of the resource in both snapshots is different :(
And now finally we need to create the app which we are going to use to detect the intent of phases sent to luis.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgha1jxenyfw1jsmfydy.png) 
<br/>

**Step 2:**
Before we start step 2 there is normally a step in-between where we gather up a list of intents we want our chatbot to be able to interpret. For this example I only want the one intent to be interpreted from a phase that being "Tell me a joke".  
To do this we need to start by creating an intent. See snapshot below:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ax61skug6e5p87vbuq0.png)
The intent is essentially what do we want to action upon in the phase. In our example is is to tell a joke. We can see this later in our C# code when we write the chatbot. After creating the intent we need to supply luis with a set of test phases (utterances) which relate to that intent. The aim is to write at least 10 utterances for our luis app to get an accurate-ish result. See below for a list of examples that I had used to train my app. 
```txt
i need a joke
give me a joke
a joke please
make me laugh
get me a random joke
i want to hear a random joke
i want to laugh
i want to be told a joke
tell me a joke
```
The very next thing we need to do is to train the app. This can take a short while to do, we do this by clicking on the train button in the top right hand corner.
From here we need to start testing the app. Now I know what you are thinking, testing with any random string such as 'I want an apple', 'I don't want a joke' and 'aoisdfoisnoifn' the app would be able to pick up on this as being outside of the intent and not interpreting the phase as out intent. And... you'd be wrong because our app has only been trained to return 'tell me a joke' as an intent. What we need to do next to is to train the model to return phrases outside of our intent as having an intent of 'None' (this a default intent set up when you created your app in luis). This can be done by testing the app and changing the intent of a test phase and then re-training the app.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yq3s8rgs9k62sbygsp8o.png)![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6qmykmh64qzmxe7bpzdn.png)![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qqppyawpicn6qhpj4zkg.png)
You can take your time with this step and be throughout but, you can go back over this again at any time when you get an incorrect intent from an input. Finally before going onto step 3, we need to publish our app. It's very easy just click the publish button and pick either production or staging slot. For the sample, I chose the production slot. The slots are there to rollback/ revert changes if the app had been trained incorrectly, or you need to test changes that were made to the app.
<br/>
**Step 3:**
Our next step is to integrate luis with our chat bot.
I've gone over how to create a chatbot in C# before you can see the tutorial [here](https://dev.to/albertbennett/how-to-adaptive-cards-and-the-ms-bot-framework-25km), kerchow!
To start us off after the basic chat bot has been setup we need to install the NuGet package: Microsoft.Bot.Builder.AI.Luis
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q0ojc6vai5r3pgrstw6.png)
From here it is a simple process of integrating with the library. 
However, before we start the C# coding we need to update the appsetting.Development.json file with our keys and endpoints from the luis app. Ideally we could use something like keyvault or a secrets store to store our secrets but... it's just a sample. 
1. The LuisAppId and LuisEndpoint come from the 'Azure Resources' section of your luis app.
![luis keys](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hgt7vkbmjzvauoe5ymn.PNG)
2. The LuisEndpointKey comes from the settings tab of you luis app.
![azure keys](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zxcbpt6i9d9ejur0q3wq.PNG) 

First off, our LuisInterpreterService is going to be used send the users input to our luis app to try and interpret an intent from it. Here is the code:
```c#
 public sealed class LuisInterpreterService : IRecognizer
    {
        LuisRecognizer luisRecognizer;

        public LuisInterpreterService(IConfiguration configuration)
        {
            //We need to create a new Luis Application object to connect our Luis app to our bot.
            var luisApp = new LuisApplication(configuration["LuisAppId"],
                configuration["LuisEndpointKey"],
                configuration["LuisEndpoint"]);

            var recognizerOptions = new LuisRecognizerOptionsV3(luisApp)
            {
                PredictionOptions = new Microsoft.Bot.Builder.AI.LuisV3.LuisPredictionOptions
                {
                    IncludeInstanceData = true,
                }
            };

            luisRecognizer = new LuisRecognizer(recognizerOptions);
        }

        public async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, CancellationToken cancellationToken) =>
            await luisRecognizer.RecognizeAsync(turnContext, cancellationToken);

        public async Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken) where T : IRecognizerConvert, new() =>
            await luisRecognizer.RecognizeAsync<T>(turnContext, cancellationToken);
    }
```
Next we need to inject our LuisInterpreterService into the chat bot. Its the same way as you would with any other dependency injected service in a C# application.
```c#
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers().AddNewtonsoftJson();

            // Create the Bot Framework Adapter with error handling enabled.
            services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

            services.AddTransient<IRecognizer, LuisInterpreterService>();

            // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
            services.AddTransient<IBot, LuisBot>();
        }

```
Finally we need to inject the LuisInterpreterService into the chat bot class and send on the users input.
```c#
public class LuisBot : ActivityHandler
    {
        IRecognizer _luisRecognizer;

        public LuisBot(IRecognizer luisRecognizer)
        {
            _luisRecognizer = luisRecognizer;
        }

        protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            var recognizerResult = await _luisRecognizer.RecognizeAsync(turnContext, cancellationToken);
            var replyText = recognizerResult.Intents.FirstOrDefault().Key;
            await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
        }

        protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            var welcomeText = "Hello and welcome!";
            foreach (var member in membersAdded)
            {
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
                }
            }
        }
    }
```
With this step done we can start playing round with our bot :)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0gjefjuqkc6gotx3qhy.png) 
Ok, so visually... it's not that impressive, all the bot returns to us is the intent in plain text that it has interpreted instead of an actual joke. But, think of what it has done. To take natural language not something structured or even really feed to it and, it was able to contrive the correct intent that we wanted from it. Something that we could action upon.
<br/>
Of course with most things you can add multiple intents to you app and with this code you can get luis to determine many other factors from the users input to the bot such as entities which are objects in the phrase, sentiment analysis, etc you just need to update your luis app and well write the code to handle those new elements.
<br/>
I hope this helps you to better understand how to integrate NLP and the Microsoft Bot Framework. Feel free to reach out if you have questions.
You can also connect with me on [linkedin](https://www.linkedin.com/in/albert-bennett/) as well, if you wanted to.