Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / artificial-intelligence

Creating an On-Premise Offline Bot in C# using Oscova

5.00/5 (6 votes)
10 Mar 2020CPOL19 min read 20.4K   1.3K  
Using Oscova and Oryzer FBP platform, we'll build an on-device table reservation bot to understand the concepts behind today's offline bot development standards.
In this post, we are going to create a Table Reservation Bot and for it, I'll be using freely available tools, especially a freely available Flow-Based Programming platform called Oryzer Studio and will make an attempt to drive the audience through the concepts one step at a time. By the end of the article, we would have successfully created a table reservation bot, tested it in Oryzer and even imported it into a Console app.

Introduction

Previously (a while back), I explained how one can talk to their database using a bot interface. In this article, we'll skim through the general concepts of an offline (on-device / on-premise) bot with the least amount of backend coding overheads.

We are going to create a Table Reservation Bot and for it, I'll be using freely available tools, especially a freely available Flow-Based Programming platform called Oryzer Studio and will make an attempt to drive the audience through the concepts one step at a time. I'll however try to refrain from directly posting links here so you might have to do a little bit of Googling here and there. Although I'll try to minimize such circumstances.

Though the audience may have general knowledge of how today's bot architectures work, I'll still try to introduce some of the basic concepts that are now widely established among Bot development platforms.

Background

Before diving in, I highly recommend that the audience takes a look at my previous article where I created a Bot for interacting with a database. The article highlights a lot of basic concepts that are used in this article.

The article is quite comprehensive and takes you through the basics of Bot Development, WPF GUI, setting up a database utility class and binding the bot's actions to a database.

Prerequisites

To fire up and get running, you'll have to have the following skill sets or maybe you'll have to polish them a little bit to get through the article swiftly.

  • Knowledge of Flow-based Programming constructs (Components/Nodes, Ports and Connections)
  • Knowledge of working in Visual Studio
  • Basic knowledge of programming in C#/.NET
  • Basic knowledge of Bot architectures

For this article, you'll just need the following:

Terminologies

I may use the following terms and their short-forms (acronyms) throughout the course of this article and for better comprehension, I recommend that you familiarize yourself with it.

  • Knowledge-Base (KB) - A collection of dialogues the Bot can engage with.
  • Components/Nodes - These terms maybe used interchangeably throughout the article.
  • Flow-based programming (FBP) - A programming construct where logic is built by connecting functional components.
  • Workspace - Sort of a graph canvas where your nodes will be placed and connected with other nodes.

Article's Approach

Instead of scripting or hard coding the bot in any programming language, we will for the sake of brevity and introduction to Flow-Based Programming, use Oryzer Studio to develop a skeleton knowledge-base of our Bot's basic interactions.

Once we've successfully built the knowledge-base (KB) of the Bot, we will test in within Oryzer Studio itself and later import the KB into a simple C# Console application.

As this article may have a lot of FBP involved, I'll take the following approach for clarification:

  • Explain the component being added/used
  • Verbally explain the steps of connecting components (initially)
  • Provide a visual cue with a screenshot that shows how components are connected.

The industry and research behind bot development is getting a bit standardized. In this article, we'll learn some of the common terms circulating around bot development. Like Dialogs, Intents, Contexts and Entities.

Let's Get Started

Assuming you've downloaded Oryzer Studio from the link mentioned above, let's get started by first creating a Dialog.

In order to create this bot, we'll collect the following information from the user during the conversation flow:

  1. The date for which the reservation is to be made
  2. The timing (Breakfast, Lunch or Dinner)
  3. Number of people to reserve the table for
  4. User number to confirm the reservation

Dialog (greet_dialog)

A dialog is a collection of intents, more like a topic of conversation. For example, if your bot has to say Hi, Hello, Hey there, you'll group them into a dialog named Greetings. A dialog is a good way to group similar intents under a unique identifier.

To create a dialog:

  • Launch Oryzer Studio.
  • Expand the Oscova category from the right Node Explorer.
  • Select the Dialog node and drag it onto the Workspace.
  • If you accidentally dragged a wrong node, simple press Delete on it and it shall be removed.

What you've just dragged is a Node and to where you've dragged it is called the Workspace.

Image 1

A Node in Oryzer Studio is a functional unit that performs a specialized task. A Node comprises:

  • Input Ports - The blue boxes on the left
  • Output Ports - The blue boxes on the right

Input ports usually store certain variable values that enable a node to perform its specialized task.

Output ports on the other hand, are returned values of a Node when it has completed performing a task. Or in some cases, the output port can also be the Node itself.

So we've now created a Dialog node that will assist us in starting a dialogue with our user and provide the first set of options for reserving a table.

