IBM Developer

Tutorial

Integrate a custom live agent with IBM watsonx Assistant

Connect watsonx Assistant to an existing call center

By Christian Baltzer

With IBM watsonx Assistant, you can create a reliable chatbot for any web page or web service. The chatbot is easy to integrate and, if you set it up with enough data, can answer a wide variety of questions. However, there are some cases where you might still want to switch to a human operator to ask about more complex scenarios or to talk about specific situations.

Watsonx Assistant has preconfigurations to connect the system to an existing service platform, such as Tello, Zendesk, or Salesforce. But what if you are not using your own platform? How do you connect your chatbot to your live agent and your call center software?

In this tutorial, you integrate an existing watsonx Assistant into an existing web page. You then connect the Watson service to a custom back end and create simplified call center software for a live agent. This tutorial assumes that you have already deployed the watsonx Assistant and configured it on IBM Cloud. If you want to know more about setting up the watsonx Assistant, see the Get started with watsonx Assistant learning path.

For this example, you use a starter kit that includes the necessary components for a fast test deployment. The kit is also great if you don’t have an existing web page.

Step 1: Set up a Watson chatbot

If you have an existing web page, integrating a chatbot with watsonx Assistant is surprisingly easy.

The watsonx Assistant provides an HTML snippet that you can insert into your web page. The given code downloads a JavaScript library that renders the UI and connects the interface to the cloud. You can configure the UI in the Settings menu of the Assistant. In the third line of this code listing, *** represents custom keys that you can find in the settings of your watsonx Assistant.

window.watsonAssistantChatOptions = {
        integrationID: "***",
        region: "us-south",
        serviceInstanceID: "***",
        onLoad: function(instance) { instance.render(); },
    };
    setTimeout(function(){
    const t=document.createElement('script');
    t.src="https://web-chat.global.assistant.watson.appdomain.cloud/versions/" + (window.watsonAssistantChatOptions.clientVersion || 'latest') + "/WatsonAssistantChatEntry.js"
    document.head.appendChild(t);
});

alt

Now you can start chatting!

If you try to connect to a live agent and you are using your page without further modifications, nothing will happen. If you are using the test kit, you are connected to Captain Kirk. This is a demonstration of a live agent by the starter kit. Chatting with the captain can be nice and he will even answer with his famous quotes, but he won't help you with your questions, so you still need to call a real live agent.

Step 2: watsonx Assistant chat architecture

First, take a step back and think about your main setup. You want to build a system that can handle the communication between multiple users of your web service and your agent. Therefore, you can assume that you have two personas: a client named Carl and an agent named Anni. Carl must be able to start a conversation with the Watson chatbot and also be able to start a conversation with Anni, if needed. Anni needs to be able to chat with multiple Carls in different, separated chats.

To keep it simple, assume that you have only one Anni.

Consider a private chat versus chat rooms

For your chat system, look at the architecture from a more abstract level.

You need your two personas, the agent (Anni) and the customer (Carl), to be able to chat. You can achieve this by implementing either a private chat or chat rooms. The difference between these two methods is the routing. If you have a private chat, each message has to be forwarded to the receiver by the server. In a chat room, the message is just published, and everyone who is part of that chat room can access it. To keep it simple, this tutorial uses chat rooms. In reality, a chat room might not be very secure because everyone could join the room, but it is enough for this demonstration.

Therefore, you need a chat server that can handle multiple chat rooms. You also need two web pages: one that can connect to all the available rooms and one that can only connect to one specific room. The server also needs an API to access basic information, such as which room is open.

To connect a web page to the chat server, you can use the web socket protocol to exchange messages between the client and server on a specific TCP/IP socket.

image shows architecture of chat system

For this case, you also need different data stores for storing messages or to save the existing cache.

Define the process for your live agent

Before you begin, you need to define what the system has to do.

The following steps outline the process for when a customer (Carl) requests a live agent (Anni):

  1. Carl requests a live agent.
  2. The customer front end sends a request to the back end.
  3. The back end creates a new chat room and returns the ID.
  4. The front end connects to this room.
  5. The chat with Anni starts.

The following steps outline the process from Anni's perspective:

  1. Annie opens the Assistant front end.
  2. The front end checks every 3 seconds to see if a new chat room is available.
  3. If a new chat room is given, the front end connects to the given room and renders a new chat.
  4. The chat with Carl starts.

Step 3: Build the microservices

You need multiple services to create the final system. You could also create the whole functionality with a single program, but that scenario is unlikely to be used in a modern architecture. Additionally, you need some way to route the traffic coming from either outside the system or from service to service.

In my case, I've created multiple microservices, created containers and deployed everything in an OpenShift cluster. You could create a similar system by using any other Kubernetes cluster or Docker (if you use Docker, you can use NGINX proxy or Traefik to set up a proxy to handle the traffic).

Set up a back-end system for the chat

For this tutorial, you set up the back-end service with Django. Django is a Python framework that makes it easy to create data models and API endpoints. It also supports plug-ins for different tasks (for example, you can use the channels plug-in to set up WebSockets). You can also use other frameworks such as Java Spring to create the API.

No matter what technology or language you use, you need to set up API endpoints and WebSocket functions that can do the following:

  • Start a new chat
  • End an existing chat
  • Get all chats
  • Check if a specific chat is still open

Set up the second frontend for our agent

In Step 1, you created a front end for customer Carl. Now, you create a second front end for agent Anni.

For this purpose, you use a React app, but any framework or programming language that supports HTTP calls will work. The advantage of a web page is that it can be used almost anywhere. The following image shows your view, which consists of two main parts and an additional component. On the left, you can see the menu to choose the customer to chat with, and on the right, you can see the actual chat. I filled in some extra information (such as name, image, address, and bonus program status) that could help the live agent. The information seen here is generated randomly by using a Faker library in the back end.

Image showing a view of the chat

Because you are using the web socket protocol, every message you send to the server is sent back into the chat, so you have extra filters in place not to render those.

Connect the watsonx Assistant chat to the back end

Remember the starter kit I mentioned in the beginning? Here is a closer look. The index.html file has the Watson script that you inserted in the first step, but this time with a small modification: The WatsonAssistantChatOptions now has the serviceDeskFactory: window.WebChatServiceDeskFactory property. This property calls the WebChatServiceDeskFactory, the factory that creates the service desk object that represents the connection to your back-end system.

The starter kit already includes everything you need, but you do need to make some slight modifications. Even though there are already some options for the service desk present, such as a version for the service Tello, you will create a custom CustomServiceDesk that fits your back end.

In the src folder, find the file buildEntry with the function getInstance. This function is basically the setup for a serviceDeskClass, so you can insert the one you create later.

function getInstance(parameters: ServiceDeskFactoryParameters): CustomServiceDesk {
  const serviceDeskClass: string = process.env.SERVICE_DESK_CLASS || 'CustomServiceDesk';
  const constructors: any = {
    CustomServiceDesk,
  };
  return new constructors[serviceDeskClass](parameters);
}

Before this works, you need to create the actual CustomServiceDesk class. In the folder serviceDesks, you can find a template for this purpose, called serviceDeskTemplate. Copy this file and call the copy CustomServiceDesk.

This class basically consists of all the functions that are supported by the watsonx Assistant. For the moment, the most important ones are startChat, endChat, and sendMessageToAgent.

The startChat function is called when an agent is requested in the chat with watsonx Assistant. Here, you can implement the process you planned and set up the WebSocket connection. You also need to create a profile for your agent.

After the WebSocket is open, you can tell watsonx Assistant by calling the callback object and the function agentJoined:

this.agentProfile = {
                        id: '12345',
                        nickname: 'Test-Agent',
                    };
                   this.callback.agentJoined(this.agentProfile);

Next, you set up the functions EndChat and sendMessageToAgent. Both are pretty straightforward. The Function sendMessageToAgent is just sending messages to the WebSocket:

async sendMessageToAgent(message: MessageRequest, messageID: string): Promise<void> {
        const text_data_json = {
            message: message.input.text,
            sender: this.agentProfile.id,
        };

        this.websocket.send(JSON.stringify(text_data_json));
    }

You can ignore the line with sender; I have this information here just to render the messages correctly.

To end the chat, you do two things: send a message to the back-end API to signal that the conversation is over, and close the WebSocket.

async endChat(): Promise<void> {
        fetch('/api/chat/closeChat', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                roomID: this.roomID,
            }),
        }).then(() => {
            this.websocket.close();
        });
      }

How the functions are called

If you implemented your code correctly, you have defined what should happen in a given scenario, such as the command to end the chat. But how is the scenario recognized? So far, you have only implemented the logic that should be executed.

When the watsonx Assistant is responding to a message, it not only returns the given answer to the message, but it also returns additional information such as the discovered intent. The code you loaded before is scanning these intents. If a specific intent is found, the client assumes that the user wants to start an action, like being connected to an agent. The script therefore calls a specific function in your implantation.

It is important that you know this, because occasionally, you might have changed the returned string by changing the watsonx Assistant actions. The framework then can't detect the requested action and will never start a given function.

The conversation history

A handy feature for the agent is to see the chat history of the customer and the watsonx Assistant, so that the agent won't ask the same questions again.

There is just one problem: As the conversation is transferred to the agent, the chat history between the client and the watsonx Assistant is only known to the client and can't be easily accessed. However, there is a way you can get the data anyway.

Every chat between the watsonx Assistant and a client has a history ID, which you can access with front-end logic. You can use the information that is stored in this ID to get the history from the Assistant itself:

    var data = historyID.split("::")

    fetch("https://integrations.us-south.assistant.watson.appdomain.cloud/public/chat/" 
    + data[4] + "/channel_conversations/" 
    + data[3] + "/notes?version=2020-09-24")
    .then((response) => {
        if (response.ok){
            return response.json();
        }else{
            throw Error("Response should be ok, was " + response.status)
        }
    })

This information contains all the history stored in the watsonx Assistant. It also contains internal system information and can be incomprehensible for the normal user. Once you have the history filtered, you can then add the messages to the existing view as information for your agent.

You can now start your services. If you start a conversation with watsonx Assistant and get redirected to the live agent, you will see the new chat appearing in the agent view. You can then start chatting.

Where to go from here

In this tutorial, you created a basic chat system between the customer and a live agent. The basic system you created lacks many features, such as a function that only opens the chat if there really is an agent online or an indication if the other person is currently typing. Some of these functions are already present in the example project, and you can implement them with ease.