Deploying a bot with the Microsoft Bot Framework

Introduction

So far in my posts/videos on the bot framework we have been using the emulator to communicate with our bot. To finish off the basic tutorials for the bots we are now going to connect our bot to another channel, in this case Slack.

Connector Service

The Bot Framework’s Connector Service allows us to easily integrate our bots with various applications. To do this we need to register out bot on the Bot Framework site. In this post we will just be setting up our bot for dev purposes so wont worry too much about Icons and site details for help etc.

The first thing we need to do is host our application somewhere. I am just using a basic Azure App for this. We will need the endpoint details shortly when we register our bot.
Azure Publish
When you register a new bot you need to give it a name and handle. I am just going to use the same for both here.
Capture1
Next we configure the endpoint ensuring to add api/messages on the end. For the App ID and password press the button and you will be taken to another window to access/generate these.
Capture4
Bot ID and PW
One important note when generating the password is that you will not be able to access that again. If you don’t save the password immediately in your bot you will need to regenerate a new password.

Just like many app stores you will need to provide some details on the publisher such as support details and terms of use. These are important when you are pushing out a live application, for development purposes we just need to ensure they are in the valid formats.
Publisher Profile
If you intend to use App Insights then you can configure this at the end of the form. Then all that remains is confirming you have read the terms and privacy and then press Register. Capture7
You will now be navigated to your apps page. You will notice that the channels for Skype and Web Chat are already enabled. You don’t need to press the Publish button when working in Dev so this can be left for now. You can press the Test button on the right of the page to confirm connectivity to your published bot.
Capture8

Slack

When you have registered your bot you will be able to see a list of available channels. Each one has different configuration steps based on their own systems requirements. We are going to go through using Slack for this demo. Slack is a great chat application that works great if you need to section people into teams.  Capture9To publish your bot to Slack you need to setup a Slack account so you can administer your team and also have an account in api.slack.com which will allow you to develop Slack applications.
Capture10
When you choose to connect to a Channel you follow a basic set of instructions from the bot framework site. These are usually just steps on what to do in the other application. For Slack you are asked to create a new App which is what we do here.
Capture11The bot framework wizard shows you what information you need to input into Slack. So in our case we need to copy the redirect uri into the appropriate part of the Slack configuration form. Capture12Once we have done this we need to create a bot user, this is the handle we are going to use to invite and call our bot .
Capture14The next step is to copy the App Credentials into the appropriate boxes in the form on the bot framework site. When you authorise the bot, you will be prompted by Slack to confirm that you are happy for the bot framework to make these changes.
Capture18Capture19You will now be able to use your invite your bot into your group on slack and ask it your normal questions. In slack all bots are considered non-sentient users, so they respond to you but don’t initialise conversations.
Capture20Capture21
We now can test this by asking a couple of our bot questions.
Capture22One problem we encounter is that if you have prompts, you need to configure slack to allow interactive messages.
Capture23
If we go back to our app in api.slack.com we can enable interactive messages.
Capture24 We can now run our commands which contain prompts without problem.
Capture25
What next?

I see this as the last of the basic tutorials for the bot framework, we can now build bots using dialogs, LUIS, prompts and publish them to a channel. You can experiment with other channels and the process is quite straight forward for each of them.

Adding prompts to a Microsoft bot

Introduction

In recent posts I have covered the basics of creating a bot. One of the last features that was added was the ability to remove a team from a competition. In a normal desktop or web application, basic user experience rules would have us ensure that the user was prompted to confirm such a decision. Our bot didn’t have this feature, which would increase the possibility of users mistakenly deleting data. In todays post we are going to look at adding prompts to our bot.

Basic yes or no questions

The first prompt we will look at is a basic confirmation. This prompt will allow the user to respond with Yes or No. As some of the channels we can connect our bot into allow for buttons, we will automatically get these displayed. We will see these in the image from our emulator later in the post, but we need to remember that depending on channel used (i.e. SMS) this may not be available to the user.

To implement our prompt we call PromptDialog.Confirm. We need to provide the context, the name of a suitable Action and also some options for how we what we will display in the prompt. In this example, for the options we just provide a caption for the dialog. In the example later in the post we will create a more complete set of PromptOptions.

private string TeamName;

