Developing a Bot with the Microsoft Bot Framework

Introduction

In this post we are going to look at creating a very basic bot using the Microsoft Bot Framework. Although the bot itself will be simple, we will connect it to the LUIS natural language service to allow us to train the service to handle responses. From this example, it should be easy to move forward with more complex bots and features.

Create Project in Visual Studio

To create a bot application, you need to install the Bot Application template and Bot Emulator which can both be found here.

When these are installed, create a new project using the Bot Application template. We have called our one MagicEightBall.

Bot Project

The template provides the basic code needed to run a bot. The main area you will edit is the MessagesController class in the Controllers folder. The example code as shown below will return a count of how many characters you have entered. It is a good idea to debug this and test your emulator now with this code.

public async Task Post([FromBody]Message message)
{
    if (message.Type == "Message")
    {
        // calculate something for us to return
        int length = (message.Text ?? string.Empty).Length;

        // return our reply to the user
        return message.CreateReplyMessage($"You sent {length} characters");
    }
    else
    {
        return HandleSystemMessage(message);
    }
}

Using Natural Language

To simplify the process of creating question and responses for our Bot we are going to use LUIS (Language Understanding Intelligent Service). This service allows you to train intents to understand what users have entered and then respond accordingly. The LUIS site can be found here.

Create a new LUIS application

Once you have registered for LUIS, create a new application for the Magic Eight Ball as shown below. The LUIS site has an excellent 10 minute video on creating and training an app, if you have not created one before then I highly recommend that you view this.

LUIS

Train the application to understand a question

The first thing you need to do is assign an intent, which will determine the questions that the user can ask. We have created a basic one called Question that we will train. We would then add new utterances to show what examples of text would be assigned to this intent. The LUIS server will then learn and try and pick up other text that it considers as having the same meaning.

Capture3

If we were getting entities to pass data to our code, we can assign part of our utterances to that entity. In the example below, the intent would be picked up as “Am I”, whilst the Action entity for this would be assigned to “funny”. This allows you to handle various options and settings sent by users in a statement.

Capture4

Connecting to LUIS service

The code sample below is of a MagicEight class that extends the LuisDialog. For each intent we have we should have a method that has the attribute [LuisIntent([IntentName])] as shown below. This method will be called when the bot determines that this question/intent has been called. For our example we do not care about any actions or details from the message, just that it was a valid question. We then send a message of a random response from the list of responses on a standard magic eight ball.

We also provide a method for the None intent by using the attribute [LuisIntent(“”)]. This allows us to provide a default response to users when it can not determine the intent.

Please note the App Id and Key shown in the example below is not valid and you will need to get these from a valid LUIS application.

[LuisModel("b514324f-d6d3-418e-a911-c7fasda6699e2", "a91e3e2044a987876291c54a153f3a6")]
[Serializable]
public class MagicEight : LuisDialog<object>
{
    private static Random rand = new Random(14891734);
    string[] responses = { "It is certain.", "It is decidedly so.",
        "Without a doubt.", "Yes, definately.", "You may rely on it.",
        "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.",
        "Signs point to yes.", "Reply hazy try again.", "Ask again later.",
        "Better not tell you now.", "Cannot predict now",
        "Concentrate and ask again", "Don't count on it.",
        "My reply is no.", "My sources say no.", "Outlook not so good",
        "Very doubtful."};

    [LuisIntent("Question")]
    public async Task AnswerQuestion(IDialogContext context, LuisResult result)
    {
        int x = rand.Next(20);
        string message = responses[x - 1];
        await context.PostAsync(message);
        context.Wait(MessageReceived);
    }

    [LuisIntent("")]
    public async Task None(IDialogContext context, LuisResult<object> result)
    {
        string message = "Sorry I did not understand";
        await context.PostAsync(message);
        context.Wait(MessageReceived);
    }
}

Our final task is to return to our MessagesController class and update the Post method to send new messages to our MagicEight class.

public async Task Post([FromBody]Message message)
{
    if (message.Type == "Message")
    {
        return await Conversation.SendAsync(message, () => new MagicEight());
    }
    else
    {
        return HandleSystemMessage(message);
    }
}

Test in Emulator

Now that we have the code complete, test the application in the emulator.

Capture5

You may not always get the correct response straight away. This is due to the LUIS application having not been trained much, the more work you put into labelling the utterings within the LUIS application the more accurate it will become. Any uttering sent to the service will be available for you to label, or approve the automatically assigned label that the service determined.

You can now take what you have learnt in this post and expand it into more complex bots and connect what you have created into web/windows applications.

Author: Dave Green

Software developer focusing on Microsoft Technologies, MCSD in Windows Store Apps, full time developer at a real job, part time writer and terrible golf player. Follow on twitter @IntelligentLabs

Leave a Reply

Your email address will not be published. Required fields are marked *