Shall I notify you about updates?

Even though most chatbot projects are customer-facing, it is estimated that up to 26% of chatbots are operations focused. Google's Dialogflow is a popular platform as it's easy to setup a conversational interface with – and free to use.

In this guide, we'll create a Dialogflow chatbot for a work-related, internal use case. Specifically, we will focus on the data-binding aspect to see just how compatible Dialogflow is with integrating enterprise data.

1. Create an agent

Dialogflow is free to get started with, all you need is to register for an account. Then click on Create Agent and assign a name, language and Google Project to the agent.

In this example we simply named our agent Workbot.

2. Create an intent

In our new Workbot agent we first will create an intent. An intent is basically what project managers refer to as a 'user story'.

So for example, 'looking up your leave allowance' or 'book a training session' would be an intent the chatbot should assist the employee with. As an example for this guide, we will be making a chatbot that looks up your website's visitors.

We chose this an example as it consumes another Google API which is free to do with Dialogflow's Firebase Cloud Functions.

Related: We have a whole guide about how to find chatbot use cases and evaluate you should do first.

3. AI training data

The Natural Language Understanding in Dialogflow can differentiate between different intents that a user may have, we need to start entering some training phrases.

So you basically ask yourself all the different ways you can think of to ask for your intent and enter those to start, for example:

  • How many visitors did we have in the last 30 days?
  • Website visitors past 30 days
  • How many people visited our website in the past month?
  • Show me how many people visited our page in the last 14 days
  • ...

Note, that we try and create a wide spread of phrases. We change between questions and commands, use active and passive voice where possible and try and include synonyms for our key trigger words (i.e. visitors, users or people should have the same meaning to the bot).

Putting some thought into this early training data should help the AI get it right more often. However it won't completely get you around having to occasionally retrain the AI using Analytics data.

You may notice the little yellow highlights around the time values, i.e. past 30 days. These represent parameters that the chatbot can capture dynamically from the user. There can be multiple such parameters which each get their own color, e.g. if you enter country names it could be orange.

For the most part this detection should happen by itself as you feed the bot more training sentences. But if you go to the Action and parameters section you can manage what parameters you will have.

We will actually just stick with a time period in this example, as you will later see that every additional parameter creates a lot of extra code.

4. General configuration

Next up are two small matters to deal with: Responses and Fulfillment.

As a response we only really need a fallback answer the bot will resort to, in case it couldn't find an answer for the question.

Why only a fallback? Unlike the training questions, that have those handy highlighted bits to show parameters, there isn't an elegant way to just write text answers and mark spots as placeholders for the number.

Instead we will have to build our answer as part of the Fulfillment script we're going to write below.

That means all we need to write is "I'm sorry, something went wrong" and continue with the Fulfillment section below.

Fulfillment is basically what we tell the bot to do or execute, once Dialogflow determined the correct intent.

For our example we just need to enable the toggle for Enable webhook call for this intent.

5. Start with fulfillment

Fulfillment is where our bot learns to actually give the user a direct answer; as opposed to relying on canned responses or randomly sputtering out links to go somewhere else.

Image from u/ThePeoplesBard

Without fulfillment the bot will have a very hard time to get long-term user adoption as it simply isn't that useful.

To start, we click on the Fulfillment menu on the left, and then we enable the Inline Editor.

Now comes the arguably hardest part and that is writing our fulfillment function.

6. Write fulfillment function

Our function is going to be a webhook function, meaning it is a function that gets triggered by a HTTP request – and Dialogflow will be making this request if it matched the user's query to an existing intent.

Webhooks can be stored on many services like AWS Lambda, Heroku or Azure. But Google conveniently offers it's own service with Cloud Functions for Firebase which we are going to use for our example.

Basically our webhook needs to do four things:

  1. Parse the agent parameters
  2. Connect to our data source (in this case Google Analytics)
  3. Request the data
  4. Return it to Dialogflow in a readable format (i.e. a Card or some text)

To begin with, we'll focus on the package.json file which contains a list of dependencies (necessary resources) for our function. Let's add the googleapis package which shall allow us to easily communicate with the Google Analytics API.

At the time of this writing the latest version was 27, so we're adding to line 20 this:

"googleapis": "^27.0.0"

