Dialogflow and fulfillment — how to prototype chatbots using Mockservice.io?

Has your bot ever needed to do additional activities, like saving something to a database? Or maybe have you ever wanted to build a dynamic response, based on user responses? Those are moments when you should use Fulfillment functionality in Dialogflow. Today I will show you how to connect your Dialogflow bot with an external API and how to simulate this API with Mockservice.io to focus on business logic and bot implementation, not on building, implementing and hosting a separated service.

Use case

Let’s assume that you want to create a chatbot that helps in learning Spanish vocabulary. The chatbot will allow you to add words and their translations, and based on them will generate tests, quizzes, and flashcards that will help you learn them. So, the chatbot, in addition to conducting conversations, requires interaction with external services (saving words to a database, generating tests and quizzes). 

In today’s article, I will implement one of the key functionality — adding words. Requirements:

  • Bot allows us to add words and their translations
  • If potential translations are found, the bot will suggest them to the user

The solution will require enabling the Fulfillment option in Dialogflow and implementing an external service. However, building, hosting and deploying a backend service will take some time, so I decided to use Mockservice.io — a tool to simulating and mocking REST API. I have written about it HERE. It will allow me to reduce the time I would spend on creating an external service, so I can use the saved time on focusing on chatbot’s business logic. This sort of tool is very helpful in the early stage of creating bots (prototyping stage).

Fulfillment

But what is Fulfillment and how it works? Fulfillment functionality helps you connect your agent with an external service. It’s an intent-context option, so each intent has a setting to enable fulfillment. So in practice, we decide which intent requires some action by your system. 

When an intent with fulfillment option is matched, your agent sends a request to your webhook service with intent metadata. In what cases that functionality could be helpful? For example, if a user asks for today’s weather in New York, you could send a request to some external weather API, get information and send back a response message.

How does Fulfillment work?

  • A user sends a message
  • Dialogflow’s agent analyzes the message and matches it to one of the defined intents
  • Dialogflow sends a request to a defined webhook service. The request consists of intent, parameters, actions, context, etc.
  • Webhook service processes the request (integration with DB, etc.)
  • Service sends the response message to Dialogflow
  • Agent sends the response to user
  • The user receives the response

More details you can find HERE and HERE.

Add word

An example of using fulfillment option functionality of adding a new word and its translation in the chatbot, which helps user to learn vocabulary. A template of conversation may look like this:

Conversation’s flow

So:

  • The user chooses “Add word” option from a menu or types it during a conversation
  • Bot asks the user for a word, he wants to add to the list
  • User types the word in English
  • Bot requests an external API to get translation’s suggestions and sends user back a message with provided suggestions to choose
  • User chooses one of the provided options or types his translation
  • Bot asks him for confirmation
  • This part of the conversation is finished

Implementation — intents

Intents I have created for the above flow are following:

When user sends Add word phrase (or a similar one), add_word intent will be matched and he will be asked for a word, he wants to add (in English). 

The next intent is add_word.handle-word. It is follow-up intent (fallback type), which is processed in the context of the parent and which reads everything the user writes. In our case, this intent is created to handle user message (word), find translation’s suggestions and generate a response to user. 

Another intent is add_word.handle-translation (follow-up fallback intent). It also waits for the user’s response (translation) and then generates a dynamic confirmation response. By answering Yes (add_word.adding.yes) or No (add_word.adding.no), the word will be saved or not, and the appropriate message will be displayed.

Implementation—
Dialogflow and fulfillment

The above description shows that there are 3 intents that require additional work (integration with the database, generating a dynamic response or querying the external API):

  • add_word.handle-word
  • add_word.handle-translation
  • add_word.adding.yes

To enable fulfillment, you have to switch on “Enable webhook call for this intent” setting in each of the listed intents.

Fulfillment enabling

The next step will be creating an endpoint, a webhook service, that will process requests sent from Dialogflow. As I mentioned earlier, for the purpose of the article and prototyping, there is no need to create and host a working, fully implemented API. You can just use one of the available tools to build API mock-ups. One of them is Mockservice.io, which allows you to easily create public and secured endpoints, that can simulate real services.

To create such an endpoint, you have to log in to the Mockservice.io, create a new project, add new endpoint (I called it VocabularyTrainer) and set Http Method to POST ( POST is sent from Dialogflow’s agent). However, for now, you don’t need to add any response (we already have a working, public URL to the endpoint we have just created).

Endpoint’s configuration:

  • Project: VocabularyTrainer
  • Endpoint: VocabularyTrainerEndpoint
  • HTTP method: POST
Endpoint’s configuration

Now, you need to copy newly created endpoint URL, back to Dialogflow andopen Fulfillments tab.

When you open it, enable Webhook and paste copied URL to the appropiate field.

In this way, we have just launched fulfillment option and for each specified intent, Dialogflow will send arequest to the created endpoint, awaiting the response from it. In the next section, I will show you how you can generate and implement that response to allow bot responding correctly.

Implementation— Dialogflow and fulfillment— response

The first intent we want to process is add_word.handle-word, which has to receive a word from user, find translation’s suggestions, save the word in a conversation context and send back to user response with found suggestions. To simulate this behavior you need to configure the previously created endpoint in Mockservice.io. One of the features of Mockservice.io is the ability to generate dynamic responses based on incoming requests. So you can create a dynamic response using JavaScript code. To use this option, got to Body section of the endpoint and change its type to Dynamic.

