Chatbase is a cross platform plug-and-play service that helps chatbot developers accelerate finding product-market fit by providing key bot metrics and workflows for fixing bots. By integrating your bots into the Chatbase analytics service developers can get detailed insights into how effectively chatbots handle specific interactions.

What you will build

In this codelab, you're going to build and integrate a basic chatbot with the Chatbase analytics API. Your final application will feature:

  • A basic, Node.js microsoft-bot-kit chatbot or your own chatbot integrated with the Chatbase REST API

What you'll learn

What you'll need

Download the Code

Click the following link to download all the code for this codelab:

Download source code

You can also clone the repo from GitHub:

git clone https://github.com/googlecodelabs/chatbase-codelab-io-2017.git

Unpack the downloaded zip file. This will unpack a root folder (chatbase-codelab-io-2017), which contains one folder for each step of this codelab, along with all of the resources you will need.

The step-N folders contained in the steps/ directory contain the desired end state of each step of this codelab. They are there for reference. We'll be doing all our coding work in the root of the unzipped directory.

Install the dependencies

Open a shell and point it to the root of the newly unzipped or cloned chatbase-codelab-io-2017 directory and run the following command to install the application dependencies:

npm install

This command should run for a couple seconds and then we should be ready to start looking at our chatbot!

Getting familiar with our bot

Your chatbase-codelab-io-2017 folder should have the following directory structure:

chatbase-codelab-io-2017/
  | node_modules/
    | [...various application dependencies]
  | constants/
    | gorge-of-eternal-peril.js
  | dialogs/
    | gorge-of-eternal-peril.js
  | steps/
    | [step-0/ - step-N/]
  | index.js
  | package.json
  | .gitignore

Our bot currently sets up its potential dialogs that it might have with a user in index.js. We won't have to modify the contents of index.js for the rest of this codelab but if you wanted to have multiple dialog options index.js would be the location to add them. The file constants/gorge-of-eternal-peril.js contains all the string constants our bot needs to help us cross the gorge of peril. We won't have to modify that file either. The file dialogs/gorge-of-eternal-peril.js contains an exported array of functions which controls the flow and format of our bots responses to our users; the botbuilder framework refers to these arrays of functions as waterfalls. dialogs/gorge-of-eternal-peril.js is where we will be doing all of our work for this codelab, let's take a brief look:

[This is a file excerpt -- please do not copy and paste over your local copy]

module.exports = [
  function (session) {
    // Prompt the user to confirm they would like to cross the gorge
    builder.Prompts.text(session, constants.questions.stop.q);
  },
  function (session, results) {
    // Check to see if the user wants to cross the gorge
    if (!constants.questions.stop.check(results.response)) {
      // Apparently they don't so in Monty Python style send them on their way
      session.send(constants.taunt);
      // End the dialog to open a new session on the console
      session.endConversation();
    } else {
      // Otherwise they chose to proceed, prompt them with the first question:
      // "What is your name?"
      builder.Prompts.text(session, constants.questions.name.q);
    }
  },
  function (session, results) {
    // Ask the second question: "What is your quest?"
    builder.Prompts.text(session, constants.questions.quest.q);
  }
  ...
]

Here you may be able to identify a common pattern in Chatbots: handled and not handled dialog chains. This branching pattern is evident in our second function wherein we check to see whether the user has confirmed to our bot whether or not they would like to proceed in attempting to answer the bots three questions. In this case the check we are using to identify whether or not the user would like to proceed is a simple phrase whitelisting function which can be found in our constants/gorge-of-eternal-peril.js file and should look something like this:

questions: {
    stop: {
      q: [
        'Stop! Who would cross the Bridge of Death must\nanswer me these',
        'questions three, \'ere the other side he see.\n Dare thouest cross?'
      ].join(' '),
      check: input => input.match('yes')
    },
...

The check function in this case would leave a lot to be desired for any real bot but it's simplicity identifies a critical problem area for chatbot and human interaction -- certain phrases in certain contexts are sometimes not identified as valid input for bots while being developed. Case in point: if I responded ‘ok' to this prompt which should be completely fine I would still get booted from the dialog chain. Luckily for us, the Chatbase analytics system is made to identify, store and report these not handled phrases while making it easy to identify which not handled phrases are most prevalent amongst your chatbot user-base.

Running our bot for the first time

In your shell pointed at the root of our chatbase-codelab-io-2017 folder run the following command to start our chatbot:

npm start

And you should get some output in your shell that looks like this:

$ npm start

> ~/chatbase-codelab-io-2017@0.0.1 start ~/chatbase-codelab-io-2017
> node index.js

[[PRESS ENTER TO BEGIN]]

You can walk through the prompts to see what happens and whether or not your Monty Python trivia is complete enough to cross the gorge (hint: answers are in the constants/gorge-of-peril.js file).

The Chatbots Dialog Visualized

Understanding when our bot should report analytics to Chatbase will help us accurately identify areas where your bot is failing to respond in a useful manner. This also allows us to calculate and display powerful visualizations of your bot's user journeys in the Session Flow Report.

For reference purposes our chatbots dialog options roughly equivocate to the following flow-chart:

Even in our chatbots basic form we could ‘handle' the user saying no to the dialog or getting the third question incorrect but for the purposes of this codelab we will consider those paths not handled and use chatbase to aggregate and analyze information about what events/information lead to those not handled paths.

Getting your Chatbase API key

Point your browser to the following url: https://chatbase.com/bots/main-page and you should see something like the following appear:

Click the ‘+' button in the ‘Add a Bot' section to create a new API key so we can integrate our bot with the Chatbase API. Clicking this button should open up a ‘Create agent' form:

Enter your desired agent name (it can be anything right now) and click the ‘create' button. This should result in the following page being displayed:

You don't have to memorize and/or copy the key right this moment -- clicking finish will take you to the main page where all your agents and their corresponding keys are listed:

Keep this window open for now -- in our next step we will integrate our existing chatbot with the Chatbase API library and our newly created API key and agent name will be needed.

Now is the time to integrate our basic chatbot with Chatbase so we can collect information about our Chatbots interactions. Each phase of the next process will include two examples when applicable: an example demonstrating how to integrate with the Chatbase API via the Node.js Chatbase wrapper and an analogous curl command which should demonstrate what endpoint be used and what data should be sent if you have decided to not use the Node.js Chatbase wrapper. Each subsection of this step will contain a corresponding step number which points to the corresponding desired result in the steps/ directory.

Include the Chatbase library

Step: steps/step-1

Open the dialogs/gorge-of-eternal-peril.js file and add the following snippet to the top of the file:

const chatbase = require('@google/chatbase')
  .setApiKey('[your api key]')  // Your api key
  .setUserId('some-unique-user-id') // The id of the user you are interacting with
  .setPlatform('gorge-of-peril') // The platform the bot is interacting on/over
  .setVersion('1.0') // The version of the bot deployed
  .setIntent('cross-gorge-of-peril'); // the intent of the user message

Your dialogs/gorge-of-eternal-peril.js file should look like the file steps/step-1/dialogs/gorge-of-eternal-peril.js. Make sure to replace the value for setApiKey with your newly created API key. In most production environments setUserID should be called on each message individually with a unique string identifying what user the bot is currently interacting with. In this case we set it to a constant string since there will be only one user interacting with this bot for the time being (us!).

We're ready to start sending data back to the Chatbase!

Sending our first message to Chatbase:

Step: steps/step-2

In the dialogs/gorge-of-eternal-peril.js file locate the second function in the exported array and modify it to look like the following:

function (session, results) {
    if (!constants.questions.stop.check(results.response)) {
      // BEGIN MODIFICATION TO LAST STEP 1/2
      chatbase.newMessage()
        .setAsNotHandled()
        .setMessage(results.response.toString())
        .send()
        .catch(e => console.error(e))
      // END MODIFICATION TO LAST STEP 1/2
      session.send(constants.taunt);
      session.endConversation();
    } else {
      // BEGIN MODIFICATION TO LAST STEP 2/2
      chatbase.newMessage()
        .setMessage(results.response.toString())
        .send()
        .catch(e => console.error(e));
      // END MODIFICATION TO LAST STEP 2/2
      builder.Prompts.text(session, constants.questions.name.q);
    }
  }

Your dialogs/gorge-of-eternal-peril.js file should look like the file steps/step-2/dialogs/gorge-of-eternal-peril.js. What did we just do? Well two things actually but let's first clarify what this function's purpose is. This function is executed when the user starts the dialog with our bot in which there are two possibilities: the user decides to answer the questions or the user decides to refrain. In the case of the user deciding to not answer any of the questions we count that as not handled behaviour and create a chatbase message which reflects that event along with the data that triggered it:

// BEGIN MODIFICATION TO LAST STEP 1/2
chatbase.newMessage()
  // The bot was not able to handle some input
  .setAsNotHandled()
  // Attach the input that the bot did not understand
  .setMessage(results.response.toString())
  // Send it to the chatbase service
  .send()
  // Catch and print any errors when attempting to send
  .catch(e => console.error(e))
// END MODIFICATION TO LAST STEP 1/2

If the user does decide to answer the questions we send a message to Chatbase notifying the service of our bots success in handling the input:

// BEGIN MODIFICATION TO LAST STEP 2/2
// New chatbase messages default to the ‘handled' state
chatbase.newMessage()
  // Set the input that the chatbot handled
  .setMessage(results.response.toString())
  // Send it to the chatbase service
  .send()
  // Catch and print any errors
  .catch(e => console.error(e));
// END MODIFICATION TO LAST STEP 2/2

We now have our first handled/not handled switch in our dialog template! Go ahead and run your application and enter anything except ‘yes' when the bot asks you if you would like to answer the questions:

What's happening behind the scenes? The Chatbase Node.js wrapper abstracts a simple, user-friendly REST API which can be addressed without the wrapper. The equivalent calls in curl syntax look like the following:

Curl ‘not-handled' equivalent:

curl -X POST \
  https://chatbase-area120.appspot.com/api/message \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{
  "api_key": "[your api key]",
  "type": "user",
  "user_id": "some-unique-user-id",
  "platform": "gorge-of-peril",
  "message": "nope",
  "not_handled": true
}'

Curl ‘handled' equivalent:

curl -X POST \
  https://chatbase-area120.appspot.com/api/message \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{
  "api_key": "[your api key]",
  "type": "user",
  "user_id": "some-unique-user-id",
  "platform": "gorge-of-peril",
  "message": "yes",
  "not_handled": false
}'

If you go back to the Chatbase website and select ‘Messages' you should see your interaction logged as ‘Not Handled'. Make sure, though, that the interface is tabbed to your newly created agent:

Hooray!! We already broke our bot and tracked its breakage in style. You can press the return (enter) key in your shell again to restart the dialog with the bot and try saying ‘yes' at which point the success case will be reported to the Chatbase analytics service. Otherwise let's move on to augmenting our third and fourth dialog functions with even moar Chatbase analytics functionality!

Sending success messages at each step:

Step: steps/step-3

It's important to track your bots success as well as its failure. In the next step we'll add two very basic analytics success calls to our third and fourth dialog functions. In the dialogs/gorge-of-eternal-peril.js file locate the third and fourth functions in the exported array and modify them to look like the following:

function (session, results) {
  // BEGIN MODIFICATION TO LAST STEP 1/2
  chatbase.newMessage()
    // send the value received for the 'name' question
    .setMessage(results.response.toString())
    .send()
    .catch(e => console.error(e));
  // END MODIFICATION TO LAST STEP 1/2
  // Ask the second question: "What is your quest?"
  builder.Prompts.text(session, constants.questions.quest.q);
},
function (session, results) {
  // BEGIN MODIFICATION TO LAST STEP 2/2
  chatbase.newMessage()
    // send the value received for the 'quest' question
    .setMessage(results.response.toString())
    .send()
    .catch(e => console.error(e));
  // END MODIFICATION TO LAST STEP 2/2
  // Randomly select our last question, several choices exist in our constants
  // file, please refer to the wildcardTags property to determine which
  // questions may be randomly selected.
  const qTag = constants
    .wildcardTags[random(0, constants.wildcardTags.length-1)];
  session.userData.lastQuestionTag = qTag;
  builder.Prompts.text(session, constants.questions[qTag].q);
}

