Additionally, you want the agent to manage and repair the conversation if it doesn't go as expected, so you'll add some contexts and fallback intents.
Fulfillment (Webhook)
In order to add response logic or include the results of an API call in your agent's response, you need to setup fulfillment for the agent. This includes some basic JavaScript and setting up and hosting the files in a cloud service. This guide uses a Google Cloud Project for the hosting and deployment.Create a Starter JS File
To start, create a directory on your local system for the code:- Linux or Mac OS X:
mkdir ~/[PROJECT_NAME] cd ~/[PROJECT_NAME]
- Windows:
mkdir %HOMEPATH%[PROJECT_NAME] cd %HOMEPATH%[PROJECT_NAME]
index.js
file in the project directory you just created, with the following code:/*
* HTTP Cloud Function.
*
* @param {Object} req Cloud Function request context.
* @param {Object} res Cloud Function response context.
*/
exports.helloHttp = function helloHttp (req, res) {
response = "This is a sample response from your webhook!" //Default response from the webhook to show it's working
res.setHeader('Content-Type', 'application/json'); //Requires application/json MIME type
res.send(JSON.stringify({ "speech": response, "displayText": response
//"speech" is the spoken version of the response, "displayText" is the visual version
}));
};
Setup Google Cloud Project
- Follow "Before you begin" steps 1-5
- Deploy the function
gcloud beta functions deploy helloHttp --stage-bucket [BUCKET_NAME] --trigger-http
helloHttp
is the name of our project. You should have created your project in step 1 and set the project at the end of step 4 when you initializedgcloud
.--stage-bucket [BUCKET_NAME]
can be found by going to your related Google Cloud project and click on Cloud Storage under the Resources section.--trigger-http
More information
https://[REGION]-[PROJECT_ID].cloudfunctions.net/helloHttp
Enable Webhook in Dialogflow
- In Dialogflow, make sure you're in the correct agent and click on Fulfillment in the left hand menu
- Toggle the switch to enable the webhook for the agent
- In the URL text field, enter the httpTrigger url you got when you deployed your function
- Click Save
Enable Fulfillment in Intent
- Navigate to the "weather" intent
- Expand the Fulfillment section at the bottom of the page
- Check the Use Webhook option
- Click Save
Try it out
<p>[This section requires a browser that supports JavaScript and iframes.]</p> In the Dialogflow test console, enter "weather". You will see the webhook response we defined in the function. This means the webhook is working! You should also see the two parameters we need from our user,date
and geo-city
.Setup Weather API
Get API Key
For this sample, we use the WWO (World Weather Online) service, so you'll need to get an API key. Once you register, login and make note of your API key.Update Code
Now that we have an API key, we can make requests of the weather service and get actual data back from them.Replace the current code in index.js with the code below. This adds communication with the weather service, our API key, and functions to handle our queries.
// Copyright 2017, Google, Inc. // Licensed under the Apache License, Version 2.0 (the 'License'); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an 'AS IS' BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. 'use strict'; const http = require('http'); const host = 'api.worldweatheronline.com'; const wwoApiKey = '[YOUR_API_KEY]'; exports.weatherWebhook = (req, res) => { // Get the city and date from the request let city = req.body.result.parameters['geo-city']; // city is a required param // Get the date for the weather forecast (if present) let date = ''; if (req.body.result.parameters['date']) { date = req.body.result.parameters['date']; console.log('Date: ' + date); } // Call the weather API callWeatherApi(city, date).then((output) => { // Return the results of the weather API to Dialogflow res.setHeader('Content-Type', 'application/json'); res.send(JSON.stringify({ 'speech': output, 'displayText': output })); }).catch((error) => { // If there is an error let the user know res.setHeader('Content-Type', 'application/json'); res.send(JSON.stringify({ 'speech': error, 'displayText': error })); }); }; function callWeatherApi (city, date) { return new Promise((resolve, reject) => { // Create the path for the HTTP request to get the weather let path = '/premium/v1/weather.ashx?format=json&num_of_days=1' + '&q=' + encodeURIComponent(city) + '&key=' + wwoApiKey + '&date=' + date; console.log('API Request: ' + host + path); // Make the HTTP request to get the weather http.get({host: host, path: path}, (res) => { let body = ''; // var to store the response chunks res.on('data', (d) => { body += d; }); // store each response chunk res.on('end', () => { // After all the data has been received parse the JSON for desired data let response = JSON.parse(body); let forecast = response['data']['weather'][0]; let location = response['data']['request'][0]; let conditions = response['data']['current_condition'][0]; let currentConditions = conditions['weatherDesc'][0]['value']; // Create response let output = `Current conditions in the ${location['type']} ${location['query']} are ${currentConditions} with a projected high of ${forecast['maxtempC']}°C or ${forecast['maxtempF']}°F and a low of ${forecast['mintempC']}°C or ${forecast['mintempF']}°F on ${forecast['date']}.`; // Resolve the promise with the output text console.log(output); resolve(output); }); res.on('error', (error) => { reject(error); }); }); }); }
Deploy Function (again)
Now that our function is different, we need to deploy again and alter the command. This is due to the code having a new function name exported.gcloud beta functions deploy weatherWebhook --stage-bucket [BUCKET_NAME] --trigger-http
Once the new function is deployed, make a note of the new httpTrigger url. It should look something like this:
https://[REGION]-[PROJECT_ID].cloudfunctions.net/weatherWebhook
Update Fulfillment in Dialogflow
- Return to Dialogflow and click on Fulfillment in the left hand menu
- Replace the current URL with the new httpTrigger url.
https://[REGION]-[PROJECT_ID].cloudfunctions.net/weatherWebhook
- Click Save
Conversation Branching
We can't expect users will always provide all the information our agent needs to fulfill their request. In the case of our weather agent, a city and a date are required as inputs for our function. If no date is provided, we can assume the user is referring to "today" or the current date, but there's no way to gather the user's location or city through Dialogflow so we need to make sure we collect that.Making Location Required
<p>[This section requires a browser that supports JavaScript and iframes.]</p>- In the "weather" intent, locate the
geo-city
parameter and check the Required option. This will reveal an additional column called Prompts. These are responses the agent will give when this specific data isn't provided by the user. - Click on Define prompts and enter the following response:
- For what city would you like the weather?
- Click Close
- Click Save on the Intent page
Give it a go!
Again, enter "weather" into the console and you should see the prompt to collect the city.<p>[This section requires a browser that supports JavaScript and iframes.]</p>
Add Location Context
In order to refer to data collected in a previous intent, we need to set an output context. In this case we want to "reuse" the value collected for location.- In the "weather" intent, click on Contexts to expand the section
- In the Add output context field, type "location" and press "Enter" to commit the context
- Click Save
Create a New Intent for Context
We want to be able handle additional questions using the same location, without asking the user for the data again. Now that we've set an output context, we can use it as the input context for the intent that handles the additional questions.- Click on Intents in the left hand menu or click the plus icon add to create a new intent
- Name the intent "weather.context"
- Set the input and output context as "location"
- Add the following User Says example:
- What about tomorrow
- Add a new parameter with the following information:
- Parameter Name:
geo-city
- Entity: empty
- Value:
#location.geo-city
- Parameter Name:
- Add the following reply in the Response section:
- "Sorry I don't know the weather for $date-period in #location.geo-city"
- Click on Fulfillment in the menu to expand the section and check the Use webhook option
- Click Save
Take it for a test drive!
In the Dialogflow console, enter "weather" and get the reply asking for the city. Then enter a city of your choosing. You'll see a response like the one above, which includes the data retrieved from the service. You can then ask questions like "What about tomorrow" to retrieve the forecast for that date.<p>[This section requires a browser that supports JavaScript and iframes.]</p>
Conversation Repair
Now that the main conversational function of our agent is complete, we want to make sure our agent welcomes the user and knows how to respond to requests that are not related to the weather.Editing the Default Fallback Intent
When our user responds with an unrelated query, we want our agent to reply gracefully and direct the user back into "familiar territory." For this we'll edit the existing Default Fallback Intent.- Click on Intents, then Default Fallback Intent
- Click on the trash can icon delete in the upper right hand corner of the Text Response table
- Click on Add Message Content and choose Text Response
- Enter the following responses:
- I didn't understand. Can you try again?
- I don't understand what you're saying. You can say things like "What's the weather in Paris today?" to get the weather forecast.
- Click Save
One more time!
Enter an unrelated request into the console and you'll get one of the two fallback responses.<p>[This section requires a browser that supports JavaScript and iframes.]</p>
Editing the Welcome Intent
Finally, we want our agent to greet users and maybe provide some ideas as to what they can ask.- Click on Intents, then Default Welcome Intent
- Click on the trash can icon delete in the upper right hand corner of the Text Response table
- Click on Add Message Content and choose Text Response
- Enter the following response:
- Welcome to Weather Bot! You can say things like "What's the weather in Mountain View tomorrow?" to get the weather forecast.
- Click Save