Journey to an intelligent Azure Chat Bot - Part 4

First, I have to mention: As of today, I have no clue yet how to build a chatbot with Microsoft Azure. None of the steps below I have done before - Research will be necessary.
That said - the following are the goals and also the features of the bot built in this blog series. Achieving those goals will certainly facilitate a lot of the daily business:

Part-4 Content


Introduction


In the first and second part of this blog series, we already have built our basic Microsoft Azure chatbot and deployed it to the cloud. We deployed the bot to Microsoft Teams. Not let's play around with dialogs and interaction. We are going to build some pre-orchestrated dialogs and later on the bot should be able to decide which dialog to launch by its own language understanding / intelligence.

I plan to do a dialog setup like that in the drawing. We have an "intent dialog", where the user of the chatbot decides what to talk about. Example: "order food", "order beverages". Then there is a redirection to a specialized dialog.
First off, we are going to use a simple design for this intent dialog. Later I plan to try more sophisticated approaches like Language understanding for the intent dialog.

Image - Rough Dialog Design

Dialog Types


After initial research, I found that the Microsoft Bot Framework has different Dialog Types - the most important being the "waterfall dialog", the "component dialog" and also a bunch of "prompt dialogs". There are many other dialog types.

  • Component Dialog
    A Component Dialog is a general purpose, stateful dialog and can encapsulate other dialogs. It allows creating reusable and composable dialog components that can be combined and also nested within other dialogs. It provides a way to modularize your dialog logic, making it easier to manage and reuse across different parts of your bot. Component Dialogs can contain multiple child dialogs and control the flow of conversation by invoking and managing these child dialogs.
  • Waterfall Dialog
    Defines a sequence of steps or prompts linearly. It is typically designed to work as part of a component dialog.
  • Prompts
    Different prompts that query information from the user. Like "choice prompt" for example, that presents the user with a multiple choice question. Or "confirm prompt", "number prompt", "text prompt", "datetime prompt". They can be part of a waterfall dialog.

Dialog Draft, API Integration


So, in our example we will use a bunch of different dialog types to get the drift.

Image - More detailed Dialog Setup Design

So we have a "main" dialog, which will launch our different other dialogs:

  • Intent Dialog
    On the top we have an intent dialog that should ask the user whether a food or a beverage should be ordered. As far as I understand at this point, we can later replace the intent dialog with language understanding. Anyway. this dialog decides which subdialog will be launched. We could have dozens of dialogs in a business app.
  • Beverage Dialog
    I intend to make a very simple component dialog that contains a waterfall dialog and some prompts to order a beverage:
    "cola" - "original" or "zero"
    "water" - "still" or "sparkling"
  • Food Dialog
    Another component dialog with a "choice prompt" (vegetarian, meat) communicates with a mock API (API Integration) that proposes a random vegetarian or meat meal.

Dialog Demo


So that's it - "main dialog", "intent dialog", "beverage dialog" and "food dialog".
Here is a demo of the already finished dialog.

Code Snippets


I have changed the code that was initially generated to implement the features mentioned above. You can have a look yourself at the public GitHub repository.

mainDialog.js

In the mainDialog.js I changed the dialogs as follows:

this.addDialog(new TextPrompt('TextPrompt'))
.addDialog(intentDialog)
.addDialog(beverageDialog)
.addDialog(foodDialog)
.addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [
  this.introStep.bind(this),
  this.intentStep.bind(this),
  this.mainStep.bind(this),
  this.finalStep.bind(this)
]));

You can see that after an "introStep" (which basically does nothing - I commented everything out), it launches the "intent step". The intentStep launches the intentDialog, which prompts the user whether he or she likes to order food or beverages. The result is then passed to the "mainStep". If the intent was to order food, the mainStep launches the "foodDialog". Otherwise, it launches the "beverageDialog". Here is a link to the main dialog:

beverageDialog.js

The beverage dialog I built with the intention of trying out a very simple waterfall dialog. It first asks "cola or water" and then "zero or original" or "still or sparkling", respectively.

this.addDialog(new TextPrompt(TEXT_PROMPT))
.addDialog(new ConfirmPrompt(CONFIRM_PROMPT))
.addDialog(new ChoicePrompt(CHOICE_PROMPT))
.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
    this.beverageStep.bind(this),
    this.kindStep.bind(this),
    this.confirmStep.bind(this),
    this.finalStep.bind(this)
]));

This logic is built into the "beverageStep" and "kindStep". Take a look at the code an note that each respective function parses the result / user input from the previous function. If "cola" was selected, it will pick up this choice and ask "zero or original" in the next step. In the beverageDialog.js on line 54 I tested how to do answer validation. If the user responded anything else than "cola" or "water", the dialog won't continue and ask the user again:
"i dont know ${beverageOrderDetails.beverage}. Please choose again"

foodDialog.js

The foodDialog will prompt the user wether it should be vegetarian or with meat...

this.addDialog(new TextPrompt(TEXT_PROMPT))
.addDialog(new ConfirmPrompt(CONFIRM_PROMPT))
.addDialog(new DateResolverDialog(DATE_RESOLVER_DIALOG))
.addDialog(new ChoicePrompt(CHOICE_PROMPT))
.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
  this.foodStep.bind(this),
  this.confirmStep.bind(this),
  this.finalStep.bind(this)
]));

In the food dialog i integrated a mock API Call to a food database, so that it will propose a meal: getMeal(true) for a vegetarian meal.

async confirmStep(stepContext) {
        const foodOrderDetails = stepContext.options;

        // Capture the results of the previous step
        foodOrderDetails.kind = stepContext.result;

        switch(foodOrderDetails.kind) {
            case "vegetarian":
                foodOrderDetails.meal = getMeal(true)
                break
            default:
                foodOrderDetails.meal = getMeal()
                break
        }

        const messageText = `How about: ${foodOrderDetails.meal} as a ${foodOrderDetails.kind} meal?`;
        const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput);

        // Offer a YES/NO prompt.
        return await stepContext.prompt(CONFIRM_PROMPT, { prompt: msg });
    }

Conclusion


After building this sample dialogs, I feel able to build all kinds of dialog flows. The intent dialog could be smarter - we will focus on that in the next post of the blog series.
We have achieved the goal: Test how to build multiple choice selections, user intent, dialog flows, prompts and answer validation. Support for Custom API Interactions.