Building Virtual Agent Fulfillment - GSP792

Building Virtual Agent Fulfillment - GSP792

ยท

11 min read

Overview

In this lab, you will continue working on your Pigeon Travel virtual agent created in the Design Conversational Flows for your Agent and add context as well as set up fulfillment to look up and store reservations entries in Firestore.

Note: You will need the exported agent zip file from the Design Conversational Flows for your Agent lab to continue with this lab. Otherwise, you will have to build all the intents and entities in Design Conversational Flows for your Agent lab from scratch before proceeding with the steps in this lab.

What you'll learn

In this lab you will perform the following tasks:

  • Create a Firestore Collection

  • Setup fulfillment as Cloud Functions code for the agent to be able to lookup and change the name on the reservation.


Task 1. Enable the API

  1. In the Cloud Console go to Navigation menu () > APIs & Services > Enable APIs and Services.

  2. Click + Enable APIs and Services.

  3. Search for Dialogflow.

  4. Click the Dialogflow API tile. If the API is not enabled, click Enable.

  5. Search for Cloud function.

  6. Click on the Cloud function API and if it is already Enabled, click Manage.

  7. Click Disable API.

    If you are asked to confirm, click Disable.

  8. Click Enable.

    When the API has been enabled again, the page will show the option to disable.

Set up IAM permissions

  1. From the Navigation menu (), go to IAM & admin > IAM.

  2. Edit the permissions for your Google Cloud Functions Service Agent by locating the service agent under the IAM list and selecting the pencil icon. The service account will have the Domain @gcf-admin-robot.iam.gserviceaccount.com.

  3. If you are unable to see any service account click the checkbox Include Google-provided role grants.

  4. Click add another role in the dialog and then select Artifact Registry > Artifact Registry Reader role.

  5. Click Save.

Task 2. Create your Dialogflow agent

You'll call your agent "pigeon-travel".

  1. Go to the Dialogflow Console.

  2. Click Sign in with the Google button, and make sure to select the lab credentials you logged into this lab with. Then click Allow.

  3. Uncheck the email preferences and check the Terms of Service. Click on Accept.

  4. In the left menu, click Create agent.

  5. Now add the agent information as you see in the screenshot below:

  • Agent Name: pigeon-travel

  • Default Time Zone: America/Denver

  • Google Project: use your lab Project ID

  1. Click Create.

Click Check my progress to verify the objective.

Assessment Completed! Dialogflow agent names: ["pigeon-travel"]

Create the Dialogflow agent

Check my progress

Assessment Completed! Dialogflow agent names: ["pigeon-travel"]

Task 3. Import your Dialogflow agent

In the previous lab, you exported the Dialogflow agent you built. You will now import it back in and continue building it.

This will create a new virtual agent project. Now you'll want to import the work you've already done.

  • If you do not have exported files to use, use this file:
https://storage.cloud.google.com/qwiklabs-resources-ccai-quest/pigeon-travel-gsp-792.zip

Copied!content_copy

  • Download the file to your local workstation.
  1. Click on the settings gear icon next to your agent name.

  2. Select the Export and import tab.

Export and Import tabbed page

  1. Click Import from zip.

  2. Click Select file and navigate to the zip file which contains the configuration of your virtual agent. You can alternatively drag and drop the file if you prefer.

  3. Type in the word "IMPORT" in all caps to enable the import button and click Import.

Upload agent page

  1. Click Done to close out the upload window once the import is complete.

Your existing configuration has been imported into your new agent project.

Click Check my progress to verify the objective.

Assessment Completed!

Import your Dialogflow agent

Check my progress

Assessment Completed!

Task 4. Set up fulfillment using Cloud Functions to look up reservations in Firestore

So far the agent does a good job communicating with a customer to get their information including a reservation number. However, the information collected is not checked or recorded anywhere to enable further possible action. In this section, you will set up fulfillment by adding Node.js code and deploy it as a Cloud Function for your agent to look up the current reservation and add the change.

Configure Firestore

  1. In the Console go to Navigation menu > Databases > Firestore.

  2. Click Create Database.

  3. For Select your Firestore mode, Choose Native mode (recommended) and click Continue.

  4. For the location select Multi-region and then choose nam5 (United States) for the multi-region.

  5. Click Create database. Once it completes you will have the ability to create a new collection.

  6. Click Start collection.

  7. Fill in the details to replicate the details below, then click Save.

  • Collection ID: reservations

  • Document ID: 100

  • Field name: fname

  • Field type: string

  • Field value: Isabel

  1. Then click the Add a Field (+) button to add another:
  • Field name: lname

  • Field type: string

  • Field value: Costa

  1. Then click the Add a Field (+) button to add another:
  • Field name: newname

  • Field type: string

  • Field value:

You have now added your first document to a Firestore collection.

Firestore Document IDs Best Practice

  • Avoid the document IDs . and ...

  • Avoid using / forward slashes in document IDs.

  • Do not use monotonically increasing document IDs such as:

    • Customer1, Customer2, Customer3, ...

    • Product 1, Product 2, Product 3, ...

Such sequential IDs can lead to hotspots that impact latency.

Dialogflow Fulfillment

  1. Navigate to the Diaglflow console and click on Fulfillment in the left menu. It may take a few minutes for the resources to be provisioned.

  2. Next to the Inline Editor option, slide the slider to the right so it appears Enabled. This enables the Cloud Functions editor within your Dialogflow agent.

Note: If you receive an error message, try refreshing the page and then try enabling the slider again.

  1. Once enabled, you will notice a default template in index.js.

  2. Click on the Deploy button on the bottom right. This may take a few minutes.

  3. Once deployment is successful, go into your Google Cloud console, and using the menu on the left, navigate to Cloud Functions to confirm if the function has been deployed.

Click Check my progress to verify the objective.

Assessment Completed!

Set up fulfillment using Cloud Function

Check my progress

Assessment Completed!

  1. Back in the Fulfillment section of your Dialogflow console, click on the index.js tab.

  2. Again notice that there is already some starter code, including functions to handle the default welcome and fallback intents. You will first add the following lines below to be able to work with Firestore.

Add the following code above the line that says process.env.DEBUG = 'dialogflow:debug';:

const admin = require('firebase-admin');

Copied!content_copy

Add this code block below the line that says process.env.DEBUG = 'dialogflow:debug';:

admin.initializeApp();
admin.firestore().settings( { timestampsInSnapshots: true });
const db = admin.firestore();

Copied!content_copy

  1. Now add the following code for handling or reservations after the handler functions for Welcome and Fallback intents.