[LuisIntent("RemoveTeam")]
public async Task RemoveTeam(IDialogContext context, LuisResult result)
{
    EntityRecommendation rec;
    if (result.TryFindEntity("TeamName", out rec))
    {
        TeamName = rec.Entity;
        if (champs.DoesTeamExist(TeamName))
        {
            PromptDialog.Confirm(context, RemoveTeamPromptAsync, 
                    $"Are you sure you want to delete the team { TeamName }?");
        }
        else
        {
            await context.PostAsync($"The team {TeamName} was not found.");
            context.Wait(MessageReceived);
        }
    }
    else
    {
        await context.PostAsync("An error occured. We were unable to remove the team.");
        context.Wait(MessageReceived);
    }

}

In the RemoveTeamPromptAsync method we created we need to add the code for handling the users response. For a confirm dialog the result is always going to be a bool, so we can await this result and use that to determine the action to carry out. If the user selects or states No then no action is taken and the user is informed of this. At the end of the method we have the IDialogContext await the next message.

private async Task RemoveTeamPromptAsync(IDialogContext context, IAwaitable<bool> result)
{
    if (await result)
    {
        champs.RemoveTeam(TeamName);
        await context.PostAsync($"{TeamName} has been removed from the championships.");
    }
    else
    {
        await context.PostAsync($"OK, we have not deleted them.");
    }
    context.Wait(MessageReceived);
}

We can test our code now in the Emulator and see how the prompt is displayed to the users.

Simple Prompt

Custom prompts

Although Yes/No prompts are great, sometimes you want to provide a more specific set of options. Imaging creating a booking application and the user asks to book an appointment. You would not respond back with a yes or no question of if they want to book unless you had more details. So rather than requiring the user to specify a time initially, you could offer them a list of available times via a prompt.

In the example below I have added a new feature to my Championships class that returns the top three teams. This is a simple example, but provides enough to show the necessary steps. The LUIS application has also been updated to have a new Intent called RemoveGoodTeam. This allows the user to inform the bot to “Remove a good team” and the bot will return a list of the top three teams and ask the user which it should remove.

Outside of the bot code, I have called the GetTopThreeTeams from my sample app and assigned to result to a List this will be used in the prompt options. We now create PromptOptions object which we will use with our PromptDialog. Within these options we can provide a number of settings. In this example not only do I provide the prompt for the user as in we did in our earlier example, but I also provide messages for incorrect entries, too many attempts, the list of options to display (taken from our GetTopThreeTeamsMethod) and finally how many attempts the user gets.

The final line of the code calls the PromptDialog but this time instead of Confirm we call Choice.

[LuisIntent("RemoveGoodTeam")]
public async Task RemoveGoodTeam(IDialogContext context, LuisResult result)
{
    List goodTeams = champs.GetTopThreeTeams();
    PromptOptions options = new PromptOptions("Select which of the top teams to remove",
            "Sorry please try again", "I give up on you", goodTeams, 2);
    PromptDialog.Choice(context, RemoveGoodTeamAsync, options);
}

The code for our action is pretty much the same as our first example, with the notable exception that we are awaiting a string and not a bool. The string value will be one of the options we provided earlier. The bot will only return one of our specified options, so we don’t have to worry as much about invalid entries as we would have done if the user had sent the request themselves.

private async Task RemoveGoodTeamAsync(IDialogContext context, IAwaitable<string> result)
{
    string res = await result;
    if (champs.DoesTeamExist(res))
    {
        champs.RemoveTeam(res);
        await context.PostAsync($"{res} has been removed from the championships.");
    }
    else
    {
        await context.PostAsync($"The team {res} was not found.");
    }
    context.Wait(MessageReceived);
}

When we test this in the emulator we can see the options now displayed instead of the Yes/No prompts. If we typed in a team name that was not on the options list, the bot would return the message we entered for incorrect entries. After the specified number of attempts had failed, the user would have to re ask the bot to remove a good team.

CustomPrompt

What next?

So far in the last three posts we have created a basic bot, added natural language support with LUIS and now provided prompts and questions. Our next stage is to deploy our bot outside of our local environment and connect up to some of the channels available.

Using Natural Language in a bot with LUIS

Introduction

Bots may be great, but one thing that can take up a lot of a developers time, is coding for as many variations of input as they can think of. Even with all this work, some customers may find that the bot does not recognise what they are entering. A great way to handle this is to use a natural language service, so you can leave the variations of language to that. As part of the Microsofts Cognitive Services, they have developed a system called Language Understanding Intelligent Service. Or just simply LUIS. In this blog we are going to create a simple LUIS application and integrate it with a bot. For those people who read my last post, we are simply using the same bot but creating a new LuisDialog to replace our old Dialog.

