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

Evolution of Microsoft Bot Framework

4.89/5 (37 votes)
8 Jun 2022CPOL10 min read 71.6K   638  
An interesting article on Artificial Intelligence Chat Ro(Bot) application development
In this article, I categorize Microsoft Bot Framework development evolutions into three generations as GenX, GenY and GenZ. We look at concepts, implementation and code samples for each of them.

Generations

Chat Bot is a software program that can simulate talk with a human being. One of the first and most famous chat bots (prior to the Web) was Eliza, a program that pretended to be a psychotherapist and answered questions with other questions.

Red and Andrette were names of two early programs that could be customized to answer questions from users seeking service for a product. Such a program is sometimes called a virtual representative or a virtual service agent.

Overview

Based on Microsoft Bot Framework development evolutions, I personally categorize into three generations as:

  1. Gen X - Core (represents the foundation of Bot Coding)
  2. Gen Y - Collaborate (interfaces with the external business service)
  3. Gen Z - Cognitive (involves the artificial intelligence in the business logic)

It is diagrammatically represented as:

Generation Layers

Use Case

To illustrate the incremental generations of Microsoft Bot framework, let me choose Chat Bot use case of 'Stock Price Teller'.

Stock Price Teller is a simple business use case, where the end user enquires the latest price of the given stock quote. As an example, if the end user wants to know the closing/latest stock price of IBM, this application will respond the price value as the result.

GenX

GenX covers the development of Chat Bot foundation and its fundamental communication framework.

Concepts

Microsoft Bot Connector is a communication service that helps you connect your Bot with any one of the communication channels like Skype, SMS, email, and others. If you write a conversational Bot or agent and expose a Microsoft Bot Framework-compatible API on the Internet, the Bot Framework Connector service will forward messages from your Bot to a user, and will send user messages back to your Bot.

In essence, the developer is advised to write their own Bot App Service with Bot Connector functionality. This custom service is built to completely leverage Microsoft Bot Framework API.

GenX Concepts

Bot Framework Emulator is part and parcel of this package to simulate the client side component.

Implementation

Code level implementation is drafted in the below sequence diagram.

GenX Implementation

Implementation starts from the foundation class of Microsoft Service, i.e., System.Web.HttpApplication. Once all the core ASP.NET objects are created, HttpApplication object is created to serve the request. In case you have a global.asax (inherits from HttpApplication) in your system, then the object of the global.asax file will be created.

The first time an ASP.NET page is attached to an application, a new instance of HttpApplication is created. To maximize performance, HttpApplication instances might be reused for multiple requests. It is depicted in the life cycle diagram.

GenX Implementation

HttpConfiguration class is in the System.Web.Http library, which is a framework for building web APIs.

Code Sample

As the initiation point, Application_Start is called when the first resource in an web based application is requested. The Application_Start method is called only one time during the life cycle of an application.

This method is used to perform startup tasks such as loading data into the cache and initializing static values.

C#
/// It's an optional file that contains code for responding
/// to application-level events raised by ASP.NET or by HttpModules
/// by Ganesan Senthilvel
public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
    }
}

RouteConfig.cs file here is the same as in any MVC project, and sets up routes for the MVC framework. The WebApiConfig.cs file is where our Web API routing configuration happens.

Our WebApiConfig static class looks like the below source code:

C#
/// It's used for any Web API related configuration, including Web-API
/// specific routes, Web API services, and other Web API settings
/// by Ganesan Senthilvel
public static class WebApiConfig
{
    /// This method registers a dependency property with the
    /// specified property name, property type, owner type, property metadata,
    /// and a value validation callback for the property.
    public static void Register(HttpConfiguration config)
    {
        // Json settings
        config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling =
                                                      NullValueHandling.Ignore;
        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
                          new CamelCasePropertyNamesContractResolver();
        config.Formatters.JsonFormatter.SerializerSettings.Formatting =
                          Formatting.Indented;
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            Formatting = Newtonsoft.Json.Formatting.Indented,
            NullValueHandling = NullValueHandling.Ignore,
        };

        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

If you notice, instead of calling Routes.MapRoutes as in the MVC RouteConfig class, we instead call Config.Routes.MapHttpRoutes as above.

On getting into the Configuration part, MicrosoftAppId and MicrosoftAppPassword are generated when your Bot is registered with the Microsoft Bot Framework Connector. MicrosoftAppId and MicrosoftAppPassword are used to authenticate the conversation, and allows the developer to configure their Bot with the Channels they'd like to be visible on. The BotId, which you specify, is used for the URL in the directory and developer portal.

AppId and AppPassword from the Bot Framework registration page have to be recorded in the project's web.config.

On coming to the next method - Post, the core functionality of the Bot Template is all in the Post function within Controllers\MessagesController.cs. In this case, the code takes the message text for the user, then creates a reply message using the CreateReplyMessage function. The BotAuthentication decoration on the method is used to validate your Bot Connector credentials over HTTPS.

C#
/// POST: api/Messages
/// Receive a message from a user and reply to it
public async Task<message> 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("ServBot::You sent the message: " +
                            message.Text + " with " +
                            length + " characters at " + DateTime.Now);
    }
    else
    {
        return HandleSystemMessage(message);
    }