(Don't forget to add a comma on line 20 at the end.)

Now we go over to index.js where our main function belongs.

We've got the finished code for this further down, so skip ahead if you want but just in case I'll walk you through each section.

'use strict';

const functions = require('firebase-functions');
const {google} = require('googleapis');  // Calling the googleapis dependency we added earlier
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');

These are our project dependencies, where we simply add a new line for googleapis so we can call it in the function.

// Set up view ID from Google Analytics
const viewid = 'Your_view_Id';

// Google Analytics API service account details
const serviceAccount = {};

The view ID is the view in Google Analytics that you want to pull information from. You can find this in the Google Analytics admin panel. ServiceAccount is an account Google APIs uses to authenticate with the Google Analytics API service. You'll need to register for a service account.

There's a good guide here, but here's a quick overview:

  1. Go to the Google Developer Console APIs page and create a new project
  2. From the dashboard, click Enable APIs and services
  3. Search for the Google Analytics API and open it. Click Enable to enable it
  4. When prompted, click Create Credentials and then Help me choose
  5. Fill out the form as follows:
  6. Fill in the form to create a service account, with a JSON key type
  7. The form will download a JSON file containing the details of your service account to your PC.
  8. Once you've got this JSON file, you can copy and paste the contents of the file in place of the empty curly brackets.

    You'll also need to add your service account to your Google Analytics account as a user. You can do this in the Google Analytics admin panel under User Management. When prompted, add the email address listed under client_email.

const serviceAccountAuth = new google.auth.JWT({
  email: serviceAccount.client_email,
  key: serviceAccount.private_key,
  scopes: 'https://www.googleapis.com/auth/analytics.readonly'
});

We now authenticate with the Google API service for Google Analytics. The 'scopes' variable refers to what level of access your function requires. You should always choose the lowest level that you need; you can read more about scopes on the Google Identity Platform documentation.

 const startDate = agent.parameters['date-period'].startDate.split('T')[0];
 const startDateString = getLocaleDateString(startDate);
 const endDate = agent.parameters['date-period'].endDate.split('T')[0];
 const endDateString = getLocaleDateString(endDate);

The first thing our agent needs to do is parse the user-provided parameters into a usable format. Google Analytics requires a specific date format for its API, but we also want to create a user-readable date string that our agent can respond to the user with. 

Parameters provided by the agent are accessible under agent.parameters; you can see all available parameters from a response in the test window:

getLocaleDateString(date) is just a helper function which parses the dates.

 function getVisitorCount(agent) {
      return getPageviews(startDate, endDate).then((visitorString) => {
          agent.add(`In the period ${startDateString} to ${endDateString}, there were ${visitorString} visitors to yourwebsite.com.`);
      }).catch(() => {
          agent.add(`Sorry, we couldn't retrieve data for the period ${startDateString} to ${endDateString}. Is there anything else I can do for you?`);
      });
  }

This is our actual intent handler. If the agent matches a request to an intent, this is the function that gets called. The function calls our API caller function getPageviews(), then depending on whether the function was successful or failed, sends a response back to the agent.

So if something was found we've got the sentence In the period between [startDate] to [endDate], there were [number] visitors to yourwebsite.com.

And if we can't get a valid result we will return to Dialogflow simply Sorry, we couldn't retrieve data for the period [startDate] to [endDate]. Is there anything else I can do for you?

Notice how the negative response is different to our fallback response from point 4. The fallback response only gets used if our function doesn't respond for whatever reason.

  let intentMap = new Map();
  intentMap.set('Page visitor count', getVisitorCount);
  agent.handleRequest(intentMap);

The intent map maps each intent defined on the intent tab to a function. When our 'Page visitor count' intent is invoked, it will call our function getVisitorCount(). You can map more intents to functions here to add more functionality to your chatbot.

Dialogflow uses just one webhook to handle all intents for the same agent. So depending on how many different use cases this chatbot should handle, there might be quite a few more functions that you will want to squeeze in here. 😉

7. Interacting with the API

Following on from this we now start to actually use the Google Analytics API.

// Analytics API caller
function getPageviews(startDate, endDate) {
    return new Promise((resolve, reject) => {
       // Make request to Google Analytics - start and end date provided by user
        google.analytics('v3').data.ga.get({
            'auth': serviceAccountAuth,
            'ids': 'ga:' + viewid,
            'start-date': startDate,
            'end-date': endDate,
            'metrics': 'ga:pageviews'
        }, (err, resp) => {
          if (err) {
            // Something went wrong
            console.log(err);
            reject (err);
          } else {
            // Data received from GA
            var count = resp.data.rows[0][0];
            resolve(count);
          }
      });
    }); 
}

We use promises to ensure that our function gets the data it needs before it proceeds. We then make the request to the Google Analytics API using the analytics('v3').data.ga.get method. The parameters it takes are well documented here, but you can query almost any aspect of your Analytics data with the API.

Here is our function again as one:

'use strict';

const functions = require('firebase-functions');
const {google} = require('googleapis');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');

// Set up view ID from Google Analytics
const viewid = '12345678';

// Google Analytics API service account details
const serviceAccount = {
  "type": "service_account",
  "project_id": "...",
  "private_key_id": "...",
  "private_key": "...",
  "client_email": "..."
...
};

// Authenticate with Google API services
const serviceAccountAuth = new google.auth.JWT({
  email: serviceAccount.client_email,
  key: serviceAccount.private_key,
  scopes: 'https://www.googleapis.com/auth/analytics.readonly'
});

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  const agent = new WebhookClient({ request, response });

  // Parse the user provided date parameters into Date object and human readable formats
  const startDate = agent.parameters['date-period'].startDate.split('T')[0];
  const startDateString = getLocaleDateString(startDate);
  const endDate = agent.parameters['date-period'].endDate.split('T')[0];
  const endDateString = getLocaleDateString(endDate);

  // Intent handler - creates user responses for success or failure
  function getVisitorCount(agent) {
    return getPageviews(startDate, endDate).then((visitorString) => {
        agent.add(`In the period ${startDateString} to ${endDateString}, there were ${visitorString} visitors to adenin.com.`);
    }).catch(() => {
          agent.add(`Sorry, we couldn't retrieve data for the period ${startDateString} to ${endDateString}. Is there anything else I can do for you?`);
    });
  }

  let intentMap = new Map();
  intentMap.set('Page visitor count', getVisitorCount);
  agent.handleRequest(intentMap);
});