Your dialogs/gorge-of-eternal-peril.js file should look like the file steps/step-3/dialogs/gorge-of-eternal-peril.js. In this case our bot is simply collecting information about the user so we don't have any not handled cases to report but in some cases simply reporting the information collected from users can be immensely useful. The curl commands for these calls are the same as the previously listed handled curl command.

Crossing the gorge

Step: steps/step-4

Our final dialog function is responsible for ‘handling' our user cross the gorge or ‘not handling' our user being thrown into the gorge of peril. In the dialogs/gorge-of-eternal-peril.js locate the final (fifth) function in the exported array and modify it to look like the following:

function (session, results) {
  const question = constants.questions[session.userData.lastQuestionTag];
  const answer = results.response.toString().trim();
  // Unlike our other two questions this last question always has a wrong
  // answer or set of correct answers. Check the validity of the users input.
  const correct = question.check(answer);
  // BEGIN MODIFICATION TO LAST STEP 1/3
  const msg = chatbase.newMessage()
    // Set up our message with the question asked and whether or not the
    // answer was correct along with the users response but don't send it yet!
    .setMessage(JSON.stringify({
      question: question.q,
      answer: answer,
      correct: correct
    }));
  // END MODIFICATION TO LAST STEP 1/3
  if (!correct) {
    // BEGIN MODIFICATION TO LAST STEP 2/3
    // Looks like the response was incorrect; count it as not handled by setting
    // the message as not handled
    msg.setAsNotHandled();
    // END MODIFICATION TO LAST STEP 2/3
    // Incorrect! Into the gorge you go
    session.send(constants.intoTheGorge);
  } else {
    // Right! On your way then
    session.send(constants.onYourWay);
  }
  // BEGIN MODIFICATION TO LAST STEP 3/3
  // Now, send the message
  msg.send().catch(e => console.error(e));
  // END MODIFICATION TO LAST STEP 3/3
  // End the conversation to open a new session on the console
  session.endConversation();
}

Your dialogs/gorge-of-eternal-peril.js file should look like the file steps/step-4/dialogs/gorge-of-eternal-peril.js. Reflecting on the above snippet we did a couple things:

Voila! For each step of our Chatbots dialog we now track handled/not handled user input and the content of that input which, when using the Chatbase analytics website, should help us quickly identify what input is being successfully handled by our bot and which input is not. It's encouraged that you run through the completed bot a couple times, Chatbase currently only displays individual messages from users if they are encountered several times so try running the codelab a couple time while giving the same responses to our new bot.

Getting acquainted with the Chatbase UI

Chatbase offers a powerful browser-based web-application which you can use to monitor and understand analytics data coming out of your chatbot. Let's go take a look at it now, point your browser to:

https://chatbase.com/agents

Let's click on our newly created agent in the list to see an overview of all our bots analytics:

Here we can get a big-picture look at how our bot is interacting and growing with users:

Now lets check out our messages: On the overhead menu bar look for the messages tab and -- go ahead and click on it:

Voila!! All of our glorious attempts at crossing the Gorge of Peril are meticulously logged for us to consult where we went wrong:

The Messages interface defaults to first showing which messages were reported as Not Handled which, in our case, was if we declined to answer our bots three questions or we got a question incorrect when talking to our bot. Since this bot got several no responses to its first question we can see that ‘no' has been logged as an Not Handled message -- if this were a real bot we could take action on this phrase right away!

Let's go ahead and look at some of our Handled messages:

Here we can see our Handled response ‘yes' to the bots first question and an ‘Exit' percentage listed to the right which indicates how many users didn't follow up their initial response with another message within 15 minutes.

If you would like to learn more about using the Chatbase web-application please check out these docs.