Home / DialogFlow ES / How to use webhook for slot filling in Dialogflow ES
DialogFlow ES

How to use webhook for slot filling in Dialogflow ES

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 webhooks 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 utterance
  • 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 events 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 parameter value in an output context from your webhook code
  • How to trigger a follow up event (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 intents. 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 action

And another:

<— End of article —>


This website contains affiliate links. See the disclosure page for more details. 
"The magic key I needed as a non-programmer"

The custom payload generator was the magic key I needed (as a non-programmer) to build a good demo with rich responses in DialogFlow Messenger. I've only used it for 30 minutes and am thrilled. I've spent hours trying to figure out some of the intricacies of DialogFlow on my own. Over and over, I kept coming back to Aravind's tutorials available on-line. I trust the other functionalities I learn to use in the app will save me additional time and heartburn.

- Kathleen R
Cofounder, gathrHealth
"Much clearer than the official documentation to be honest"

Thanks a lot for the advice (of buying and following your videos)! They helped a lot indeed. Everything is very clear when you explain, much clearer than the official documentation to be honest 🙂

Neuraz T
Review for Learn Dialogflow CX
"I will strongly recommend this course because even I can learn how to design chatbot (no programming background)"

I think Aravind really did a great job to introduce dialogflow to people like me, without programming background. He organizes his course in very clear manner since I have been a college professor for 20 years. It is very easy for me to recognize how great Aravind’s course is! Very use-friend and very easy to follow. He doesn’t have any strong accent when he gives the lectures. It is so easy for me to understand. Really appreciate it.

Yes, I will strongly recommend this course because even I can learn how to design chatbot (no programming background) after studying Avarind’s course, you definitely can!

Ann Cai
Review for Learn Dialogflow ES

Similar Posts