// Analytics API caller
function getPageviews(startDate, endDate) {
    return new Promise((resolve, reject) => {
          // Make request to Google Analytics - start and end date provided by user
        google.analytics('v3').data.ga.get({
            'auth': serviceAccountAuth,
            'ids': 'ga:' + viewid,
            'start-date': startDate,
            'end-date': endDate,
            'metrics': 'ga:pageviews'
        }, (err, resp) => {
          if (err) {
            // Something went wrong
            console.log(err);
            reject (err);
          } else {
            // Data received from GA
            var count = resp.data.rows[0][0];
            resolve(count);
          }
      });
    }); 
}

// Helper function to convert date into a user-readable string
function getLocaleDateString(date) {
  let dateObj = new Date(date);
  return dateObj.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', timeZone: 'America/Los_Angeles' });
}

Once the API has responded, we pull out the information we need and pass this back to our intent fulfilment function. The response format is detailed here, but you might find it easier to just print the response to the console to figure out what you need.

8. Debugging

Okay that was a lot already, but like any good developer you'll want to test this code extensively now.

There is debugging available through the Firebase console. There's a link to this underneath the function editor.

You can use console.log() to print to the Firebase console, and other debugging and error messages are viewable here. General messages about the webhook status can also be seen under Diagnostic Info when you test your chatbot in the Dialogflow console.

9. Deploy the function

We can't yet deploy our chatbot to our users, but we can deploy our function now. Do that just click Deploy.

Dialogflow will notify you once the deployment is complete, and you'll also see a message in the Firebase console. If there are any errors with the deployment, these will be logged in Firebase too.

You can now use the Dialogflow console on the right to test your chatbot. Start with one of your training phrases to see if the chatbot is communicating with the API properly.

If so, you can start to test more user phrases to find blind spots in your training phrase list. Your chatbot will work by default with Google Assistant, but of course you can deploy it to many more services.

10. Deploy the chatbot

We've worked hard to get this chatbot up and running, and thankfully the one thing Dialogflow make easier than anything else is deploying your chatbot to other channels.

Let's say you want to deploy your chatbot to Slack (as it is an employee-facing example), then just open the Integrations page, enable the toggle for Slack and authorize Dialogflow with your Slack workspace.

In Slack simply add the Dialogflow Bot to your Apps and start asking away.

Bottom line

As you can see, creating even a rather simple chatbot with Dialogflow requires a deep understanding of Webhooks and APIs.

Easy to train the AI
Easy to mark parameters
Deployment to multiple chat channels is super quick

Data-binding only possible with coding
Only one function needs to manage all intents which can can get messy
Google APIs only free Firebase connection
Text responses merely a fallback as response must be constructed in the function

While the actual interface to create intents or deploy the chatbot are all as simple as could be, on balance you need to budget for the overhead of having to write hardcore code and debug every single API before you get much mileage out of your chatbot.

We have a range of chatbots we've created workplace-related use cases with, why don't you look at some other guides.

Article by: Henry Amm

I’m the Senior Director for the Digital Assistant Platform. Prevously gained 6 years of experience as an Intranet consultant. Fluent in German.

Join the discussion

This field is required.
This field is required.
This field is required.

0 responses

Be the first to leave a comment!

Try Digital Assistant with your team for free

Get started