Add the following code block below the line that says

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {:

function reservation(agent) {
    let id = agent.parameters.reservationnumber.toString();
    let collectionRef = db.collection('reservations');
    let userDoc = collectionRef.doc(id);
    return userDoc.get()
        .then(doc => {
            if (!doc.exists) {
                agent.add('I could not find your reservation.');
            } else {
                db.collection('reservations').doc(id).update({
                    newname: agent.parameters.newname
                }).catch(error => {
                    console.log('Transaction failure:', error);
                    return Promise.reject();
                });
                agent.add('Ok. I have updated the name on the reservation.');
            }
            return Promise.resolve();
        }).catch(() => {
            agent.add('Error reading entry from the Firestore database.');
        });
}

Copied!content_copy

  1. Modify the intentMap to include an entry to handle the name.reservation-getname mapping to the function you just added:
intentMap.set('name.reservation-getname', reservation);

Copied!content_copy

so that it looks something like this:

  let intentMap = new Map();
  intentMap.set('name.reservation-getname', reservation);
  intentMap.set('Default Welcome Intent', welcome);
  intentMap.set('Default Fallback Intent', fallback);
  agent.handleRequest(intentMap);
  1. In package.json tab make sure that firebase-admin is set to "^5.13.1":
"firebase-admin": "^5.13.1"

Copied!content_copy

  1. Click the Deploy button to save and deploy the code.

  2. Click on the Intents in the left menu, and go into the name.reservation-getname inside the name.reservation intent and navigate all the way down to Fulfillment, under click the toggle switch for Enable the webhook call for this intent to enable it. Save the intent.

  3. Try it out in the simulator by typing out the question: change name on booking.

You will get the default response like: Sure I can help you to change your name on the reservation. Can I have your first name?.

  1. Try by entering any username you want.

  2. Then Try entering 100 when you get the default response asking for the reservation number.

  3. After that enter the new name for which you want to make a reservation, for example: Kelly.

  4. After the successful completion, you will receive the default response like: Thank you dylan. I have changed the name on reservation number 100 to be kelly.

  5. You can confirm this further by going into the Cloud Console and using the left menu, navigate to Firestore > Data.

You will see the entry for your name change. Notice a new key/value pair was added, this way you can see what the original name was plus know what the new name is.

new key/value pair. fname: "Isabel", lname: "costa", newname name: "kelly"

  1. Examine the logs for code errors in your Dialogflow console. On the bottom left of the Fulfillment section, click View execution logs in the Google Cloud console to view the logs.

  2. Examine the logs to see if there are any errors. Click Navigation menu > Operations > Logging.

  3. In the Logs Explorer, select Cloud Function > dialogflowFirebaseFulfillment. You can verify all related logs here.

Alternatively, you can go to Navigation menu > Cloud Functions. Click LOGS inside your created function to view the logs.

Click Check my progress to verify the objective.

Test the agent using Dialogflow simulator

Check my progress

Task 5. (Optional) Export your code

Export your work so you can use it in the next lab. Click on the Source tab under Cloud Functions, scroll down, and you'll see a button to Download zip.

Export your agent

Export your agent as a zip file so that you can import it later when you start the next lab. This way you can reuse the intents and entities you've configured so far.

  1. Click on the settings gear โš™ icon next to your agent name in the left menu.

  2. On the settings page that opens up, go to the Export and Import tab.

  3. Click on Export as zip. This will download your agent into a local zip file.


Solution of Lab

Index.js

// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';

const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const admin = require('firebase-admin');
admin.initializeApp();
admin.firestore().settings( { timestampsInSnapshots: true });
const db = admin.firestore();
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  function reservation(agent) {
    let id = agent.parameters.reservationnumber.toString();
    let collectionRef = db.collection('reservations');
    let userDoc = collectionRef.doc(id);
    return userDoc.get()
        .then(doc => {
            if (!doc.exists) {
                agent.add('I could not find your reservation.');
            } else {
                db.collection('reservations').doc(id).update({
                    newname: agent.parameters.newname
                }).catch(error => {
                    console.log('Transaction failure:', error);
                    return Promise.reject();
                });
                agent.add('Ok. I have updated the name on the reservation.');
            }
            return Promise.resolve();
        }).catch(() => {
            agent.add('Error reading entry from the Firestore database.');
        });
}
  const agent = new WebhookClient({ request, response });
  console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
  console.log('Dialogflow Request body: ' + JSON.stringify(request.body));

  function welcome(agent) {
    agent.add(`Welcome to my agent!`);
  }

  function fallback(agent) {
    agent.add(`I didn't understand`);
    agent.add(`I'm sorry, can you try again?`);
  }

  // // Uncomment and edit to make your own intent handler
  // // uncomment `intentMap.set('your intent name here', yourFunctionHandler);`
  // // below to get this function to be run when a Dialogflow intent is matched
  // function yourFunctionHandler(agent) {
  //   agent.add(`This message is from Dialogflow's Cloud Functions for Firebase editor!`);
  //   agent.add(new Card({
  //       title: `Title: this is a card title`,
  //       imageUrl: 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png',
  //       text: `This is the body text of a card.  You can even use line\n  breaks and emoji! ๐Ÿ’`,
  //       buttonText: 'This is a button',
  //       buttonUrl: 'https://assistant.google.com/'
  //     })
  //   );
  //   agent.add(new Suggestion(`Quick Reply`));
  //   agent.add(new Suggestion(`Suggestion`));
  //   agent.setContext({ name: 'weather', lifespan: 2, parameters: { city: 'Rome' }});
  // }

  // // Uncomment and edit to make your own Google Assistant intent handler
  // // uncomment `intentMap.set('your intent name here', googleAssistantHandler);`
  // // below to get this function to be run when a Dialogflow intent is matched
  // function googleAssistantHandler(agent) {
  //   let conv = agent.conv(); // Get Actions on Google library conv instance
  //   conv.ask('Hello from the Actions on Google client library!') // Use Actions on Google library
  //   agent.add(conv); // Add Actions on Google library responses to your agent's response
  // }
  // // See https://github.com/dialogflow/fulfillment-actions-library-nodejs
  // // for a complete Dialogflow fulfillment library Actions on Google client library v2 integration sample

  // Run the proper function handler based on the matched Dialogflow intent name
  let intentMap = new Map();
  intentMap.set('name.reservation-getname', reservation);
  intentMap.set('Default Welcome Intent', welcome);
  intentMap.set('Default Fallback Intent', fallback);
  // intentMap.set('your intent name here', yourFunctionHandler);
  // intentMap.set('your intent name here', googleAssistantHandler);
  agent.handleRequest(intentMap);
});

package.json

{
  "name": "dialogflowFirebaseFulfillment",
  "description": "please like share & subscribe to quicklab",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "10"
  },
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "dependencies": {
    "actions-on-google": "^2.2.0",
    "firebase-admin": "^5.13.1",
    "firebase-functions": "^2.0.2",
    "dialogflow": "^0.6.0",
    "dialogflow-fulfillment": "^0.5.0",
    "firebase-admin": "^5.13.1"
  }
}
ย