On execution of GenX code base, the result is:

GenX Result

GenY

GenY covers the additional step to connect another server component, submit the request to fetch the relevant information, retrieve the response back to Bot component. It is an extend arm of GenX with external communication.

Concepts

As the next level of Bot Application development, the arm is extended to connect the other services for core logic execution. In continuation to the previous GenX example, we will show case GenY concepts.

In the custom MyStockBot MyController object, Post method receives a message from the end user and replies back. So, it is the right container to build the core business logic.

In this method, Stock Exchange Price finder is instantiated to interface the stock value query process. This App Server connectivity executes the core business logic and returns the matching result back to Post method. In turn, the result goes back till end user layer.

GenY Concepts

Implementation

There are three main SDK components of the Microsoft Bot Framework to build:

  1. Bot Builder - Open source SDK hosted on GitHub that provides everything you need to build great dialogs within your Node.js- .NET- or REST API-based bot
  2. Developer Portal - Let's connect the bot seamlessly text/sms to Skype, Slack, Facebook Messenger, Kik, Office 365 mail and other popular services
  3. Bot Directory - Users will be able to discover, try, and add bots to their favorite conversation experiences

Developers writing bots all face the same problems: bots require basic I/O; they must have language and dialog skills; they must be performant, responsive and scalable; and they must connect to users – ideally in any conversation experience and language the user chooses.

GenY Implementation

As the core logic of Bot App execution, Component diagram starts with Http.ApiController base class. The application's object is derived from ApiController and named as MessageController.

Post method of MessageController class takes Message object and switches to the actions based on Type member of Message. If Type is part of pre-defined System data like ping, BotAdded, UserAdded, etc., then they are handled in a separate method named HandleSystemMessage. The custom message is handled/processed separately.

In our example, the non-system message is handled to count the number of characters and return to the caller.

Code Sample

As an extension of GenX Bot Coding, GenY is added with the collaborative effort of the external services. It is primarily achieved using the extension of ApiController class, namely MessageController.

As said earlier, there are two key asynchronous methods in MessageController namely:

  1. Post
  2. HandleSystemMessage

Post is the main entry of ApiController object. It is getting triggered when the message/request is received from the end user. The message context is getting passed in the method argument. This method is supposed to process the incoming request and sent the output response to the end user.

HandleSystemMessage is the handler method for non "Message" typed parameter of the incoming end user request. As depicted in the above sequence diagram, Handler node processes any sort of system message and takes the appropriate actions like BotAdded, UserAdded, etc.

C#
namespace StockBot
{
    /// MessageController class processes incoming requests, 
    /// handles user input and interactions, and executes 
    /// the appropriate application logic.
    /// by Ganesan Senthilvel
    [BotAuthentication]
    public class MessagesController : ApiController
    {
        /// Invoking GetStockRateAsync to retrieve stock
        /// value for the given StockSymbol
        private async Task<string> GetStock(string StockSymbol)
        {
            try
            {
                String retStockValue = await YahooBot.GetStockRateAsync(StockSymbol);
                if (retStockValue.Trim() == "")
                {
                    return string.Format
                    ("This \"{0}\" is not an valid stock symbol", StockSymbol);
                }
                else
                {
                    return string.Format
                    ("Stock symbol : {0} has Name: {1} with Price : {2} as on : {3}",
                        StockSymbol,
                        retStockValue.Split(',')[3],
                        Convert.ToDouble(retStockValue.Split(',')[1]),
                        retStockValue.Split(',')[2]);
                }
            }
            catch(System.FormatException ex)
            {
                return string.Format
                ("This \"{0}\" is not an valid stock symbol", StockSymbol);
            }
        }
        /// POST: api/Messages
        /// Receive a message from a user and reply to it
        public async Task<message> Post([FromBody]Message message)
        {
            if (message.Type == "Message")
            {
                // Parse the last word on the sentence
                String stockRate = await GetStock(
                    message.Text.Split(' ').Select(s=>s.Trim()).Last());
                return message.CreateReplyMessage(stockRate);
            }
            else
            {
                return HandleSystemMessage(message);
            }
        }

        /// Bot's Incoming message is handled in this method
        /// based on Message Type
        private Message HandleSystemMessage(Message message)
        {
            if (message.Type == "Ping")
            {
                Message reply = message.CreateReplyMessage();
                reply.Type = "Ping";
                return reply;
            }
            else if (message.Type == "DeleteUserData")
            {
                // Implement user deletion here
                // If we handle user deletion, return a real message
                Message reply = message.CreateReplyMessage
                                ("ServBot::You opted to delete User Data");
                return reply;
            }
            else if (message.Type == "BotAddedToConversation")
            {
                Message reply = message.CreateReplyMessage
                ("ServBot::You opted to connect the conversation");
                return reply;
            }
            else if (message.Type == "BotRemovedFromConversation")
            {
                Message reply = message.CreateReplyMessage
                ("ServBot::You opted to disconnect the conversation");
                return reply;
            }
            else if (message.Type == "UserAddedToConversation")
            {
                Message reply = message.CreateReplyMessage
                ("ServBot::You opted to add User into conversation");
                return reply;
            }
            else if (message.Type == "UserRemovedFromConversation")
            {
                Message reply = message.CreateReplyMessage
                ("ServBot::You opted to remove User from conversation");
                return reply;
            }
            else if (message.Type == "EndOfConversation")
            {
                var hours = DateTime.Now.Hour;
                String partDay = (hours > 16) ? "Evening" : 
                (hours > 11) ? "Afternoon" : "Morning";
                Message reply = message.CreateReplyMessage
                ("ServBot::Good " + partDay + " User!!" +
                    Environment.NewLine + "Local Time is: " + 
                    DateTime.Now.ToString());
                return reply;
            }
            return null;
        }
    }
}

In GenY mode, external App Service is added to find the stock price of the given equity listed quote. In our example, it is developed in a separate class named YahooBot, to retrieve the current stock price from Yahoo financial service.

From our main entry MessageController class, a new asynchronous method namely GetStock is developed to invoke AppServer YahooBot. GetStock method gets StockSymbol as the input parameter and returns the consolidated price output as the return value.

On execution of GenY code base, the result is:

GenY Result

GenZ

GenZ is the advanced stage of Microsoft Bot App development with the integration of Cognitive Services. It has the high intelligence capabilities by leveraging the pre-built Azure services.

Concepts

In terms of programming concepts, GenZ has the natural language understanding of the end user free text input, before GenY AppServer FinStock integration. It has the end user advantage to query Bot App in the natural way of asking questions, not in pre defined stock input parameter.

GenZ Concepts

Logically, there is a two step process in MyStockBot class, as depicted in the above conceptual diagram.

Implementation

The step by step implementation of GenZ cognitive BotApp development, is carefully written in the below flow chart. The coding execution process is broadly categorized into two segments:

  1. Microsoft LUIS Azure App Setup
  2. My custom .NET App Setup

Basically, the custom .NET code should be written after the initial setup in Microsoft Azure LUIS portal.

GenZ Implementation

According to luis.ai, LUIS is a Language Understanding Intelligent Service, which offers a fast and effective way of adding language understanding to applications. With LUIS, you can use pre-existing, world-class, pre-built models from Bing and Cortana whenever they suit your purposes and when you need specialized models.

LUIS guides you through the process of quickly building them. LUIS is a part of Microsoft Cognitive Service here. Details are written in MSDN here.

GenZ LUIS Set up

Code Sample

In terms of GenZ coding, the intelligence call is getting invoked at the source code line:

C#
Rootobject StLUIS = await GetEntityFromLUIS(message.Text);