You can see that the Dialog node has a couple of Input and Output Ports:

  • Input Ports - Name, Intent Alias, Domain and Intents
  • Output Port - (self) - Returns the node itself as an output

We'll call this dialog the greet_dialog and we'll connect 2 intents to it.  To name the dialog in the Name port, simply type greet_dialog.

Save this workspace by pressing CTRL+S and naming the file table-reservation-bot.west.

Intent (greet_intent)

An intent is something that binds a user query to a response and optionally an action when a given intent's expression (user query example) matches. In order to initiate our conversation, we'll add an intent called greet_intent to our greet_dialog.

Our greet_intent will greet the user, introduce itself a bit and collect the initial set of information we need to proceed with the reservation.

To add an intent to a dialog, we'll do the following:

  • Drag an Intent node from Node Explorer (on the right).
  • Click and drag from the output (self) port of the Intent node to the Intents input port of the dialog node.

Image 2

We've now created a dialog and connected an intent to it.

Expressions

In the field of Bot development, expressions are sets of user query examples that help a bot engine train itself to find patterns for similarity detection. The more the number of expression examples you provide, the better a bot gets in detecting similar user queries.

An expression maps a user query to an intent which then maps it to a response or an action. We won't be adding too many expressions here for brevity but just a couple of them to clarify the concept.

We'll go ahead and add 4 expression examples (remember these are examples of user queries).

  • Hi there
  • Hello there
  • Hey can I book a table
  • I would Iike to book a table please

Now let's add expression nodes to the workspace and connect them to the aforementioned intent node:

  • Drag Expression node from node explorer.
  • In the Value port's text box, enter the above expression samples for each new expression node.
  • Connect the output ports of the Expression nodes to the greet_intent as shown in the image below:

Image 3

Now that we've provided some samples of user query to the bot system, we'll have to specify what will be the response of the bot when an example query (expression) is matched.

Response

A response is a part of an action that is invoked when a user query matches an expression that's associated with an intent. We've already added 4 expressions so now let's add a response if the user query matches one of our added expressions.

Our response is going to do the following:

  • Send a Textual message that will greet and ask the user to provide some information
  • Provide options to the user to swiftly select a follow-up response for the bot's message
  • Set up a context to create a flow (we'll learn about it in the next section)

So now, let's drag a Response node and connect it to the greet_intent as shown below:

Image 4

Since our first task is to add a textual message, we're going to drag in another node called the Input Text node and connect it to the Text port of the Response node.

The content of the text node is going to be:

The Text part

Hi there! I am here to help you book a table.  
So let's begin by letting me know for when would you like to book a table?

We'll also add 2 input options to be displayed to the user by separating them with a vertical bar in the Hint box of the Response node

The Hint part

Today|Tomorrow

Image 5

Apart from asking the user for when they'll need the reservation for, we'll need to prepare another intent to be ready to take the answer the user provides to our opening question.

We'll go ahead and drop a Context Add node and connect it to the response node. So when the response is invoked, the relevant context gets added.

Context (table-date-con)

A context in bot development is usually a string label added to something called the chat session to create a hierarchical flow of conversation. The existence or non-existence of these contextual strings govern the activation of intents.

As seen below, I've dragged and dropped a Context Add node and connected it to the output port of the Response node. Whenever a response is being generated by the intent, this node will get executed and a context item table-date-con will get added in the session and will exist for the next 2 user queries. As we've selected the Lifespan value of 2.

Image 6

Intent (table_date_intent)

If you recall in our greet_intent's response, we've asked the user for when he would like to have the table reserved for. Well, if the user then specifies a date like Today or Tomorrow, we need to have an intent to capture just that.

This intent has a couple of tasks to complete:

  • Capture the user specified date.
  • Store the user specified date value in something called a user variable.
  • Prompt the user to specify the meal type/timing (Breakfast, Lunch or Dinner)
  • Create another context named table-time-con to help enable the follow up intent.

Now that you know how to drag, drop and connect nodes, I won't be needing to redundantly re-explain the steps of doing so.

Image 7

Context and Expression

Let's go ahead and specify that this intent requires that the context term table-date-con exists in user chat session and then we'll go ahead and specify an expression that ensures it captures only date values from the user input.

Image 8

In the screenshot above, you can see that I've specified a context with the name table-date-con, if you may recall this context was added by our greet_intent.

Now by specifying the context name in this intent, we are telling the bot system to match this intent's expressions if and only if the context table-date-con exists in user chat session.

The expression @sys.date is a pre-built system entity that matches dates. More info on this is available in Oscova's developer portal. You can just Google that one out for now.

Response

The response of this intent has three parts to it:

  1. Store the date value to some user variable (we'll use the value in our response).
  2. Create a new follow up context table-time-con so that the next intent can capture the meal type.
  3. Prompt the user to specify the meal type.

For this:

  • Drag a Response node and connect it to the intent

Firstly, we'll go ahead and store the date part specified by the user. Note, this intent is invoked only if the user specifies a date value following the greet_intent

To do so:

  • Drag Entity Get node and specify the entity type as sys.date
  • Then drag a Variable Set node and connect the Value port of Entity Get node to the Variable Value input port of the Variable Set node.
  • Specify the variable name as table-date

Image 9

Make sure that in the Entity Get node's Get property, you have set Value as the get type and in the Variable Set node select User as the Target.

Voila! We are now set. Whenever this response node is called, a variable named table-date will be saved in the user variables.

Context and Textual Response

We'll have to add a new context for the next intent that will capture the meal type or timing. So let's go ahead and drag and connect a Context Add node to the call output port of the Response node.

We'll have the context as table-time-con as shown in the screenshot below:

Image 10

Apart from adding a context for the next follow-up intent, we'll provide a textual response that will display some message like the following:

Alright so the table's set for Lunch and for which meal would you like to come? (If the user specifies Lunch as the meal type/timing)

Note that we've used $sys.user.table-date in our response text. This is a special parameter that tells the bot system to replace the value with a stored user variable called table-date

If you, for some reason, stored a variable in the bot's variable collection, then the parameter would be $sys.bot.table-date.

Entity Creation

In the above intent, we are giving the options Breakfast|Lunch|Dinner to the user for meal timing. When the user enters any one of the above values, we'll need to capture it.

An easy workaround here is to simply convert them to a known entity type. In order to create an entity of type say @meal-type, we'll follow the steps below:

  • Drag and drop an Entity Recognizer node.
  • Connect the (self) output port of this node to the input Recognizer port of Oscova Bot node.
  • Specify the entity type as meal-type in the Entity Type port editor.

Image 11

Next let's specify 3 entry values to this Entity Recognizer node by:

  • Dragging 3 Entry nodes.
  • Specifying Breakfast, Lunch and Dinner as their Entry Values.
  • Connecting their (self) output port to the input Entries port of the Entry Recognizer node.

Image 12

Let's Test the Bot

So far so good. Now that our 2 intents are ready, we can go ahead and try interacting with the Bot. In order to test the Bot, we'll firstly have to connect the Dialog node to an Oscova Bot node and then connect this Oscova Bot node to an Oscova Test Panel node.

To connect your greet_dialog node to an Oscova Bot node:

  • Simply drag the Oscova Bot node next to the dialog node.
  • Connect the (self) output port of the dialog node to the Dialogs input port of Oscova Bot node.

Image 13

The Oscova Bot node is the actual node that represents the bot in entirety. This node has a couple of input ports to which you can connect a couple of other node types.

For now, let's just connect this node to Oscova Test Panel node to test the response of our bot.

Image 14

After you've connected Oscova Bot and Oscova Test Panel node:

  • Press Train Bot to complete the training process.
  • Type hi there and you should see our first response with options.
  • Select or type Today and the second intent's response should be displayed.

Moving On

Now that you've (most probably) seen and tested the response of your bot, in this next phase, we'll migrate towards other important intents that will capture some additional information from the user.

So we've taken the Date and now have asked the user to provide us with the Meal timing. Our next 2 intents are going to be simple. Their task at hand is to:

  • Capture the meal time requested by our previous intent.
  • Request the user to provide the number of people the table should be reserved for.

After these 2 intents, there will be our final confirmation intent that will display our closing message to the user.

To keep things comprehensible, we'll limit storing any further information in variables because you already know how to do so. So adding such an extra layer wouldn't contribute to our learning process.

Dialog (table_config_dialog)

It's going to simpler from here point onwards. Again, let's add a Dialog node to the workspace, connect its (self) output port to Oscova Bot node's Dialogs input port.

In the Name port editor, specify table_config_dialog as the name.

Image 15

Intent (table_time_intent)

Add a new Intent node, name the intent table_time_intent. This intent is going to capture the meal type (breakfast, lunch or dinner) requested by our previous intent and will prompt the user to specify the number of people to book the table for.

  • Drop an Intent node
  • In the Name port editor of the node, specify table_time_intent as the name
  • Connect it to the table_config_dialog Dialog node.

Since the previous intent added a table-time-con context item. We'll specify that this intent requires that context to exist and also, we'll add an Expression node which will capture entities of the type @meal-type.

So, go ahead:

  • Drag and drop a Context node and connect its (self) output port to the Intent node.
  • Specify the context name as table-time-con.
  • Drag and drop an Expression node and specify @meal-type in the Value port editor. This is because we want this intent to be called whenever the user specifies breakfast, lunch or dinner as a response.

Image 16

Next, let's simply add a response and a context item for our next follow-up intent that will capture the number of people to reserve the table for.

  • Add a new Response node, connect it to the intent.
  • Add a new Context Add node, specify table-num-con as the Context name
  • Add an Input Text node, specify Perfect! How many people shall I book the table for? as the text value.
  • Connect the Input Text node to the Response node.

Image 17

Intent (table_num_intent)

Now in this intent, let's capture the user's response to our question that requests the user to provide the number of people the table has to be reserved for.

  • Add an Intent node, Name the intent table_num_intent.
  • Add a Context node and an Expression node and connect it to the input ports of the intent node.
  • Set table-num-con as the context name.
  • Set @sys.number as the Expression node's value.

I guess by now you are getting the gist behind all the connections and nodes we keep meddling with.

Image 18

Let's add a response node, store the value for the number of people and set the context for our final confirmation intent.

  • Add a new Response node and connect it to the intent node
  • Drop a new Context Add node, specify table-phone-con as the context name and connect it to the Response node.
  • Drop a new Entity Get and Variable Set node. Configure them to store the @sys.number entity.
  • Add an Input Text node with the response text. Awesome! I'll set the table for $sys.number people then. We are all set. Could you please share your number so that I can confirm your booking.

The response contains the $sys.number parameter which will be replaced with the value specified by the user for the number of people the table is to be reserved for.

Image 19

Dialog (confirm_dialog) - Finale!

Our final dialog with the user is to show a confirmation message regarding the reservation. The following is our final confirm_dialog setup.

  • Add a new Intent node and name it confirm_intent.
  • We add a Context to specify the context term that should exist.
  • We capture a number using the @sys.number pre-built entity type.
  • Finally deliver a textual response that says Thank you so much! Your table booking for $sys.user.table-date for $sys.user.table-num is now confirmed. You'll receive a notification on your mobile number $sys.number soon. We use two parameters, $sys.user.table-date and $sys.user.table-num that will be replaced with the date and table count specified by the user.

Image 20

Let's Test It

Go ahead, again tap on Train Bot on Oscova Test Panel node and follow the conversation flow. If all's well, then your conversation with the bot should end up looking something like the following:Image 21You can also see a short video that shows how it ended up looking for me.

Importing into a Console App

Now that our knowledge-base (KB) for the bot is ready, why not check out how to import such a KB into an application. It wouldn't obviously make any sense if we just created the KB within Oryzer and left it there.

Creating a C# Console Project

Fire up Visual Studio 2019 or above in your machine.

  • Launch Visual Studio 2019.
  • Select Console App (.NET Core) as your project type.

Image 22

  • You can name the project anything. I happened to have chosen TableReservationBot as the project name.

Image 23

Importing NuGet Package

The knowledge-base we've developed so far is for a Bot development platform called Oscova which is part of  Syn.Bot framework which is available in NuGet.

To import the NuGet package:

  • Once your project is opened
  • Click on Tools, select NuGet Package Manager and choose Package Manager Console
  • Type Install-Package Syn.Bot to install the required packages and its references.

Replace the code in the Program.cs class file with the following:

C#
using System;
using Syn.Bot.Oscova;

namespace TableReservationBot
{
    class Program
    {
        static void Main(string[] args)
        {
            var bot = new OscovaBot();
            bot.ImportWorkspace(@"C:\Users\Workstation\Documents\table-reservation-bot.west");
            bot.Trainer.StartTraining();

            bot.MainUser.ResponseReceived += (sender, eventArgs) =>
            {
                Console.WriteLine($"Bot: {eventArgs.Response.Text}");
            };

            while (true)
            {
                var request = Console.ReadLine();
                var evaluationResult = bot.Evaluate(request);
                evaluationResult.Invoke();
            }
        }
    }
}

In the code above, we've created an instance of OscovaBot class, called the ImportWorskspace method and passed in the location of our workspace based knowledge-base file.

In the lines that follow, we are simply taking in user inputs, processing it and displaying the bot's output message.

Build and Run the project and type in the first input as Hi and let the conversation flow.

Image 24

And there you are! We've successfully created a table reservation bot, tested it in Oryzer and even imported it into a Console app. Ah, what a long journey it has been! :)

Points of Interest

Alrighty! So I personally think I've barely scratched the surface here. Comparatively to my previous articles that were code centric, this one took a different dive. I refrained from using any back-end C# code unlike my previous article as I wanted to in entirety stay with flow-based programming constructs.

In real world logic, one would indeed have back-end code that would actually do something about the information collected by the bot. It may come as a surprise to many that whatever logic I created using nodes can also be created using pure C# backend code.

For brevity, I reluctantly had to stay away from expanding more on how Oryzer Studio works, instead I chose to stick with our bot development. One can however freely test drive the platform to build absolutely anything to their hearts content.

History

  • 10th March, 2020: Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)