Microsoft Bot Framework: Building Intelligent Chat Bot Using Azure Functions and LUIS
Introduction
Any business which transacts online requires providing some kind of chat support for the issues related to their business. This can be done by developing chat applications where the consumers directly chat with the agent of the business who then solve their problems. A new alternative to answering frequently asked questions is the use of the Chat Bot which is intelligent enough to understand the questions raised by the user and determine the solutions to them on the real-time basis. Or in case of an administrator, if they are managing a lot of operative tasks or a lot of source code bases, then an intelligent bot is of great help.
For the bot to become intelligent, the important step is that the bot should understand the language in which humans communicate with the bot (e.g English, French, Hindi etc.). To facilitate this understanding Microsoft has provided developers with a powerful feature called as Language Understanding(LUIS). This feature can be used to understand the language by comparing the uttered sentence against the intents and the entities defined while creating the LUIS application.
When the bot is being developed, the LUIS app can be consumed from the azure functions using the Bot Builder Framework SDK. This Azure Function Bot can then be coded to perform various function after detecting the intents behind the user commands.
What are Azure Functions?
As per Microsoft
"Azure Functions is an event driven, compute-on-demand experience that extends the existing Azure application platform with capabilities to implement code triggered by events occurring in Azure or third party service as well as on-premises systems. Azure Functions allows developers to take action by connecting to data sources or messaging solutions thus making it easy to process and react to events. Developers can leverage Azure Functions to build HTTP-based API endpoints accessible by a wide range of applications, mobile and IoT devices. Azure Functions is scale-based and on-demand, so you pay only for the resources you consume."
What is LUIS?
As per Microsoft
"Language Understanding (LUIS) allows your application to understand what a person wants in their own words. LUIS uses machine learning to allow developers to build applications that can receive user input in natural language and extract meaning from it. A client application that converses with the user can pass user input to a LUIS app and receive relevant, detailed information back."
Key Concepts In LUIS
- Utterance: This is the actual command spoken/written by the User /APP
- Intents: Intents describes the actual intention behind the command given by the user.
e.g. when the User says "Find me all the Push made on the PublicWorks Repository", what user wants is the list of the Push made on the PublicWorks repository, hence Push made is the intent here. - Entity: This is extra information that is present in the utterance which helps the LUIS to understand the utterance more. In above e.g PublicWorks is the name of the repository and hence is the entity which gives the name of the repository on which the Push is to be queried.
Scope
This article discusses the use of the .NET Bot Builder SDK along with the Azure function to create a azure function bot which detects the user intent and brings back the list of all the repositories from the GIT for that particular user.
Design
The design for the bot can be broken down into following steps.
- Creating a LUIS application To Detect the Intents and Entities in the user utterance.
- Creating LUIS cognitive services account.
- Publishing the LUIS app.
- Creating an Azure function app to take appropriate actions based upon the user intents
- Registering the bot with various channels.
Creating LUIS Application
The main reason behind creating a LUIS application is to make the bot intelligent so that t can understand what the user wants and then reply back accordingly. The LUIS application will help the bot to understand the intents from the statements where the user greets the bot to the statements where the user wants the bot to fetch the list of the repositories etc. Each of the Intent created should have as many different variations of the utterances so that when the LUIS application is trained, it will be more intelligent. This training process should be undertaken frequently to keep the bot evolving.
Following steps will highlight how to create the LUIS application.
Note: For each of the created intents, utterances should be provided to train the app. A minimum of five should be provided but more the merrier.
Log On to the LUIS portal at https://www.luis.ai
Create a LUIS application named GitAssistantBotLuisApp. Refer sample screen shot below.
Create a intent called as WelcomeIntent as shown below. This intent will detect if the user is greeting the bot.
Create an intent called as HelpIntent as per following screenshot. This intent will detect if the user requires help from bot.
Create an intent called as SupportedFunctionsIntent as shown below. This intent will detect if the user wants to know the functions supported by the bot.
Create an intent called as ListRepositoryIntent. This intent will detect if the user wants to know the list of all the repositories under the account.
Create an intent called as the ActionOnRepositoryIntent . This intent will detect if the user wants to perform any action on any particular repository.
Create an Entity called as ActionEntity. This entity is used to record the action detected in the user utterance. e.g. If user says "Bring me all the commits on AdventureWorks repository" the commits is detected as ActionEntity as it denotes the action that needs to be performed against the repository.
Create an Entity called as RepositoryEntity. This entity is used to record the action detected in the user utterance. e.g. If user says "Bring me all the commits on AdventureWorks repository" the AdventureWorks is detected as RepositoryEntity as it denotes the action that needs to be performed against the repository.
Map the entities created above to the utterances added to the ActionOnRepositoryIntent . Refer sample screenshot.
Train the App using the Train button in the portal.
Creating LUIS Cognitive Service Account
In order to consume the LUIS app from the logic apps, the LUIS app must be published to the Resource group so that logic app connectors can use it, in order to do so, a LUIS cognitive services account need to be set up in the Azure. Refer to following steps to set up the account.
Publishing the LUIS Application
Once an account for LUIS is created, the publication of the app becomes very easy. Navigate to the Publish Tab in the LUIS portal and select the environment (Production/Staging) and the time zone.
Select the region where the LUIS app needs to be Hosted and click Add Keys, this will allow the LUIS portal to link the LUIS app to the LUIS account created above.
Once done, click on the publish button, this will publish the LUIS app into the LUIS account created earlier.
The LUIS app is now available to be consumed by the Azure Function App.
Creating Azure Function
Azure function can be created from the Azure portal and the code for the function app can be editied in the online editor itself.
Following are the steps that are to be undertaken to create the azure function app bot.
Log on to the Azure portal at https://www.portal.azure.com
Select the FunctionApp Bot as shown below. It is available under the AI + Cognitive Services tab.
Create the Bot by providing details asked. Refer a sample below.
Once the bot is deployed, open the bot in Azure functions as shown in reference below.
Function App Code
The function App project for the bot contains following C# script files.
- run.csx : This file is the main driving file for the azure function app and contains the code for accepting and replying back with the messages, responses to the user.
- BasicLuisDialog.csx : This file has the logic to select the messages based upon the intent detected.
- GitHelper.csx : This class contains the helper methods which can be used to consume the GitHub api. In this example the helper contains only the method to get the repositories related to the account (based upon a standrard user access token).
The reader should get familar with the concepts of bot and the classes and the Interfaces used to understand the code posted below. The user is urged to refer the See Also section to get the idea about the classes and interfaces and then return to the code.
run.csx
#r "Newtonsoft.Json"
#load "BasicLuisDialog.csx"
using System;
using System.Net;
using System.Threading;
using Newtonsoft.Json;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info($"Webhook was triggered!");
// Initialize the azure bot
using (BotService.Initialize())
{
// Deserialize the incoming activity
string jsonContent = await req.Content.ReadAsStringAsync();
var activity = JsonConvert.DeserializeObject<Activity>(jsonContent);
// authenticate incoming request and add activity.ServiceUrl to MicrosoftAppCredentials.TrustedHostNames
// if request is authenticated
if (!await BotService.Authenticator.TryAuthenticateAsync(req, new [] {activity}, CancellationToken.None))
{
return BotAuthenticator.GenerateUnauthorizedResponse(req);
}
if (activity != null)
{
// one of these will have an interface and process it
switch (activity.GetActivityType())
{
case ActivityTypes.Message:
await Conversation.SendAsync(activity, () => new BasicLuisDialog());
break;
case ActivityTypes.ConversationUpdate:
var client = new ConnectorClient(new Uri(activity.ServiceUrl));
IConversationUpdateActivity update = activity;
if (update.MembersAdded.Any())
{
var reply = activity.CreateReply();
var newMembers = update.MembersAdded?.Where(t => t.Id != activity.Recipient.Id);
foreach (var newMember in newMembers)
{
reply.Text = "Welcome";
if (!string.IsNullOrEmpty(newMember.Name))
{
reply.Text += $" {newMember.Name}";
}
reply.Text += "!";
await client.Conversations.ReplyToActivityAsync(reply);
}
}
break;
case ActivityTypes.ContactRelationUpdate:
case ActivityTypes.Typing:
case ActivityTypes.DeleteUserData:
case ActivityTypes.Ping:
default:
log.Error($"Unknown activity type ignored: {activity.GetActivityType()}");
break;
}
}
return req.CreateResponse(HttpStatusCode.Accepted);
}
}
Above code is auto generated when the bot gets created
BasicLuisDialog.csx
#load "GitHelper.csx"
using System;
using System.Configuration;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Luis;
using Microsoft.Bot.Builder.Luis.Models;
// For more information about this template visit http://aka.ms/azurebots-csharp-luis
[Serializable]
public class BasicLuisDialog : LuisDialog<object>
{
public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
await this.ShowLuisResult(context, result);
}
// Go to https://luis.ai and create a new intent, then train/publish your luis app.
// Finally replace "Greeting" with the name of your newly created intent in the following handler
[LuisIntent("WelcomeIntent")]
public async Task WelcomeIntent(IDialogContext context, LuisResult result)
{
await this.ShowLuisResult(context, result);
}
[LuisIntent("HelpIntent")]
public async Task HelpIntent(IDialogContext context, LuisResult result)
{
await this.ShowLuisResult(context, result);
}
[LuisIntent("SupportedFunctionsIntent")]
public async Task SupportedFunctionsIntent(IDialogContext context, LuisResult result)
{
await this.ShowLuisResult(context, result);
}
[LuisIntent("ActionOnRepositoryIntent")]
public async Task ActionOnRepositoryIntent(IDialogContext context, LuisResult result)
{
await this.ShowLuisResult(context, result);
}
[LuisIntent("ListRepositoryIntent")]
public async Task ListRepositoryIntent(IDialogContext context, LuisResult result)
{
await this.ShowLuisResult(context, result);
}
private async Task ShowLuisResult(IDialogContext context, LuisResult result)
{
string detectedIntent = result.Intents[0].Intent;
string messageToUser = string.Empty;
switch(detectedIntent)
{
case "WelcomeIntent":
messageToUser = $"Hola Amigo, I am chat bot of the Wonderland account.";
break;
case "HelpIntent":
messageToUser = $"Fear Not!! I can help you with activties like getting repositories in wonderland account. \n I can get the list of commit on a particular reposotory too!";
break;
case "ListRepositoryIntent" :
string repositoryList = await GetRepositories();
messageToUser = $"I will now bring you the list of the repositories in Wonderland. \n" + repositoryList;
break;
case "ActionOnRepositoryIntent" :
messageToUser = $"I will bring you the {result.Entities[0].Entity} for the respository {result.Entities[1].Entity}";
break;
case "SupportedFunctionsIntent":
messageToUser = $"I can perform following operations. \n 1. Get all the repositories in WOnderland \n 2. Get the list of commits, pulls on any particular repository present in Wonderland";
break;
case "None":
messageToUser= $"I detected no intent in your command";
break;
}
await context.PostAsync(messageToUser);
context.Wait(MessageReceived);
}
}
Above class implements the LuisDialog from the Microsoft Bot Builder SDK and generates the responses based upon the detected intents.
GitHelper.csx
#r "System.Web"
#r "Newtonsoft.Json"
using System.Net;
using System.Configuration;
using System.Web;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static async Task<string> GetRepositories()
{
string repositoriesList = string.Empty;
try
{
string GitAccessToken = ConfigurationManager.AppSettings["GitAccessToken"];
string GitHubApi = ConfigurationManager.AppSettings["GitHubApi"];
var client = new HttpClient();
var queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
var uri = GitHubApi + "/user/repos?access_token=" + GitAccessToken;
var response = await client.GetAsync(uri);
var jsonResponseString = await response.Content.ReadAsStringAsync();
var repositoriesArray = JArray.Parse(jsonResponseString);
foreach( var repository in repositoriesArray)
{
repositoriesList = repositoriesList + (string)repository["name"] + "\n";
}
}
catch(Exception ex)
{
repositoriesList = ex.Message;
}
return repositoriesList;
}
The task in this script file consumes the GIT HUB API, fetches a json response containing repository details, parses the response so the user only gets fed with the names of the repository.
Registering ChatBot To Skype Channel
The chat bot can be easily added to various channels. Following steps are used to add the chat bot to Skype.
On the Channel tab of the Bot project, select the Skype channel.
Configure the details like the messaging, calling, publishing by filling out the basic details and saving the channel (Refer Reference section).
Click on the Skype Icon so that it can be added to Skype for testing. Refer following screenshots.
Performing above steps adds the bot to the Skype Account.
Testing
Using Web Chat Client
Skype Chat
Conclusion
As evident from above process, it can be concluded that we can set up a server less intelligent bot in very less time. The bot can be configured to receive messages in other formats like images and sounds and then the other cognitive services can be used in conjunction with LUIS to create an intelligent bot. It is also evident that once the bot is created it is very easy to integrate the bot with different channels like Facebook Messenger, Skype, Skype for Business, Microsoft Teams etc.
Source Code
The Source code for this bot can be found at Source Code: Building Intelligent Chat Bot Using Azure Functions and LUIS .
How To Use the Source Code
Download the source code and unzip the file.
Create a function app bot as per instructions above.
Copy paste the code from the run.csx from downloads into the actual file in functions app editor.
Create two new files BasicLuisDialog.csx and GitHelper.csx (copy names from the downloaded files). Then copy paste code from each of the file.
Import the LUIS app into the LUIS portal. Train the App and follow the steps mentioned for creating the Account for app and Publish the LUIS app.
Add the key value pairs : LuisAppId, LuisAppId, LuisAPIHostName, GitAccessToken( Can be generated on GIT) and GitHubApi (https://api.github.com) to the app settings of the function app.
Follow other process for integrating the bot with Skype and testing as suggested above.
See Also
Refer following links to read more about the topics discussed in this article
- Logic Apps: Integrating LUIS Application With Logic Apps To Predict Intent in User Utterances
- Create your first function in the Azure portal
- Azure Functions C# script (.csx) developer reference
References
Following articles were referred while writing this article.
- About Language Understanding (LUIS)
- Introducing Azure Functions
- Asynchronous programming
- Asynchronous programming with async and await (C#)
- await (C# Reference)
- Bot Builder SDK for .NET
- Connect a bot to channels