IBM Watson Assistant is a service that enables the building of chatbots, and other applications that need natural language processing capabilities. A common pattern needed in chatbots is to gather a set of information, much like you would on a traditional web form.
With a chatbot, the complication is that you want to be able to accept all pieces of information up front if provided, and then only prompt for the missing pieces. If you have read one of my previous articles, Conversation Patterns with IBM Watson, you may remember there was a section on exactly this, but you had to handcraft a set of nodes to do it. Watson Assistant has been updated with a new capability which makes it much easier to build this pattern.
For the purpose of this article, we’ll be creating a simple version of a pizza ordering bot.
We’ll start with something really simple. We want to recognise that the user wants to order a pizza and we want to ask them for the size. First we create our #order intent:
and our entities:
Now we create a node that responds to the user asking to order a pizza:
Now let’s use the new capability to make sure the user tells us the size of pizza that they want. We can enable it by clicking on the “Customize” link at the top of the node editor, and then selecting “Slots” in the Customize modal.
You will now see a new section in the node editor that allows us to define the information we want to gather.
So now we can gather information about the size of pizza being ordered.
This is saying, if the input contains @size then save it in the $size context variable. If @size is not specified then ask the user for it using the question text.
Now we can update the node response to indicate what size was chosen.
Let’s see this in action. If I ask for a pizza but don’t give the size, it asks me for it:
But if we give the size upfront then it doesn’t need to prompt:
I want to know more!
Ok, so that was easy, right? But what if we want to gather more information, and what if we want to gather a list of information, like the pizza toppings?
We can create an entity for our toppings:
and add another prompt to capture them:
Now this will only capture the first topping the user says, so we need to adjust this to get all of them. We can simply use the values property of the entity to get them:
And update our response to show the toppings:
Now let’s see this in action. Notice that the ordering of prompts affects the behavior as it will prompt for the earlier ones first.
Tell me everything straight away!
Wouldn’t it be nice if we could tell the user everything we needed upfront to give them a chance to provide it all on the first go? We can do this by selecting “Prompt for everything” from the Customize modal.
This will add a section just below the slots:
Here we can add text to ask for all the information we want to collect:
And let’s see it in action:
So now the user knows upfront what information we want, and we will still prompt for anything they don’t provide.
There could be some information which we want to collect if it’s supplied but we don’t want to explicitly prompt for it. For example, since cheese is usually assumed on the pizza we wouldn’t want our bot to explicitly ask if the customer wants cheese or not, but if they tell us that they don’t want cheese then we should capture it.
We can do this with an optional slot, so if the information is provided then it is captured but it won’t be asked for. So for our pizza bot we can define an entity for dairy-free:
And then add an optional slot to handle this:
Note that there is no prompt, and it says “Optional”. Leaving the prompt empty like this makes the slot optional. Adding the $dairy_free variable to our response then gives us:
That’s not what I want to know
What happens if the user doesn’t tell us the right things when we prompt? At the moment, it will just keep prompting with the same text. How about we help the user out with some additional responses?
Click the little cog icon in the slot. This will bring up an editor where we can define additional behaviour when users responds. We can add behavior for “Found” and “Not found” to have different responses when the user provides correct or incorrect information respectively.
Let’s add a “Not found” response to provide some more information on what we want to know. Note if you want to add conditions to these found and not found responses, click on the 3 dot button at the top right and select “Enable conditional responses”.
So now, if the user doesn’t provide a size, we will re-prompt them but providing more information about what sizes are available.
You got it!
If you want to give users a warm fuzzy feeling by showing that your bot understood them, you can add a “Found” response to confirm the input.
Which results in this:
Are you sure?
We have everything working now and can order pizzas to our hearts content, but wouldn’t it be nice to have a confirmation step before the order is placed?
We can do this by adding another slot like this:
Notice that we have added a “Not found” response which clears the $size and $toppings variables. This tells the node that it has to reprompt for these.
So let’s try it out:
Or maybe we changed out minds:
But what about this other thing?
So we’ve got our prompts all set up and we can take pizza orders, but what if the user asks something else while we are prompting them for information, e.g. they ask about delivery:
The bot doesn’t understand and just carries on prompting. We can add some additional responses to handle these situations. First we need to add an intent for the delivery question. Then we can add handlers. Click on the “Manage handlers” link above the slots, and add a handler conditioned on #delivery and provide a suitable answer:
And let’s see how this works:
Go gather info!
And there we have it. A single node that allows us to simply gather information from the user in a robust and conversational way. Have a go in your bot!
Find more of my Watson articles in the Conversational Directory.