Creating a Dialogflow CX Text Bot Using Avaya OneCloud CPaaS

“No matter how good you get you can always get better, and that’s the exciting part.”

Tiger Woods

In my previous article, I showed you how to integrate Google Dialogflow ES into an Avaya CPaaS text bot. While Dialogflow ES is a great platform for uncomplicated natural language processing (NLP) applications, ES has no concept of state or transitions. This is perfectly fine for an FAQ bot, but a more sophisticated solution often requires an understanding of where the user is within a workflow. Dialogflow CX, a significant upgrade from ES, allows a developer to create an AI agent that defines, manages, directs, and maintains state.

Teaching you how to write a CX agent is beyond the scope of this article, but there are plenty of online tutorials that walk you through the process. Take a few minutes to find something that works for you.

For the purposes of this discussion, I created a very simple small-talk agent that among other things, tells jokes. However, before it will tell you a joke, you need to pass out of the Start phase by sending the agent some form of salutation. Acceptable salutations include phrases such as “Hello,” “How are you,” “Good morning,” “Good evening,” and “Hi.” Once one of these phrases is encountered, the agent moves into the Request phase. Here, you can bombard it all kinds of small talk (“I love you,” “You are cool,” “How old are you,” etc.) as well as the main purpose of the bot, ask it to tell jokes (“Tell me a joke,” “Do you know any jokes,” “Can you tell jokes,” etc.). It will remain in the Request phase until the user closes the session (“Bye,” “Goodbye,” “Catch you later,” etc.). At that point, the agent enters the End Session phase and awaits another salutation to kick things off again.

My simple small-talk/joke CX agent looks like this:

Integrating a Third Party Application

Once you have a CX agent built and tested, you are ready to integrate it with an external application. This begins with creating a Service Account and obtaining an Account Key. Here are the steps that Google recommends:

Next, you need to gather information about your project. Specifically, you need the Project Id, Region Id, and Agent Id. These can be found via the following steps:

You are now ready to add these values and the reference to your JSON Account Key into your application. What follows is a node.js application that integrates with my small-talk/joke agent using my personal credentials — I show you everything but my JSON credentials.

Pay attention to the code that works with sessionMap. The application creates a unique sessionPath for every user. This allows it to independently manage transactions from different mobile devices at the same time.

/*

This Avaya CPaaS application accepts incoming SMS text messages and uses
Google Dialogflow CX to generate responses back to the sender.

*/

const express = require('express');
const request = require('request-promise');
const bodyParser = require('body-parser');
const cpaas = require('@avaya/cpaas'); //Avaya cloud
var enums = cpaas.enums;
var ix = cpaas.inboundXml;
const uuid = require('uuid');
const {SessionsClient} = require('@google-cloud/dialogflow-cx');

const URL_PORT = 5085; // If necessary, change to available port

var app = express();

// Middleware to parse JSON
app.use(bodyParser.urlencoded({
    extended : true
}));
app.use(bodyParser.json());

// Tell server to listen on port
var server = app.listen(URL_PORT, function () {
   var port = server.address().port;
   console.log("SMS Virtual Agent is listening on port %s", port)
});

// Initialize Google

// Change the following constants to match your environment
const projectId = 'andrewcx';
const location = 'us-central1';
const agentId = '376c9c7d-00e9-4d15-9df7-73de9586e21e';
const languageCode = 'en';

const client = new SessionsClient({
	keyFilename: "./andrewcx.json", // Change to filename of your Service Agent's JSON credentials
	apiEndpoint: "us-central1-dialogflow.googleapis.com" // Change to your location specific setting
});

var sessionMap = new Map();  // Key = User Phone Number, Value = DialogFlow SessionPath

// Entry point for sms text
app.post('/cpaas-sms/', function (req, res) {
	var sessionPath = sessionMap.get(req.body.From);
	if (sessionPath == null) {
		// Create new DialogFlow Session for this user
		var sessionId = Math.random().toString(36).substring(7);
		var sessionPath = client.projectLocationAgentSessionPath(
			projectId,
			location,
			agentId,
			sessionId
		);
		sessionMap.set(req.body.From, sessionPath);
	}
    processText(req.body.From, req.body.To, req.body.Body, sessionPath, res);
});

async function processText(from, to, body, sessionPath, res) {
	var prompt = await detectIntentText(body, from, sessionPath);
	returnTextResponse(prompt, from, to, res);
}

async function returnTextResponse(prompt, from, to, res) {
	var xmlDefinition = generateXMLText(from, to, prompt)
    var serverResponse = await buildCPaaSResponse(xmlDefinition);

    res.type('application/xml');
    res.send(serverResponse);
}

function generateXMLText(customer , cpaas , body) {
    var sms = ix.sms({
        text : body ,
        to : customer ,
        from : cpaas
    });

    var xml_content = [];
    xml_content.push(sms);
    var xmlDefinition = ix.response({content: xml_content});
    return xmlDefinition;
}

async function buildCPaaSResponse(xmlDefinition) {
      var result = await ix.build(xmlDefinition).then(function(xml){
          return xml;
      }).catch(function(err){
          console.log('The generated XML is not valid!', err);
      });
      return result;
}

//-----------  Google Dialogflow function

async function detectIntentText(query, from, sessionPath) {
  const request = {
    session: sessionPath,
    queryInput: {
      text: {
        text: query,
      },
      languageCode,
    },
  };
  const [response] = await client.detectIntent(request);
  if (response.queryResult.match.intent) {
	if (response.queryResult.match.intent.displayName == "small_talk.greetings.bye") {
		sessionMap.delete(from);  // Session is over
	}
  }
  return response.queryResult.responseMessages[0].text.text[0];
}

I left a lot of room to improve the application (e.g. I have almost zero error checking, I ignore most of the values from detectIntent(), and there are better ways than Map to manage sessions) but this should be more than enough to make you dangerous. Heck, it’s more than I had when I started writing this article.

As with my CX text bot, I need to attach the application to the SMS Configuration of an Avaya CPaaS number:

Seeing is Believing

You can try the application yourself. Simply text a salutation to 289-207-0398 to get the ball rolling. Here is an sample conversation between the bot and my Avaya Cloud Office account:

After the “Goodbye” Intent transition is encountered, the application discards sessionPath for this user and waits for another salutation to create a new session.

Mischief Managed

That’s all there is to it. The code is very similar to integrating with Dialogflow ES, but different enough to warrant a separate article. I hope you found this useful and are thinking up your own ways to integrate Dialogflow CX with text or voice technology from Avaya OneCloud CPaaS. These are two powerful platforms that go well together.

As always, feel free to reach out to me with any questions or comments.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: