Build An Enterprise Chat Bot For Service Now Integrations And QnA Based FAQs

Requirement

 
Recently, we had a requirement to build a chatbot for enterprise users, with features like creating a request in Service Now or determining the latest status of requests submitted in service now. In addition, the bot must act as a knowledge champion with respect to its department. 
 
Overall, the objective was to produce an intelligent chatbot to interact with users to provide solutions based on their needs.
 

Solution 

We have decided to utilize the Microsoft Bot Framework to build the chatbot. The solution consists of 3 components.
 
First, we use Azure Web App Bot; you can choose basic bot (C#) version 4. For development purpose, use the free app service plan (60 mins daily). 
 
Refer this link to find out more on Azure Bots.  
 
Second, Azure Cognitive Services for building LUIS and QnA Services. Luis service will determine the intent of the user through conversations, whereas QnA can be used for providing answers to user queries. 
 
For more details, refer LUIS and QnA Maker.  
 
In our case, I would like to use a dispatch tool to handle multiple LUIS or QnA models. This will ensure our design is capable of handling queries from different areas (say we have one QnA for Technical, another one for functional or business units). 
 
Third, we will have to develop Dialogs, which will handle the service now interactions. Let's say you want to report an incident to service now, in this case, end-user will be provided with a set of dialogs to capture the incident details. 
 

Implementation

 
Implementation is quite simple as you can find examples in GITHUB, 
 
Use below code to implement dispatch tool,  
 
https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/14.nlp-with-dispatch 
 
Use the below code to implement Dialogs,  
 
https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/19.custom-dialogs
 
Let's say you have created a dialog class, with the following events to capture inputs
  •  Ask the user whether he/she wants to raise an incident
  • Capture the incident details
  • Confirm the incident details
  • Create the incident and return the incident number
We can use prompt dialogs (Choice, Number, Datetime, etc.) to capture the incident details. 
  1. public ServiceNowDialog(UserState userAccessor): base(nameof(ServiceNowDialog)) {  
  2.     _userAccessor = userAccessor.CreateProperty < User > ("User");  
  3.     var waterfallSteps = new WaterfallStep[] {  
  4.         TicketCreationOptionsAsync,  
  5.         TicketInputSiteAsync,  
  6.         TicketInputShortDescriptionAsync,  
  7.         ConfirmStepAsync,  
  8.         SummaryStepAsync  
  9.     };  
  10.     AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));  
  11.     AddDialog(new TextPrompt(nameof(TextPrompt)));  
  12.     AddDialog(new NumberPrompt < int > (nameof(NumberPrompt < int > )));  
  13.     AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));  
  14.     AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));  
  15.     InitialDialogId = nameof(WaterfallDialog);  
  16. }  

Now, the tricky part is integrating these two features so that chat bot will become intelligent to handle both services now, i.e., Requests and FAQs. You can handle this in the root dialog class, (which we define as Startup.cs using AddSingleton). In Root Dialog class, navigate to the event named.

OnMessageActivityAsync retrieves the current conversation state with turncontext object. The object conversationData will have details of dialogs in use. it will give you the number of dialogs at present. If count is zero, you can call the dispatch tool to determine the intent of the user query, thereby collecting info to determine the action needs to be taken. The actions can be as follows, 
 
If count is zero -> call dispatch tool to determine the action based on user query. 
 
If count > zero -> call the dialog service to continue the process,
  1. protected override async Task OnMessageActivityAsync(ITurnContext < IMessageActivity > turnContext, CancellationToken cancellationToken) {  
  2.     Logger.LogInformation("Running dialog with Message Activity.");  
  3.     var conversationStateAccessors = _conversationState.CreateProperty < DialogState > (nameof(DialogState));  
  4.     var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new DialogState());  
  5.     if (conversationData.DialogStack.Count > 0) {  
  6.         //If count is greater than zero, then you can continue dialog conversation.  
  7.         await Dialog.RunAsync(turnContext, _conversationState.CreateProperty < DialogState > ("DialogState"), cancellationToken);  
  8.     } else {  
  9.         //If count is zero, call dispatch tool to determine the intent.  
  10.         var recognizerResult = await _botServices.Dispatch.RecognizeAsync(turnContext, cancellationToken);  
  11.         // Top intent tell us which cognitive service to use.  
  12.         var topIntent = recognizerResult.GetTopScoringIntent();  
  13.         // Next, we call the dispatcher with the top intent.  
  14.         await DispatchToTopIntentAsync(turnContext, topIntent.intent, recognizerResult, cancellationToken);  
  15.     }  
  16. }  
Finally, you can write the logic in DispatchToTopIntentAsync to manage the bot behaviour for each intent, as shown below. 
  1. private async Task DispatchToTopIntentAsync(ITurnContext < IMessageActivity > turnContext, string intent, RecognizerResult recognizerResult, CancellationToken cancellationToken) {  
  2.     switch (intent) {  
  3.         case "ServiceNow":  
  4.             await Dialog.RunAsync(turnContext, _conversationState.CreateProperty < DialogState > ("DialogState"), cancellationToken);  
  5.             break;  
  6.         case "HRDeptFAQ":  
  7.             await ExecuteOnA(turnContext, cancellationToken);  
  8.             break;  
  9.         case "None":  
  10.         default:  
  11.             await turnContext.SendActivityAsync(MessageFactory.Text(LowScoreMsg), cancellationToken: cancellationToken);  
  12.             break;  
  13.     }  
  14. }  
So in order to combine Dialogs (for Service Now Interactions) and QnA services, you can utilize the current state of Bot conversation. 
 
That's it. Happy chatbotting :) 
 
Cheers.
Kannan Muraleedharan