The Intelligent Agent Assistant: Bots in the Agent Desktop – Part 2


This post is the second in a series in which we explore how we can leverage natural, conversational experiences powered by automated intelligence to empower the CSR, by building a contextually-aware Intelligent Agent Assistant, integrated into the Unified Service Desk agent desktop.

We will build upon the bot that we brought into the Unified Service Desk and enabled to communicate with the Unified Service Desk by initiating actions within the desktop in Part 1.

Part 2 of our series will focus on extending this functionality by:

  • Proactively provide contextually-relevant information and alerts to the CSR via the integrated bot
  • Enabling administration of the proactive alert content, buttons, and actions from within the Unified Service Desk configurations

To see a 2-minute video that shows the final result we are building towards, check out Part 1 here.

 
Snapshot of Final Result

 

Extending our Intelligent Agent Assistant

Now that we have surfaced our bot in USD using the Direct Line channel, and the WebChat control in a USD Standard Web Application hosted control, and enabled the bot to trigger actions in USD when buttons are clicked through USD’s event moniker syntax, let’s move on to triggering proactive alerts for the CSR in the bot.

The alerts will be raised in a three-step process:

  1. An Action Call in USD will use the RunScript Action to inject JavaScript into the Bot Assistant control, including JSON data
  2. JavaScript in the Bot Assistant will use the Backchannel to send an event to the bot with the JSON data
  3. The bot will receive the event, and use the received data to create an alert to the user

 

We’ll see how we can do this for the example of a "High Churn Risk" warning to our agent, based on negative customer sentiment.

 

Initiating Messages to our Bot with a USD Action Call

First, we create an Action Call in USD that will be called whenever we open up a case, initiated from the BrowserDocumentComplete event of the Incident control which we use to load our cases.

The Action Call uses the RunScript Action, and passes in a payload of JSON data and other parameters into a postMessage event in the Bot Assistant:

 

 

We add a condition such that the Action Call only executes if the detected sentiment on the case is below a threshold score of 0.35:

 

 

The full JavaScript code in our Action Call data is shown here. Note how the JSON data contains all of the information required to construct a rich Thumbnail Card in our bot, including URLs for our buttons that use the event moniker syntax from Part 1 to raise events in USD when clicked:

var messageData = '{' +
'"introText":"Important Alert - High Churn Risk",' + 
'"cards":[' +
'{ "imageUrl":"https://<YourAzureSubdomain>.blob.core.windows.net/mycontainer/warningicon.png",' +
'"alertTitle":"Dissatisfied Customer Offer",' +
'"alertSubtitle":"",' +
'"alertText":"This customer has had multiple negative experiences recently. You are authorized to offer a 50% discount on next month\'s purchases",' +
'"buttons":[' +
'{ "title":"View Offer" , "type":"openUrl", "url":"http://event/?eventname=ViewOfferRequested" },' +
'{ "title":"Send Offer" ,  "type":"openUrl", "url":"http://event/?eventname=SendOfferRequested" }' +
']' +
'}' +
']}';
var channelData = JSON.parse(messageData);
postMessage("event","", "botMessageAlert", channelData, true);

 

Using the Backchannel to Raise Events with our Bot

As outlined in the Backchannel example on Github, by creating a DirectLine object in our page, and then sharing it when creating our WebChat instance, we are then able to use the DirectLine object to post activities such as events to our bot.

We now create our postMessage JavaScript function referenced in our Action Call. It receives parameters and uses them to post an activity to our bot via DirectLine, we will have a means of sending information via activities to our bot programmatically. Our postMessage method includes the following parameters:

  • activityType – the type of activity, such as event, message, typing, etc; in our example, we will be sending activities of type event
  • activityText – the text property for the activity
  • activityName – the name of the event we are raising
  • activityData – the channelData that we will receive in the activity; this will be used to pass important event data that the bot will use
  • showBotRequest – a parameter that will dictate whether or not we wish to raise an event in USD which will cause the Intelligent Agent Assistant to be displayed in the agent desktop if it is not already; this will be helpful in our alerts to the agent

 

The function will receive the parameters, use them to post an activity to the bot, and then will trigger a USD event to display the bot, if appropriate:

...

var botConnection = new BotChat.DirectLine({
    secret: params['s'],
    token: params['t'],
    domain: params['domain'],
    webSocket: params['webSocket'] && params['webSocket'] === "true" // defaults to true
});

BotChat.App({
    botConnection: botConnection,
    user: user,
    bot: bot
}, document.getElementById("BotChatGoesHere"));

function postMessage(activityType, activityText, activityName, activityData, showBotRequest) {
    botConnection.postActivity({ type: activityType, value: "", from: { id: user.id, name: user.name }, name: activityName, text: activityText, channelData: activityData })
    .subscribe();
    if (showBotRequest == true) {
        window.open("http://event/?EventName=ShowBotRequest");
    }
}

...

 

Receiving Events in our Bot

We now need to enable our bot to receive our events, and respond appropriately.

We can do this in our Message Controller class. Typical dialog-based samples for the Bot Framework will send received activities that are Message type to a dialog class, and will have a separate method to handle other types of messages. This is where we can include our code to handle our incoming events.
We adapt the HandleSystemMessage method to include logic to handle events. In this method, we:

  • check to see what the name of the event is; if it is named botMessageAlert, we will:
  • create a ConnectorClient
  • create a message reply
  • call our FormatGenericMessage method, passing in our reply and our JSON data and receive our formatted message as output
  • use our ConnectorClient to send the message back to our end-user

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Microsoft.Bot.Connector;
using Newtonsoft.Json;
using Microsoft.Bot.Builder.Dialogs;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;


namespace USDAgentBot
{
    [BotAuthentication]
    public class MessagesController : ApiController
    {
        /// <summary>
        /// POST: api/Messages
        /// Receive a message from a user and reply to it
        /// </summary>
        public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
        {
            if (activity.Type == ActivityTypes.Message)
            {
                await Conversation.SendAsync(activity, () => new LuisServiceDialog());
            }
            else
            {
                await HandleSystemMessage(activity);
            }
            var response = Request.CreateResponse(HttpStatusCode.OK);
            return response;
        }

        private async Task<Activity> HandleSystemMessage(Activity message)
        {
            if (message.Type == ActivityTypes.DeleteUserData)
            {
                // Implement user deletion here
                // If we handle user deletion, return a real message
            }
            else if (message.Type == ActivityTypes.ConversationUpdate)
            {
                // Handle conversation state changes, like members being added and removed
                // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
                // Not available in all channels
            }
            else if (message.Type == ActivityTypes.ContactRelationUpdate)
            {
                // Handle add/remove from contact lists
                // Activity.From + Activity.Action represent what happened
            }
            else if (message.Type == ActivityTypes.Typing)
            {
                // Handle knowing tha the user is typing
            }
            else if (message.Type == ActivityTypes.Event)
            {
                // Handle event
                if (message.Type == ActivityTypes.Event &&
                    string.Equals(message.Name, "botMessageAlert", StringComparison.InvariantCultureIgnoreCase))
                {
                    ConnectorClient connector = new ConnectorClient(new Uri(message.ServiceUrl));
                    IMessageActivity genericMessage = (IMessageActivity)message.CreateReply("");
                    genericMessage = LuisServiceDialog.FormatGenericMessage(genericMessage, (JObject)message.ChannelData);
                    await connector.Conversations.ReplyToActivityAsync((Activity)genericMessage);
                }
            }
            else if (message.Type == ActivityTypes.Ping)
            {
            }

            return null;
        }
    }
}

 

Our FormatGenericMessage receives our reply message and JSON data, and uses the JSON data to create a well-formatted Thumbnail type of rich card:

public static IMessageActivity FormatGenericMessage(IMessageActivity message, JObject channelData)
{
    message.Type = "message";
    message.Attachments = new List<Attachment>();
    message.AttachmentLayout = AttachmentLayoutTypes.Carousel;
    message.Text = (string)channelData["introText"];
    message.TextFormat = TextFormatTypes.Markdown;

    JArray cards = (JArray)channelData["cards"];
    for (int j = 0; j < cards.Count; j++)
    {

        List<CardAction> cardButtons = new List<CardAction>();

        List<CardImage> cardImages = new List<CardImage>();
        cardImages.Add(new CardImage(url: (string)cards[j]["imageUrl"]));

        JArray buttons = (JArray)cards[j]["buttons"];
        for (int i = 0; i < buttons.Count; i++)
        {
            CardAction Button = new CardAction()
            {
                Title = (string)buttons[i]["title"],
                Type = (string)buttons[i]["type"],
                Value = (string)buttons[i]["url"]
            };
            cardButtons.Add(Button);
        }

        ThumbnailCard genericCard = new ThumbnailCard()
        {
            Title = (string)cards[j]["alertTitle"],
            Subtitle = (string)cards[j]["alertSubtitle"],
            Text = (string)cards[j]["alertText"],
            Images = cardImages,
            Buttons = cardButtons
        };

        Attachment genericAttachment = genericCard.ToAttachment();
        message.Attachments.Add(genericAttachment);
    }
    return message;
}

 

Now, when we load a case in the Unified Service Desk, our bot will proactively display an alert as shown below, with buttons that will trigger Events and associated Action Calls in USD when clicked:

 

 

Stay tuned for more, as we cover:

  • Making our bot aware of contextual information from USD
  • Enabling our bot for engagement via speech, in addition to typing
  • and more
Comments (0)

Skip to main content