As you might know, I don't recommend using slot filling.
This leads us to a question: one of the benefits of slot filling is that you can re-prompt if user provides the input in an unexpected format. (However, that is a small(ish) benefit for the much bigger problem of being unable to predict your chatbot's behavior when using slot filling) How can we replicate that behavior?
So we would like to do something which will "mimic" slot filling while not running into the slot filling loop issue.
Getting a phone number as input from the user
Let us say we want to get a phone number from the user.
1 We will prompt for the phone number
2 If user provides an unexpected number (either Dialogflow cannot parse it, or they type in something which is not a phone number), we give them a second try to get the number right
3 If the second attempt also doesn't work, we will not prompt the user again (unlike slot filling which goes into an endless loop)
This is what the flowchart looks like:
Now let us take a look at the intent definitions.
Intent 1: Welcome Intent
This one is self-explanatory. When the user says "Hi" or "Hello", the bot prompts them for their phone number and sets the output context "awaiting_phone"
Intent 2: UserProvidesPhoneNumber
Now let us create the intent which handles the case where the user provides the correct phone number.
There are plenty of things you need to take note here.
1 Dialogflow's phone number entity is quite poor and matches just about anything
You can take a look below, and see what Dialogflow does when it tries to match a phone number entity.
It matches a number which has nothing more than an area code (US format)
It matches made up phone numbers
And it gets even more interesting. It even does intent mapping while failing to capture the actual number. Note that the parameter phone-number below is actually empty.
2 So you need to enable the webhook on the intent
As you can see from the intent definition, the webhook is enabled. On the webhook, you will use some kind of regex to see if the phone-number parameter is a valid phone number in the region of your choice.
3 I have omitted the webhook code
In this tutorial, I have not added the webhook code for parsing the parameter and validating the phone number since it is (obviously) very unique to the programming language that you use.
4 The webhook will change the output context based on whether the phone number is valid
If the phone number is valid, the webhook will not change the context. That is, the next context should be awaiting_email if the phone number is valid, and the webhook will leave it as it is.
On the other hand, if the phone number is invalid, the webhook will change the output context to awaiting_phone_2 (as seen in the flowchart). To see an example of setting the output context from your webhook code, check out this article. That example uses NodeJS, but at the end of the day you can set the context by making sure you set the correct JSON, which should be quite simple in any modern programming language.
This is what causes the branching you see on the flowchart.
Intent 3: PhoneNumberFirstFallback
When we set the output context awaiting_phone, the user can provide a lot of different types of numbers and Dialogflow will still map the intent and try and capture the number.
But what if the user types complete gibberish at that point?
That's when we will use a context-based fallback intent. That is, we create a new Fallback Intent and add a context to it.
In the Intents list view, click on the three dots next to the Create Intent (blue) button and from the dropdown menu select "Create Fallback Intent"
And define the intent as follows:
What is happening here?
When you create a Fallback intent which uses the same context as the one which gets the regular input (in this case awaiting_phone), anything that Dialogflow cannot understand automatically goes to this "context-based" fallback intent.
For example, take a look below:
As you can notice, when the user types "this is some gibberish" (remember that the context has already been set to awaiting_phone in the previous step) - the text pattern is so different from the regular UserProvidesPhoneNumber intent that Dialogflow realizes it cannot handle this pattern at all. So it goes to the context-based fallback and tries to nudge the user in the right direction. That is, it asks them to provide the number in a specific format. In addition, it also sets the output context to awaiting_phone_2, indicating that Dialogflow is ready for the second attempt from the user to provide a phone number.
Why is there no webhook call?
Do we need to call the webhook in the context-based fallback intent? No, we don't.
As you can see, we don't capture any number at all in the intent. The mere fact that this intent got triggered means that there is nothing for us to validate, hence no need to call the webhook.
Intent 4: UserProvidesPhoneNumber_2
Now we create the intent which handles the user's second attempt to input the phone number.
Hopefully, we have already indicated (via the webhook's text response) the specific format the user should use. If the user is able to understand the format, they can try to fit their phone number into the format specified.
This is the intent definition:
What is happening here?
We keep the input context as awaiting_phone_2 (indicating the second attempt) and make the output context as whatever we need for the next intent. For now, let us suppose it is to collect the email address and so we set awaiting_email as the output context.
Do we need to call the webhook?
Now we have told the user the exact format we expect. So there isn't any way for the user to make a mistake.
So can we suppose all is good and forget about the validation this time?
Of course not!
We need to send whatever input Dialogflow received to the webhook again, and once again the webhook will branch the conversation based on whether it can validate the phone number.
What does the webhook do?
If the webhook still cannot validate the phone number, we need to decide what should happen next. Do we really want to prompt the user another time? It certainly depends on your use case, but the user has had two chances and it is probably alright to stop prompting for yet another attempt. (Although if you choose to do so, you can use the same approach)
The webhook response should be something like "Unfortunately, I still didn't get that. Please contact support at 888-888-8888". Which is exactly what you will use for the context-based fallback for awaiting_phone_2 as you can see below.
Intent 5: PhoneNumberSecondFallback
Its possible the user types some more gibberish the second time around. Or they might just say "I don't want to tell you my number".
This is not going to match the text pattern in UserProvidesPhoneNumber_2 intent, and will trigger a fallback. Since we have decided not to prompt the user yet again, at this point we can just call it quits.
Why not just use slot filling?
At this point you might be wondering - does it really have to be this much work? Why can't I simply use slot filling instead?
One, slot filling suffers from pretty much exactly all the same issues when it comes to entity extraction. You can go verify this for yourself.
Second, if your webhook cannot validate the number from the intent (during slot filling), it is actually a whole lot more tricky to set up your output contexts to handle the situation gracefully.
Third, this approach gives you a lot of fine grained control as you can already see.
Fourth, you cannot control the order of the "reprompt" messages in slot-filling, so unlike my approach, you will not even be able to progressively help the user.
At the same time, if you can get it to work using slot-filling, I would love to hear about how you did it, so please leave me a comment.