11 How to use webhook for slot filling in Dialogflow ES
Dialogflow ES Quickstart Templates Part 2
While the slot filling feature in Dialogflow ES makes for a very good demo, in practice it is really hard to use.
One option is to use webhooksYou can use webhooks to add custom business logic in both Di... More to assist your slot filling. This will give you the best of both the worlds – you allow Dialogflow ES to extract maximum possible relevant information from the user’s first sentence. At the same time, you will exit the slot filling immediately after the first sentence by using follow up events. This helps you avoid the slot filling loop and gives you more fine grained control of your conversation flow. The downside is that this requires a lot more effort on your part.
Note: Dialogflow CX can do all this natively, and without even using a webhook. That is why I think the implementation of slot filling in Dialogflow CX is far superior to the one in ES.
Let us see how we can convert the flight booking slot filling chatbot to use webhooks for slot filling. The basic idea is quite simple: as soon as the user utters the first sentence
- we will check all the slots the user has input in the first utteranceAn utterance is simply what the user says to the bot. Both E... More
- we will select the next slot based on a pre-existing slot ordering
- we will trigger a custom event (also called followup event) to exit the slot filling loop
- as we keep collecting the remaining slots, the webhook code will check which slot has not yet been filled and call followup eventsEvents are used in both Dialogflow ES and Dialogflow CX as t... More for that slot
- once all slots have been filled, we will trigger a custom event called completed
This article actually covers three concepts simultaneously (it is a pretty complex bot):
- How to use webhooks for slot filling
- How to set a parameterBoth ES and CX support the concept of parameters. If entitie... More value in an output contextContexts are used in Dialogflow ES to manage the state of th... More from your webhook code
- How to trigger a follow up eventYou can use follow up events in Dialogflow ES to trigger an ... More (custom event) from your webhook code
First add a session-vars output context to the book.a.flight intent and also select the “Enable webhook for slot filling” toggle.
The webhook code will have a single method called check_slots
@app.route('/webhook', methods=['POST'])
def hello_webhook():
req = request.get_json(silent=True, force=True)
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(req, f, ensure_ascii=False, indent=4)
result = check_slots(req)
return result
The check_slots method does all the heavy lifting
def check_slots(req):
try:
ordered_slots = ['departuredate', 'returndate', 'flightclass', 'numpassengers', 'fromcity', 'tocity']
query_result = req.get('queryResult')
params = query_result.get('parameters')
context_prefix = get_context_prefix(query_result)
session_vars = get_session_vars(query_result)
params_from_session_vars = get_params_from_session_vars(session_vars)
filled_params = get_filled_params(params, params_from_session_vars)
first_unfilled_slot = get_first_unfilled_slot(filled_params, ordered_slots)
result = {
"outputContexts": [
{
"name": f'{context_prefix}/session-vars',
"lifespanCount": 50,
"parameters": params
}
],
"followupEventInput": {
"name": first_unfilled_slot,
"languageCode": "en"
}
}
with open('result.json', 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=4)
return result
except Exception as e:
error_str = str(e)
return generic_error_message
Notice the following information being returned in the response of the check_slots method
In the outputContexts JSON object array, we send some parameters back to Dialogflow ES inside the session-vars context. This is what I mean when I say “set a parameter value in an output context from your webhook code”
Then we get the first unfilled slot and use it as the follow up event. As you will see later, this in turn will fire a specific intent in your agent which has a custom event that matches the name of the slot. This is what I mean when I say “trigger a follow up event (custom event) from your webhook code”
The get_context_prefix method gets the prefix string which is sent to the webhook for all output context objects as shown in the image.
This is the code for the get_context_prefix method
def get_context_prefix(query_result):
output_contexts = query_result.get('outputContexts')
first_output_context = output_contexts[0]
context_prefix = (str(first_output_context.get('name')).rsplit('/', 1))[0]
return context_prefix
Then we get the session-vars object. In the first webhook call, it will be empty. This is because the session-vars is “populated” only after the webhook call returns.
def get_session_vars(query_result):
session_vars = None
output_contexts = query_result.get('outputContexts')
session_vars_iter = [context for context in output_contexts if
str(context.get('name')).endswith('/session-vars')]
if len(session_vars_iter) > 0:
session_vars = session_vars_iter[0]
return session_vars
Then we get the list of parameters from the session-vars object. We need to “repopulate” this into the output context.
def get_params_from_session_vars(session_vars):
params_from_session_vars = []
if session_vars:
for key, value in session_vars.get('parameters').items():
if str(value).strip() != '' and not str(key).endswith('.original'):
params_from_session_vars.append(key)
return params_from_session_vars
Then we find the list of parameters which have already been populated. We need to combine the parameters which were extracted in the current step and the parameters which were extracted in the previous steps.
def get_filled_params(params, params_from_session_vars):
filled_params = []
for key, value in params.items():
if str(value).strip() != '' and not str(key).endswith('.original'):
if key not in params_from_session_vars:
filled_params.append(str(key).strip())
filled_params.extend(params_from_session_vars)
return filled_params
Once we do all these steps, we are ready to calculate the first unfilled slot
def get_first_unfilled_slot(filled_params, ordered_slots):
first_unfilled_slot = 'completed'
for slot in ordered_slots:
if slot not in filled_params:
first_unfilled_slot = slot
break
return first_unfilled_slot
For each slot, we will create two intentsBoth Dialogflow ES and Dialogflow CX have the concept of int... More. The first intent will have the name followupevent.<slotname> and it will have the custom event and one response. Do not add training phrases into this intent. In addition we will set an output context called await_<slotname> for this intent. This intent will not call the webhook.
Then we will have another intent called provides.<slotname>. It will use await_<slotname> as the input context and here we will have training phrases corresponding to the slot data type. This intent will call the webhook.
Example intent definitions for departure date slot
Here is the example intent definition for the tocity slot
You can do the same for the other slots and complete the bot
Here is a demo of the bot in actionYou define an action in Dialogflow ES intents to tell your w... More
And another:
About this website
I created this website to provide training and tools for non-programmers who are building Dialogflow chatbots.
I have now changed my focus to Vertex AI Search, which I think is a natural evolution from chatbots.
Note
BotFlo was previously called MiningBusinessData. That is why you see that watermark in many of my previous videos.