Create a LUIS application

Much of our work with LUIS will be done within the browser. From the LUIS site we are able to create and configure our applications. We will also use this site to train our applications, which is something we will cover later on in this post. For now we are going to create our application.

If you navigate to the LUIS site luis.ai you will be taken to your “My Applications” page. If you are logging in for the first time, you will need to register with a Live account.

Within My Applications we have the option to create a new App. When you select this, a dialog will appear and you will need to enter an application name. I will be sticking with the English application culture for this one, mainly as that is the only language I can do more than just order food and beer in. When you press Add App you will need to wait for a minute or two whilst the application is built.

Add New Application

Once the application has been created, the browser will navigate to that applications page.

Application Area

Responding to the user – Intents

In a LUIS application each different type of command you want to raise based on the users input needs to be created as an Intent. When you create an Intent you need to provide a name for it and an example utterance.

The name should be named with the same care as a class/method name in your code. This will be used in the bot code to identify the intent, it is also how you will train and configure your application within LUIS.

An utterance is an example of what a user might say when communicating with the service. In the example below we have an intent that provides the number of teams called TeamCount. The example utterance we provide is “How many teams are there”. This is a natural way to ask for this information, it is not the only way however, so we will want to add some more later on.

Create New Intent

Once we have created an Intent, we can add more utterances from the application page. To do this, from the “New utterances” tab enter a new expression in the text box and press the arrow key. You will now have the option to select which intent this utterance should be assigned too. Note that there is always an intent called “None”, this indicates that the application does not understand what has been asked. This will be used in our code for a simple message back to the user saying that we don’t understand their question.

Adding utterances

Training and publishing

Before we can use our app we need to publish it. But before we can do that we need to press the Train button. This takes all the information on utterances that we have entered and does its magic in the background.

Training

Once trained, we can then select Publish and we a dialog similar to the one below will be shown. Its worth noting that you can test your application by just entering text into the Query field and pressing enter. This will open a new page in your browser with the JSON return.

Capture6

Getting the keys

So we are have our application built, trained and published, what we need to do now is link it to our bot code. This is actually relatively simple, but what we need from the LUIS site is the App Id and our own subscription key. These will be copied into the LUISDialog we create. You can get your App Id from the App Settings page.

App Key

The subscription key can be found on your main account settings page. When you sign up for LUIS you get a subscription key that allows you to use apps for development purposes. If you need more calls than this will allow, you can purchase a key from Azure and add this to your application.

Sub Key

Updating your bot with a LuisDialog

OK, for a blog that has the C# tag it has taken us some time to get to code. But here we are. What we are going to do is create a new LuisDialog inside of a bot project. I am not going to go through the bot application as this was covered in my last post. For now, we just create a new class within our project. I have called mine ChampionshipDialogLUIS. This class extends the LuisDialog. We then add the attribute for LuisModel which has a parameter for App Id and then one for Subscription Key, these are the details we took from our application on the LUIS site.

[LuisModel("e0357190-4455-4506-8764-055b7e04a674",
    "a91e3e2044be4be99c291c54a153f3a6")]
[Serializable]
public class ChampionshipDialogLUIS : LuisDialog<object>
{

}

In a change from our standard Dialog, in the LuisDialog we create a method for each of our intents and use the attribute LuisIntent with the name of the intent as shown below. For the builtin None intent we just use the attribute LuisIntent(“”).

The LUISDialog does have some functionality already that saves us a bit of work, so now when we finish in a method we just add context.Wait(MessageReceived) and the LuisDialog takes care of listening for the next input. The remainder of the code for our TeamCount intent is the same as it would be for a standard dialog.

[LuisModel("e0357190-4455-4506-8764-055b7e04a674",
    "a91e3e2044be4be99c291c54a153f3a6")]
[Serializable]
public class ChampionshipDialogLUIS : LuisDialog<object>
{
    [LuisIntent("TeamCount")]
    public async Task GetTeamCount(IDialogContext context, LuisResult result)
    {
        Championships champs = new Championships();
        await context.PostAsync($"There are {champs.GetTeamCount()} teams in the championships.");
        context.Wait(MessageReceived);
    }
        
    [LuisIntent("")]
    public async Task None(IDialogContext context, LuisResult result)
    {
        await context.PostAsync("No clue what you are talking about");
        context.Wait(MessageReceived);
    }
}

What I have now gone and done is add a few more intents in exactly the same way as our TeamCount one. This just shows how easy it is to have the LuisDialog handle the various message we get from the bot.

[LuisIntent("TopTeam")]
public async Task TopTeam(IDialogContext context, LuisResult result)
{
    await context.PostAsync($"The highest rated team is {champs.GetHighestRatedTeam()}.");
    context.Wait(MessageReceived);
}

[LuisIntent("BottomTeam")]
public async Task BottomTeam(IDialogContext context, LuisResult result)
{
    await context.PostAsync($"The lowest rated team is {champs.GetLowestRatedTeam()}");
    context.Wait(MessageReceived);
}

Getting user entered data – Entities

The problem with what we have done so far, is that we have only handled simple requests from the users. In reality a user is going to want to do more than just ask for some predefined information, they are going to want to ask a more detailed question. For this we will bring in Entities.

Entities allow us to take part of the expression entered by the user and use this as an argument. For example, my test app provides information about a football championship, it is therefore logical that the user will want to ask about a specific team. So that is what we are going to add.

Back in the LUIS site, we add a new entity. For this we enter a name using the same care as with our intents. In my case I have called mine TeamName.

Capture7

What I have also done is create an intent that allows us to remove a specific team, called RemoveTeam. When you are entering utterances, you can select a word and declare it as an entity. This tells the application that it is for the intent RemoveTeam but that the text in this area would form an entity. This should become clear when we look at the code.

Selecting a sample entity

Back to the code

In our code we add a new method for remove team and add the necessary attribute. This method is different to the others as we now use the TryFindEntity method on the LuisResult. The first argument is the name of our entity (see this is why I keep saying about care in naming them). The output of this is of an EntityRecommendation, its easy to think of this as a recommendation as it may not always be correct and is down to the training and experience of the application. Once we have this we can get the Entity from the EntityRecommendation and use that in our code to remove the selected team.

[LuisIntent("RemoveTeam")]
public async Task RemoveTeam(IDialogContext context, LuisResult result)
{
    string teamName = "";
    EntityRecommendation rec;
    if (result.TryFindEntity("TeamName", out rec))
    {
        teamName = rec.Entity;
        try
        {
            champs.RemoveTeam(teamName);
            await context.PostAsync($"{teamName} has been removed from the championships.");
        }
        catch (TeamNotFoundException)
        {
            await context.PostAsync($"The team {teamName} was not found.");
        }
    }
    else
    {
        await context.PostAsync("An error occured. We were unable to remove the team.");
    }
    context.Wait(MessageReceived);
}

Retraining

Before we go any further, we should retrain our application from the web page. This will make sure any changes that we have made, such as our new entities are added. Once we have our application working and it is receiving requests, we can go to the Suggest tab and look at what intent the application has assigned to the input from our bot. This wont always be correct, but you can select each one and select the correct intent and add entities if appropriate. Something you will notice is that the more you use this, the more accurate it becomes.

Retraining

On the right of the web page are some statistics on how accurate the application has performed. It also shows how many utterances have been entered for an intent. If you have an intent that isn’t getting picked up as you expect, it might be worth checking to see if you have not provided enough utterances for the application to be able to accurately predict the intent.

Response Recommendations

Testing in the emulator

We are now coming to the end, so we just need to check this in the emulator. As you can see from the screenshot, the application can pick up what you trying to say and then provide the right response.

Emulator

What next

This post has covered natural language and my last post covered the basics of bots, so continuing the theme I will be writing about prompts and images in your conversations on my next post.

Creating your first bot with the Microsoft Bot Framework

Introduction

This post is actually my second on getting started with the bot framework. I’m starting again as Microsoft has provided significant updates to the bot framework with V3 and some things are now done in a different way. I will be posting several tutorials over the coming days which will take you from the basics and onto more advanced topics. Today we will just look at creating a bot with a simple dialog.

Create the project

To create a bot application you should install the Visual Studio template from here. For my example I have taken a basic solution which contained a project called FootballData. This project is just used for simple demonstrations and gives us something to allow our bot to get data in response to our input.

Create your dialog

When you first create bot project you get some sample code in the MessageController class. Rather than use this we are going to create our own Dialog. To do this you create a new class in the root of the project. I am going to call mine ChampionshipDialog. This class needs to implement IDialog and also to be marked as serializable. At this stage the dialog should look similar to this. (if you have Visual Studio generate the methods for the interface it wont mark as async, but it will be needed later so its worth adding it now.)

[Serializable]
public class ChampionshipsDialog : IDialog
{
    public async Task StartAsync(IDialogContext context)
    {

    }
}

When our Dialog is first created the StartAsync method will be called. Here we need to wait for a message from the user. Additionally what we are going to want to do is carry out an action based on the users input, and ensure that we can still listen out for more messages.

public static Championships champs;
        
public async Task StartAsync(IDialogContext context)
{
    champs = new Championships();
    context.Wait(MessageReceivedAsync);
}

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var msg = await result as Activity;
    //we will carry out our actions here

    context.Wait(MessageReceivedAsync)
}

In the code above all we have done is call MessageReceivedAsync and then get the Activity object from the bot. The Activity is where we will get the text entered by the user. In earlier versions of the bot framework the result would be of a type Message, this changed in v3 and the Activity could be of various types. In our application we are only going to call this Dialog if the Activity is of the type Message, but that will be covered in the next section. The other thing I have done is at the end of MessageReceivedAsync I have told the context to wait for further input. This ensures that our bot keeps listening and doesn’t just shut down after the first message.

Now we are in a position to add some functionality to our Dialog. First of all, we are just going to see if the text sent to the bot contains “how many teams”. If this is the message we can then call the appropriate method in our Championships class and then post back to the user the result.

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var msg = await result as Activity;
    if (msg.Text.Contains("how many teams"))
    {
      await context.PostAsync($"There are { champs.GetTeamCount() } teams in the championships.");
    }
    context.Wait(MessageReceivedAsync)
}

Just providing simple answers like that is not really much use, so what we can now add cases to respond to different questions/statements from our users. So now we go through and interpret the message and carry out actions accordingly. A more complete example is shown below.

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var msg = await result as Activity;
    if (msg.Text.Contains("how many teams"))
    {
       await context.PostAsync($"There are { champs.GetTeamCount() } teams in the championships.");
    }
    else if (msg.Text.StartsWith("who") || msg.Text.StartsWith("which"))
    {
       if (msg.Text.Contains("best") || msg.Text.Contains("top") || msg.Text.Contains("greatest"))
       {
          await context.PostAsync($"The top rated team is { champs.GetHighestRatedTeam() }");
       }
       else if (msg.Text.Contains("worst") || msg.Text.Contains("bottom") || msg.Text.Contains("lowest"))
       {
          await context.PostAsync($"The lowest rated team is { champs.GetLowestRatedTeam() }");
       }
       else
       {
          await context.PostAsync("Sorry I didnt understand the question.");
       }
    }
    else if (msg.Text.StartsWith("remove"))
    {
       string team = msg.Text.Replace("remove", "").Trim();
       try
       {
          champs.RemoveTeam(team);
          await context.PostAsync($"The team { team } has been removed from the championships.");
       }
       catch (TeamNotFoundException)
       {
          await context.PostAsync($"The team { team } was not found.");
       }
    }
    else
    {
       await context.PostAsync("Sorry I didnt understand the question.");
    }
    context.Wait(MessageReceivedAsync);
}

Don’t be too worried about the what I’m calling in the Championships class, the important thing is to look at the structure of the dialog. There are a couple of things here that I would like to point out. The first is that at the end I have added a final else statement for we don’t get any recognised input. It is important to always send a response back to the user, even if it is just to tell them that you have no idea what they are asking. The second is that we are having to a lot of checking what text says and even having or statements to allow for users using different phrases. This can add up to a lot of time trying to work out how users will ask a question. We can improve this by using a natural language service such as LUIS. Which my next post will be covering.

Wire things together

For now lets connect our dialog to our bot. If we go back to the MessagesController class and in the Post method, replace everything within the if statement for ActivityTypes.Message with the statement

await Conversation.SendAsync(activity, () => new ChampionshipsDialog());

This statement is telling the bot to use our new Dialog. The full code looks similar to this:

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
    if (activity.Type == ActivityTypes.Message)
    {
       await Conversation.SendAsync(activity, () => new ChampionshipsDialog());
    }
    else
    {
       HandleSystemMessage(activity);
    }
    var response = Request.CreateResponse(HttpStatusCode.OK);
    return response;
}

Test in the emulator

If we debug the bot service now and open up the bot emulator that can be downloaded from here. We can communicate and get the results expected. We now have our simple bot up and running.

Emulator

What next

The code in this post is a simplistic view of a bot. We haven’t covered prompts to the user to confirm an action, holding state, multiple users or many other features. I will be blogging regularly on bots and will be going through many of the more advanced features, so you can subscribe or keep checking back here for more info. The Microsoft site dev.botframework.com has some great information as well.