BestMatchDialog for Microsoft Bot Framework now available via Nuget

Recently I have been developing a number of bots using the Microsoft Bot Framework, with the LUIS service to allow users to use natural language to interact with them.  One thing that struck me when we released the bots to a wider user base was just how polite everybody was towards them.  The bot was receiving messages like “hi”, “how are you?”, “thanks” and “bye bye” – the only problem was that the bots didn’t know how to deal with these messages.  Sure, they could deal with a ton of more complex messages / intents using LUIS, but wasn’t able to ‘understand’ and provide such common responses.

I set out to think about how I could solve this problem in a re-usable way and whilst doing so I ended up looking through the open source code for the Bot Framework on GitHub to see how dialogs like the LUIS dialog worked. Then, whilst browsing I came across some code in the Node section of the Framework that handled the matching of Intents within the IntentDialog – a few hours later I had my first version of the BestMatchDialog.

The BestMatchDialog allows you to match the incoming messages against a lists of strings, like “hi”, “hello”, “hey there” or “goodbye”, “bye bye”, “adios” and attach a handler to each list (much like the LUIS dialog has a handler for each intent). When a message is received by the dialog it is compared against each list and you can set a threshold for how close the incoming message and items in the list should match. For example, you could have “hello there” in your list and the incoming message “hello” match providing you set the match threshold at the right level. Matching is carried out against every list and then the handler for the highest scoring match (providing one is found) is executed.  If no match is found then the NoMatchFound handler is called, which you can override.

The BestMatchDialog also allows you to enable / disable case sensitivity and include / exclude non-alphanumeric characters on a per list basis.

You can use the BestMatchDialog in your own bot application project by adding the NuGet package which is now available and you can find a working sample bot application, more documentation and the source over at my GitHub repository.

Below is an example of a class implemented using the BestMatchDialog with two lists / handlers and an overridden NoMatchFound method.

You can use the BestMatchDialog as your root dialog if you need to, but for me the real advantage for a dialog like this is when you use it as a child dialog.  For example, I call the BestMatchDialog within the ‘None’ intent handler on my LUIS dialogs to hanlde common responses like greetings or goodbyes.  So, if the LUIS dialog doesn’t find anything that the user wants to do specifically it checks to see if there are any general responses it should be providing.  You can find an example of how to do this over at the GitHub repository in the readme.

I hope you like the BestMatchDialog and find it useful.  Let me know if you have feedback or find any bugs and I will happily take a look as soon as I can.

Happy bot building!

11 thoughts on “BestMatchDialog for Microsoft Bot Framework now available via Nuget

  1. Hi Gary,

    Awesome stuff. Really like your enthusiasm with LUIS and the Bot Framework. A few questions..

    I know you mentioned that you like to call the BestMatchDialog in the ‘None’ LUIS intent, which sounds like a great idea, however can this be reversed? So we call the BestMatchDialog first, check for our users intent and THEN call LUIS if there are no matches? Could this be used to save on LUIS API calls? I am developing a POC for my company and saving on API calls would be a big deal for me as I can save the company money in the long run.

    I have a class called DefaultDialog which extends LuisDialog and every time a message is sent by the user, a new instance of this is created, like so:

    if (activity.Type == ActivityTypes.Message)
    {
    await Conversation.SendAsync(activity, () => new DefaultDialog(_luis, _defaultDialogPromptsProvider));
    }

    How would I approach this with the way I asked in the first question? Would I go about putting this line in the NoMatchHandler Task of the BestMatchDialog?

    Thanks

    Chris

    1. Hi. If I understand correctly, what you would need to do is use the BestMatchDialog as your default dialog in your MessagesController, which will try and handle the message that arrives into the bot. Then, within the NoMatchHandler you would then call your LUIS dialog to try and handle the intent. Does that make sense?

      1. Hi Gary,

        Thanks for your reply. Yep, I completely understand and I am trying to implement it now. I will let you know how it goes.

        I have another quick question:

        Upon launching the the bot beginning a new conversation, is there any way to get the bot to be the first one to send a message?

        Thanks

        Chris

      2. Having a bit of trouble understanding how to call my LuisDialogs within the NoMatchHandler…

        I have the below code:

        public override async Task NoMatchHandler(IDialogContext context, string messageText)
        {
        await Conversation.SendAsync(context.Activity.AsMessageActivity(), () => new DefaultDialog(_luis, _defaultDialogPromptsProvider));
        context.Wait(MessageReceived);
        }

        But it just seems to get stuck on this line. Any suggestions?

      3. Having a bit of trouble understanding how to call my LuisDialogs within the NoMatchHandler…

        I have the below code:

        public override async Task NoMatchHandler(IDialogContext context, string messageText)
        {
        await Conversation.SendAsync(context.Activity.AsMessageActivity(), () => new DefaultDialog(_luis, _defaultDialogPromptsProvider));
        context.Wait(MessageReceived);
        }

        But it just seems to get stuck on this line. Any suggestions?

      4. Hi,

        Just to let you know I managed to get it working. I can now call LUIS if BestMatch doesn’t find any matches:

        public override async Task NoMatchHandler(IDialogContext context, string messageText)
        {
        await context.Forward(
        new LuisIntentsDialog(_luis, _defaultDialogPromptsProvider),
        NoMatchCompletionHandler,
        context.Activity.AsMessageActivity(),
        CancellationToken.None);
        }

        private async Task NoMatchCompletionHandler(IDialogContext context, IAwaitable result)
        {
        await context.PostAsync(“Sorry, I don’t understand. Please re-phrase.”);
        context.Wait(MessageReceived);
        }

        1. Great news! Thanks for trying the BestMatchDialog as well 🙂 If you have any feedback or suggestions then please let me know – would love to hear it.

          1. Hi Gary

            Would you be able to explain in a little more detail exactly what the threshold property does, how much I should be increasing/decreasing it to get the results I want and what the maximum/minimum values are?

            Thanks
            Chris

          2. Hi. Apologies for the delay in coming back to you. The threshold value is set between 0 and 1, with 1 meaning that the match should be exact (in terms of characters with case being managed by the ignoreCase attribute). e.g. When the threshold is set to 1, “Hello there” will only match with “hello there”, but if we lower the threshold to 0.5 then “hello” will match with “hello there”. Does that help?

    1. The BestMatchDialog should allow for simple spelling mistakes. You could try reducing the threshold for matching and see if that helps. Alternatively you could use the Bing Spell Checker service to correct common spelling mistakes before you pass the message to the dialog.

Leave a Reply

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