Other than that, GenZ coding is kind of the extension on GenY source code. The cognitive service is getting invoked in the custom method GetEntityFromLUIS, before calling FinStock service. If you notice the last method in GenZ source code, it makes the call to the pre-built Azure service and returns the proper response on Success Status code.

Also, based on the configured intent in Azure service, our code will switch the multiple business cases as in the attached code.

C#
[BotAuthentication]
 public class MessagesController : ApiController
 {
     /// POST: api/Messages
     /// Receive a message from a user and reply to it
     public async Task<message> Post([FromBody]Message message)
     {
         if (message.Type == "Message")
         {
             string StockRateString;
             Rootobject StLUIS = await GetEntityFromLUIS(message.Text);
             if (StLUIS.intents.Count() > 0)
             {
                 switch (StLUIS.intents[0].intent)
                 {
                     case "StockPrice":
                         StockRateString = await GetStock(StLUIS.entities[0].entity);
                         break;
                     case "StockPrice2":
                         StockRateString = await GetStock(StLUIS.entities[0].entity);
                         break;
                     default:
                         StockRateString = "Sorry, I am not getting you...";
                         break;
                 }
             }
             else
             {
                 StockRateString = "Sorry, I am not getting you...";
             }

             // return our reply to the user
             return message.CreateReplyMessage(StockRateString);
         }
         else
         {
             return HandleSystemMessage(message);
         }
     }
     private async Task<string> GetStock(string StockSymbol)
     {
         double? dblStockValue = await FinStockBot.GetStockRateAsync(StockSymbol);
         if (dblStockValue == null)
         {
             return string.Format("This \"{0}\" is not an valid stock symbol",
                                   StockSymbol);
         }
         else
         {
             return string.Format("Stock Price of {0} is {1}",
                                   StockSymbol, dblStockValue);
         }
     }
     private static async Task<rootobject> GetEntityFromLUIS(string Query)
     {
         Query = Uri.EscapeDataString(Query);
         Rootobject Data = new Rootobject();
         using (HttpClient client = new HttpClient())
         {
             string RequestURI =
             "https://api.projectoxford.ai/luis/v1/application?id=7f626790-38d6-
             4143-9d46-fe85c56a9016&subscription-key=
             09f80de609fa4698ab4fe5249321d165&q=" + Query;
             HttpResponseMessage msg = await client.GetAsync(RequestURI);

             if (msg.IsSuccessStatusCode)
             {
                 var JsonDataResponse = await msg.Content.ReadAsStringAsync();
                 Data = JsonConvert.DeserializeObject<rootobject>(JsonDataResponse);
             }
         }
         return Data;
     }

If you notice, we have the communication object named 'Rootobject' during the Entity process of LUIS service. It is created as Microsoft LUIS recognizer that points at our model and adds it as the root dialog for our Cortana Bot.

Other than the newly added GetEntityFromLUIS method as the interface to Microsoft Cognitive Service, the other processing logic is pretty much the same like GenY code base.

On execution of GenZ code base, the result is:

GenZ Result

Chat Bot in Business

In Artificial Intelligence, Chat Bot plays a key tool by providing feedback to users on purchases with customer service agents on hand to provide further assistance.

In China, not only is WeChat used by close to two thirds of 16-24 year-old online consumers, but the service has capitalized on its massive market share by offering functionality well beyond simple messaging by attempting to insert itself into as many stations along the purchase journey as possible.

As the major part of digital consumers’ purchase journeys and online lives, Chat Bot will need to be non-intrusive, obviously beneficial to the user and, perhaps most importantly, present themselves as an honest assistant, not an advertisement in disguise.

FB Chat Bot

As the summarization of my analysis, two key business benefits of Chat Bot usage:

  1. High automation in manual contact center business; leads to cost reduction
  2. Continuous improvement (on usage) is possible with the usage of Machine Learning in AI intelligent Chat Bot

Chat Bots shift the shopping experience from browsing (web/retail stores) to recommendation. Bots learn about you, much like a trusted friend or personal shopper.

Conclusion

For now, a hybrid approach may be best to delve into Chat Bot. Let the technology perform the simplest tasks, but have human backup to handle more complex requests and questions.

What you research today may eventually underpin how you deploy a successful Chat Bot application for your business sooner rather than later once all the kinks get worked out.

History

  • 27th March, 2017: Initial version

License

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