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.
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.
Based on Microsoft Bot Framework development evolutions, I personally categorize into three generations as:
- Gen X - Core (represents the foundation of Bot Coding)
- Gen Y - Collaborate (interfaces with the external business service)
- Gen Z - Cognitive (involves the artificial intelligence in the business logic)
It is diagrammatically represented as:
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 covers the development of Chat Bot foundation and its fundamental communication framework.
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.
Bot Framework Emulator is part and parcel of this package to simulate the client side component.
Code level implementation is drafted in the below sequence diagram.
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.
HttpConfiguration
class is in the System.Web.Http
library, which is a framework for building web APIs.
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.
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:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
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,
};
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.
public async Task<message> Post([FromBody]Message message)
{
if (message.Type == "Message")
{
int length = (message.Text ?? string.Empty).Length;
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:
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.
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.
There are three main SDK components of the Microsoft Bot Framework to build:
- 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
- Developer Portal - Let's connect the bot seamlessly text/sms to Skype, Slack, Facebook Messenger, Kik, Office 365 mail and other popular services
- 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.
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.
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:
Post
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.
namespace StockBot
{
[BotAuthentication]
public class MessagesController : ApiController
{
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);
}
}
public async Task<message> Post([FromBody]Message message)
{
if (message.Type == "Message")
{
String stockRate = await GetStock(
message.Text.Split(' ').Select(s=>s.Trim()).Last());
return message.CreateReplyMessage(stockRate);
}
else
{
return HandleSystemMessage(message);
}
}
private Message HandleSystemMessage(Message message)
{
if (message.Type == "Ping")
{
Message reply = message.CreateReplyMessage();
reply.Type = "Ping";
return reply;
}
else if (message.Type == "DeleteUserData")
{
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:
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.
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.
Logically, there is a two step process in MyStockBot
class, as depicted in the above conceptual diagram.
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:
- Microsoft LUIS Azure App Setup
- My custom .NET App Setup
Basically, the custom .NET code should be written after the initial setup in Microsoft Azure LUIS portal.
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.
In terms of GenZ
coding, the intelligence call is getting invoked at the source code line:
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.
[BotAuthentication]
public class MessagesController : ApiController
{
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 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:
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.
As the summarization of my analysis, two key business benefits of Chat Bot usage:
- High automation in manual contact center business; leads to cost reduction
- 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.
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.
- 27th March, 2017: Initial version