Dynamic body enabled

After enabling this option, we can generate a response by implementing the getBody (requestContext) method. 

function getBody(requestContext) {
let body = {};

return body;
}

The requestContext object contains information about the incoming request (e.g. http method, body, headers, etc.). It is useful, because in our case, it‘s important to know what intent is currently being processed to decide what response should we send back. (Remember that we cannot create separate endpoint for each intent — a webhook URL is once for all intents).

Implementation of add_word.handle-word intent may look like this:

function getBody(requestContext) {
let body = {};
let fulfillmentText = {};
let contexts = [];
let bodyJs = JSON.parse(requestContext.body);
if(bodyJs.queryResult.intent.displayName == "add_word.handle-word"){
let facebookPayload = {};
facebookPayload.payload = {};
facebookPayload.payload.facebook = {};
facebookPayload.payload.facebook.text = "Add translation or choose from suggestions";
facebookPayload.payload.facebook.quick_replies = [];
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Correr", payload:"Correr"});
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Nadando", payload:"Nadando"});

let context = {};
context.name = "projects/testpl-58158/agent/sessions/7b212397-801d-3819-e4da-98016c4adb3b/contexts/translation";
context.lifespanCount = 2;
context.parameters = {};
context.parameters.phrase = "to run";

body.fulfillmentMessages = [facebookPayload];
body.outputContexts = [context];

}

A few words of explanation:

let bodyJs = JSON.parse(requestContext.body);
  • converting string body to JavaScript object
if(bodyJs.queryResult.intent.displayName == "add_word.handle-word"){
  • checking the currently processed intent (checks if current intent is add_word.handle-word)
let facebookPayload = {};
facebookPayload.payload = {};
facebookPayload.payload.facebook = {};
facebookPayload.payload.facebook.text = "Add translation or choose from suggestions";
facebookPayload.payload.facebook.quick_replies = [];
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Correr", payload:"Correr"});
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Nadando", payload:"Nadando"});
  • creating a payload, which represents response with translation’s suggestions (in form of quick replies)
let context = {};
context.name = "projects/testpl-58158/agent/sessions/7b212397-801d-3819-e4da-98016c4adb3b/contexts/translation";
context.lifespanCount = 2;
context.parameters = {};
context.parameters.phrase = "to run";
  • setting custom context, which stores a word passed by user
body.fulfillmentMessages = [facebookPayload];
  • assigning fulfillmentsMessages property aspreviously created payload (in fact it is the response that will be shown to user)
body.outputContexts = [context];
  •  adding previously created context to the response

The endpoint, we have just implemented, which will be fired when add_word.handle-word is matched, will display suggestions to the user and save information about the entered word to the custom context.

As you can see, I hardcoded words and suggestions, so we do not make any hits to an external API. But this kind of dummy endpoint is enough to work on bot’s prototype. This allows us focusing on the business logic of the bot, not on implementation’s details.

The next intent we need to handle is add_word.handle-translation. Like in previous one, we need to add support for this intent in our endpoint. To do this I add the following code to the Body section:

else if (bodyJs.queryResult.intent.displayName == "add_word.handle-translation"){
let facebookPayload = {};

facebookPayload.payload = {};
facebookPayload.payload.facebook = {};
facebookPayload.payload.facebook.text = "Do you want to add this record?\n\nPhrase: *"+"to run"+"* 🇬🇧\nTranslation: *"+bodyJs.queryResult.queryText+"*",
facebookPayload.payload.facebook.quick_replies = [];
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Yes👍", payload:"Yes"});
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "No", payload:"No"});
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Again", payload:"Again"});

let context = {};
context.name = "projects/testpl-58658/agent/sessions/7b212397-801d-3819-e4da-98016c4adb3b/contexts/add_wordhandle-translation-followup";
context.lifespanCount = 2;

body.fulfillmentMessages = [facebookPayload];
body.outputContexts = [context];
else if (bodyJs.queryResult.intent.displayName == "add_word.handle-translation")
  • checking if processed intent is add_word.handle-transition
let facebookPayload = {};

facebookPayload.payload = {};
facebookPayload.payload.facebook = {};
facebookPayload.payload.facebook.text = "Do you want to add this record?\n\nPhrase: *"+"to run"+"* 🇬🇧\nTranslation: *"+bodyJs.queryResult.queryText+"*",
facebookPayload.payload.facebook.quick_replies = [];
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Yes👍", payload:"Yes"});
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "No", payload:"No"});
facebookPayload.payload.facebook.quick_replies.push({content_type: "text", title: "Again", payload:"Again"});
  • creating a dynamic response 
body.fulfillmentMessages = [facebookPayload];
body.outputContexts = [context];
  • creating context, which will be assigned to response

In this case, as in the previous one — we have just created a working dummy service that will send confirmation of adding a word to the database to the user.

The last intent we need to handle is add_word.adding.yes, which is responsible for saving data to the database. In our case, since this is dummy implementation, we do not have to process/saving anything here.

The created endpoint can simulate the real API (which will be created one day) and generates different responses depending on the phase of the user’s conversation with the bot.

Summary

Today we managed to create a fragment of the conversation responsible for adding a word and its translation to the database. Then we learned what fulfillment, how it works, and how to turn it on. Finally, we created a mock-up of a webhook service using Mockservice.io and created the endpoint, which generates different responses, depending on what phase of the conversation it is in.

Share: