Dialogs and Fulfillment

After the Google Assistant invokes your Conversation Action, you handle the user experience from beginning to end, by defining the user interface with dialogs and satisfying the user's request with fulfillment logic.

Overview

As described in Conversation API, your fulfillment endpoint receives requests when users provide input. It then processes the request and responds. This back-and-forth request and response process drives the conversation forward until you eventually fulfill the initial user request.

The following steps describe how to build dialogs and fulfillment for a Conversation Action:

  1. Initialize the ActionsSdkAssistant object.
  2. Create functions to handle requests in your fulfillment logic.
  3. Build and initialize an action map that maps intents to functions. When your endpoint receives a request, the client library checks the intent associated with the request and automatically calls the appropriate function.

Building dialogs

After you action is invoked, you can handle user input requests by constructing your own responses as described in the Conversation API Reference, but we highly recommend using the Node.js client library.

Initialize the ActionsSdkAssistant object

The following code instantiates ActionsSdkAssistant and does some boilerplate Node.js setup for Google Cloud Functions:

'use strict';

const ActionsSdkAssistant = require('actions-on-google').ActionsSdkAssistant;

exports.sayNumber = (req, res) => {
  const assistant = new ActionsSdkAssistant({request: req, response: res});

  // Create functions to handle requests here

}

Create functions to handle requests

When users speak a phrase, you receive a request from the Google Assistant. To handle the requests, create functions to handle one of the triggered intents, which can be one of the following types:

  • StandardIntents.MAIN - This intent is triggered when users invoke your action by name, such as "talk to Personal Chef".
  • StandardIntents.TEXT - This intent is triggered when users speak input and you receive the input as a request to your fulfillment endpoint.
  • StandardIntents.PERMISSION - This intent is triggered after a call to askForPermission().

To handle requests:

  1. Carry out any logic required to process the user input. You'll typically call the following get functions to get user input from the request.

  2. Build an input prompt with buildInputPrompt() to create the text to speak back to users.

  3. Call the ask() function with the input prompt.

The following code shows how to build a simple response:

function mainIntent (assistant) {
  let inputPrompt = assistant.buildInputPrompt(false,
    'Hi! Say something, and I\'ll repeat it');
  assistant.ask(inputPrompt);
}

function respond (assistant) {
  let inputPrompt = assistant.buildInputPrompt(false,
    'Hi! Say something, and I\'ll repeat it.');
  assistant.ask(inputPrompt);
}

Build and initialize an action map

Once you have all your functions to handle triggered intents, build an action map to map intents to functions.

let actionMap = new Map();
actionMap.set(assistant.StandardIntents.MAIN, welcomeIntent);
actionMap.set(assistant.StandardIntents.TEXT, numberIntent);

//add more intents to the map
...

assistant.handleRequest(actionMap);

If you have only one handler function, then you don't need to do this and just pass in the function name to handleRequest. In this case, all triggered intents will call this function, so you have to check for the intent that is triggered to do the appropriate thing.

function responseHandler (assistant) {
  // intent contains the name of the intent you defined in `initialTriggers`
  let intent = assistant.getIntent();
  switch (intent) {
    case assistant.StandardIntents.MAIN:
      assistant.ask('Welcome! Say a number.');
      break;

    case assistant.StandardIntents.TEXT:
      let number = assistant.getArgument(NUMBER_ARGUMENT);
      assistant.tell('You said ' + number);
      break;
  }
}
// you can add the function name instead of an action map
assistant.handleRequest(responseHandler);

Ending conversations

When you no longer want any user input in return and want to end the conversation, call the tell() function. This function tells the Google Assistant to speak back the text to the user and end the conversation by closing the microphone. You need to call this function at least once in your Conversation Action.

Handling the main invocation intent

When users trigger the assistant.intent.action.MAIN intent, you normally don't need to do any user input processing. If your action package contains many actions and covers many use cases, it's a good idea to orient the user by telling them a few things that they can do.

  1. Build an input prompt with 'buildInputPrompt()' that orients the user and describes a few things your actions can do.
  2. Call the ask() function with the input prompt. The Google Assistant speaks the input prompt to the user as a response and then waits for the user to trigger one of the intents that you specified.

The following snippet shows how to handle a simple welcome intent:

// handle the initialTrigger
function mainIntent (assistant) {
  let inputPrompt = assistant.buildInputPrompt(false, 'Hi, I\'m Do It All.
    You can ask to do this or that. What would you like to do?');
  assistant.ask(inputPrompt);
}

Dialog building features

The following sections describe ways you can augment dialogs to make them more dynamic and user-friendly.

No-input prompts

When calling ask(), you can provide up to three no-input prompts that are spoken to the user when the Google Assistant cannot detect any input.

The following snippet shows how to define these prompts:

let inputPrompt = assistant.buildInputPrompt(false, 'Can I have a number?',
  ['Say any number', 'Pick a number', 'What is the number?']);

SSML

SSML lets you make your dialogs sound more life-like. The following code snippet shows you how to use the client library in your fulfillment code to create a response that uses SSML:

function saySSML(assistant) {
  let text_to_speech = '<speak>Here are <say-as interpet-as="characters">SSML</say-as> samples. '
    + 'I can pause <break time="3"/>. '
    + 'I can play a sound <audio src="https://www.example.com/MY_WAVE_FILE.wav">your wave file</audio>. '
    + 'I can speak in cardinals. Your position is <say-as interpret-as="cardinal">10</say-as> in line. '
    + 'Or I can speak in ordinals. You are <say-as interpret-as="ordinal">10</say-as> in line. '
    + 'Or I can even speak in digits. Your position in line is <say-as interpret-as="digits">10</say-as>. '
    + 'I can also substitute phrases, like the <sub alias="World Wide Web Consortium">W3C</sub>. '
    + 'Finally, I can speak a paragraph with two sentences. '
    + '<p><s>This is sentence one.</s><s>This is sentence two.</s></p></speak>'
  assistant.tell(text_to_speech);
};

See the SSML reference documentation for more information.

Conversation state

You can maintain state of your conversations with a JSON formatted object that is passed back and forth between you and the Google Assistant.

You can write to and read from the assistant.data field directly. This field is passed back and forth automatically when handleRequest() is called and your endpoint is ready to receive requests.

assistant.data = { something:10 }
... 
let value = assistant.data.something

Or, you can use the ask() function to pass the dialog state to the Google Assistant as a JSON object and then read it with getDialogState() when you receive a response.

// get state object and modify it
let state = assistant.getDialogState();
...
// return it in the response
assistant.ask(inputPrompt, state);

Deploying fulfillment

You can deploy your fulfillment logic to any webhost platform that supports Node.js. If you don't already have a preferred platform, see fulfillment hosting for more information on how to deploy your app on a variety of hosting options.