The Advanced Chatbot Development Course
When you build a chatbot, you sometimes have to make a decision between 2 options that both work, but have slightly different benefits and disadvantages, depending on the channel, audience, use case or personal preference.
For example, some bot projects might be better off by working with Quick Replies instead of Button Replies for the menus, and for other chatbots it will be the other way around.
For other bots, both options won’t work due to Channel Limitations, so for example for WhatsApp or SMS chatbots, you might want to use a totally different type of menu: Textual Selection Lists.
This illustrates that even a simple function like a menu can be built in several ways. Since Flow.ai is such a flexible platform, there is a good solution to pretty much every situation, but when something works it doesn’t necessarily mean it wouldn’t work better if it was handled in a different way.
Because every use case is different, it’s important to thoughtfully consider the chatbot strategy every time you create a chatbot, whether you are just a beginner or even a seasoned conversational designer.
A thorough understanding of the differences between your strategic bot options can truly improve your development efficiency and enrich your customer experience.
This article elucidates the difference between multiple strategic options to choose from when developing your chatbots:
- Different methods for asking closed user input
- Asking names
- Unknown, Intents or another Any Text next to an Any Text that should be a specific type
- Escape routes for Fallback
- Capturing user input in a single or multiple Any Texts
- Intents with or without context
- A general Unknown or separate Unknowns within context
- Multiple intents or a single intent with entities
- Handover procedures
- Chatbot launching procedures
At the end of this article you can see a quick overview of all strategic chatbot recommendations.
This article is meant for more experienced conversational designers. If you are just getting started, we recommend reading the Complete Guide From Beginner to Expert in Chatbots and Conversational Design and watching the YouTube Beginners Tutorials Playlist first.
Different methods for asking closed user input
A lot of chatbots tend to display a couple of options for the user to choose from. This can be a Yes/No question, a menu with topics to choose from, or a product selection, just to name a few.
The 5 most commonly used solutions for these types of questions are:
- Button Replies
- Quick Replies
- Textual Selection Lists
- Only intents
The first 3 options make use of rich UI elements, which are not supported by some channels like SMS and WhatsApp. The first step when concepting your bot is to have a clear understanding about those Channel Limitations, so you won’t be held up by unwelcome surprises along the road.
This means that for some channels you’ll have to choose between Textual Selection Lists or just using intents for capturing user input.
Textual Selection Lists versus only intents
Let’s say we created a simple chatbot that can respond to 3 questions:
- What is your address?
- What are your social media channels?
- What are your payment methods?
You could implement these questions with just intents (in the context), like shown below:
This option could theoretically work, but it’s not clear to the user what kinds of questions they can ask.
That’s why it would already be a bit better if we change the opening message:
This makes it easier for the user to get an idea of what they are expected to say. This reduces the chance that they will say something the bot doesn’t understand.
Now we just have 3 options to choose from, but sometimes there will be 10 or even more options. Summing up all of those options in a simple text could make it a bit unclear. To give more structure to your options, you can create a Textual Selection List:
Generally speaking, the more options, the better it would be to display them as a Textual Selection List.
When you work with these Textual Selection Lists, you’ll have to keep in mind that some of the users will not respond with “Address” or “What’s your address?”. They can now also respond with “Option 1” or just “1”.
The bot wouldn’t understand that right now, so we will have to do something about that. There are multiple ways to do this.
The easiest way would be to simply add the numbers to the intents like this:
If you do it this way, make sure to only do it for intents that are placed in the context. This method should never be used for intents on the top-level of a flow.
Confused about what context actually is? No worries! We have a tutorial for it:
In this example, you’ll probably want to add the intents to the top of separate flows, so they are accessible at any time. This means we will have to use another method for this Textual Selection List.
In the image below, you can see how this flow can be built ideally.
This flow looks a lot more complicated than the previous one, but it’s actually pretty easy when you know what’s going on. We have numbered the comments so we can go over them one by one.
1. Save the choice with the Any Text Trigger and configure it so that it should be a number or the entity
By clicking on the Any Text Trigger, you can change its name and select what type of text it should be. We advise not keeping the default name “Something”, because it’s always good practice to give your parameters a logical name. Since we are only looking for a number, select “Should be number”.
There is also a method where you don’t only allow the user to respond with a number, but also by typing the topic itself. In this particular case that wouldn’t be necessary since we also have intents for “What’s your address?”, “What are your social media channels?” and “What are your payment methods?”.
However, when you don’t have these intents, you might want to use entities for this. This would be useful for selecting something like a product, or in this case a type of vehicle:
Notice we also added words like “1” and “first” to the entity lists. If you set it up with the use of entities instead of only numbers, configure the Any Text Trigger that it should be that entity instead of a number.
2. Use conditions to route the user
After capturing the user input, the next step is to create a condition for every possible value.
If you are using entities instead of numbers, set the conditions to “Is equal to custom entity”.
If for some reason multiple numbers/entities should route to the same flow, you could use the same condition for them. Add a rule for every option to the condition and configure the condition so that ANY rule is valid.
3. Route the user to the right flow by using Event Replies
This one is pretty straightforward.
4. Add a 4th Else condition in case someone responds with an invalid number
If you have 3 options, but the user for some reason responds with something like “4” or “12947129”, the Any Text Trigger will still get triggered.
If you only have a condition for 1, 2 and 3, the bot won’t be able to route the conversation forwards if someone entered another number. This means the bot won’t respond and the conversation will be stuck.
That’s why it’s best practice to always add an Else condition, just in case something happens you didn’t expect. There you can give an error message to the user and loop back.
When you are working with entities in your Any Text Trigger, you might think it’s not really necessary, and you are probably right, but it won’t hurt to add an escape route for the bot, just to be sure.
This additional Else condition with the error message is not just advised when using Textual Selection Lists, but it’s useful for pretty much any situation where you use conditions.
5. Add an Any Text that should be any text as a fallback option
If someone doesn’t respond with a number or with the right entity in his or her message, you can add a fallback option next to the Any Text to let them know they didn’t enter a valid entry and ask them to try again.
If you don’t do this, the general Unknown from your Unknown flow will get triggered when the bot isn’t able to match the Any Text Trigger.
The problem is that the user will then lose the context from the previous flow. This means that if they respond with the right number or entity after the general Unknown message, the bot won’t be able to match it with the contextual Any Text.
6. Reset the parameter before capturing the new value
When you use multiple Textual Selection Lists, or when your user gets through your only Textual Selection List multiple times, you need to make sure that the parameter for capturing the number (or entity) is empty.
When a parameter that already has a value gets a new value because the Any Text is matched again (or another Any Text with the same name gets matched), you might expect that the new value will overwrite the old one, but that’s not what actually happens. The parameter will now contain both values.
In some cases that’s useful, but it’s not what we want for our Textual Selection List. If the user first typed “1” and types “2” the next time, the value of “number”, will be “1, 2”, and it will still match condition A.
This means we will have to reset it before giving it a new value, so we are sure it doesn’t have two values and it will always match the condition of the new entry. Make sure to only reset the parameter instead of resetting everything.
7. Loop the user back when given an invalid response
Don’t forget to loop the user back to the point where you ask for the input. Route back to the point just above the Reset.
You can route it back to above the Text Reply with the question as well, but since you just asked that question, it’s not really necessary to send that Text Reply again.
Button Replies vs Carousels vs Quick Replies
If your channel(s) support rich UI elements, you might want to create menus with Button Replies, Carousels or Quick Replies. These are commonly used for Messenger and Web Widget chatbots.
Note that for Twitter you can only use Quick Replies. Button Replies will only work when they have the URL type.
If you create an omnichannel chatbot that works on Messenger and Twitter for example, you might want to create the menus for both versions by using Quick Replies, so a single bot works for both channels.
This makes the development process a bit faster and less vulnerable to mistakes. This isn’t really necessary though if you prefer using a different method on Messenger or Web.
For Web Widget and Facebook Messenger chatbots, all 3 options have advantages over the other ones.
With Carousels you can make it look visually more appealing by having the option to add images, but the user would have to navigate through the cards to see all the options.
If the user then doesn’t know or want to scroll through the different cards, they might not see the option they are looking for.
When you want to ensure the user has the clearest overview of all the options, especially if there are a lot, Textual Selection Lists would generally work best.
You could also add Quick Replies to your Textual Selection List, but that would make it look a bit weird on all channels except for Twitter, where it could actually be pretty useful.
On other channels like Messenger and Web, we recommend only using Quick Replies for simple responses like:
- Main Menu
- Talk to Agent
A lot of people want to personalise their customer experience by capturing the name of the user and using the name in their bot responses.
Theoretically, you could create a flow like this for asking a name:
The problem here is that there are endless possibilities for a name, and it’s practically impossible to let the bot check if the user input is a valid entry, like it can with a date or an email address.
If someone doesn’t respond with a name, but types something inappropriate, the bot would respond with that word instead. That’s not what you want of course.
You can counter this with a profanity filter, but there might always be the possibility you left some words out on that list.
There is also another problem besides the profanity. The bot will not be able to extract the name from a longer text message, but it will capture the entire message as the name. When somebody types “I’m John”, instead of just “John”, the bot will respond with “Nice to meet you I’m John!”.
The third problem you can encounter is that the Any Text will literally capture any form of text. This means that if someone doesn’t want to give their name or tries to match another intent, that message will also get captured:
In the example above, the project contained an intent for “What is your address?”, but that intent was not possible to match the first time because the bot was capturing the message as a name.
So in short, we advise not to try responding with a name with your chatbot. The benefits do simply not outweigh the disadvantages and increased complexibility.
However, when you use your chatbot for gathering contact information before handing it over to an agent, you might still need to know their name even though you won’t need to let the chatbot do something with it.
In that case you could still let the bot ask for it and save it. If they didn’t enter a valid name the bot would still continue and if the agent really needed it he or she can always manually ask again.
This applies to any information that cannot be validated, not just to names.
Unknown, Intents or another Any Text next to an Any Text that should be a specific type
If you are using an Any Text Trigger that has the configuration that it should be text, any type of text message will be captured, and the flow will go on.
If you set it so it should be an email address for example, the Any Text will not get triggered when someone types a message that doesn’t include an email address. In that case, 3 things can happen:
- An intent next to the Should Be Email Address Any Text gets triggered
- An intent on the top of another flow gets triggered
- The Unknown will be triggered
If an intent in another flow or the Unknown gets triggered, the user will go to another flow and the context of the flow for capturing the email address will be gone.
For some use cases that’s not really a problem. Sometimes you don’t really necessarily need an answer to a question. In that case you could just keep it this way.
However, most of the time you’ll actually want to keep the user in the flow so they have another chance to give a valid answer before pulling them out of the context.
There are 4 methods to keep the user in the context when their message can’t trigger the Any Text that should be a specific type:
- Add an Unknown next to the Any Text
- Add intents next to the Any Text
- Add another Any Text next to the Any Text that should be a specific type
- Combine multiple methods
Which method works best depends on your use case.
1. Add an Unknown next to the Any Text
To do this, simply add an Unknown next to the Any Text, add an error message that clearly instructs the user what to do, and then loop back:
You could also add another event below the question and loop to that place instead of the beginning of the flow, so the “What’s your email address?” won’t be sent again every time:
When creating your flows this way, you’ll probably also want to give the user an escape route. Otherwise the users can get in a never-ending loop when they can’t or don’t want to give the asked information.
Read the next chapter for more information about creating escape routes for your user.
2. Add intents next to the Any Text
You can also add intents next to your Any Text. These intents will only work if the Any Text can’t get triggered.
As you can see, we combined the second method with the first one in the screenshot above.
3. Add another Any Text next to the Any Text that should be a specific type
Instead of an Unknown, you could also add another Any Text. This Any Text should be set to Should Be Any Text, so it will be able to match any type of text.
There are some key differences between this method and the method where you use the Unknown.
If you are using the Unknown, you might encounter a problem that users will activate this Unknown instead of the general Unknown when they are in another flow due to the context.
To prevent that from happening, you can reset the context just before you trigger the next flow. If you do that, make sure to trigger the next event immediately after the Reset or it won’t work.
Now when we test again, you can see the generic Unknown gets triggered this time instead:
So for most situations you might want to use the Any Text instead of the Unknown to avoid these context issues with the Unknown. The only time you can better use the Unknown, is for some edge cases where you actually need to use context for Unknowns this way.
You can also implement a method that makes sure the bot will only ask again for X number of times. When for example the user types an invalid email address two times in a row, we can assume they can’t or don’t want to give it. We’ll explain how to do that in the next chapter.
Escape routes for Fallback
When a user can’t or doesn’t want to give the answer to one of your bot questions, like the question for their email address, you don’t want to make them stay in that loop forever, because it’s very bad for the customer experience:
That’s why it’s vital to implement good escape routes in your chatbot design. There are multiple different ways to set this up:
- Escape Intents
- Escape Buttons
- Fixed number of Fallbacks
- Combine multiple methods
1. Escape Intents
We already showed some examples of these intents earlier during this course. Simply place intents next to your Any Text for messages like:
- I don’t want to say
- I don’t have one
- Stop asking me
If some of these intents are very similar, you might want to combine them.
If you display this option in your Text Reply where you ask the question, it’s even easier for the user to understand they don’t actually need to give an answer if they don’t want to.
The downside of doing this is that you are less likely to receive the answer. Therefore you might want to move this message to the Fallback part of this flow:
2. Escape Buttons
This method is pretty similar to the previous one. The difference is that you use a button instead of text.
However, if you do this when using an Unknown, you might think the button should just trigger the next event, so in this case the “Ask Phone Number” event, but note that this won’t reset the context.
Therefore you can still encounter the potential context problems with the Unknown we discussed earlier. That’s why we recommend letting the button send text that matches an intent that continues the conversation (“I don’t want to” will match the ‘Don’t want to say” intent in this example):
3. Fixed number of Fallbacks
You can also let the bot just continue the conversation after X amount of tries to get the information.
You could add the Any Text, intents and another Fallback Any Text underneath the first Fallback Any Text (or Unknown), but that wouldn’t be really efficient, especially if you want to ask it some more times. It would also make the flow look confusing if you do it this way.
Instead, we can use a combination of tags and conditions. In this example, we will ask for the email address 3 times in total.
Add 3 conditions underneath your Unknown. Give the user the tag “fallback1” under condition C and “fallback2” under condition B.
Condition A will first check if the user has both the tags, and if so, it will continue the conversation to the next event.
Condition B will be triggered when the user has been through condition C already and has the “fallback1” tag.
Condition C will be the Else condition, so it will be triggered when the user can’t enter condition A or B. This will make sure the user will first enter condition C.
Wouldn’t it make more sense to let the user first go into condition A, then B and then C? Yes, but there is actually a good reason we made it the other way around.
If for some reason, like a new unforeseen bug, your conditions start malfunctioning, the bot will always route the conversation to condition A. Therefore it’s best practice when working with conditions to place the least-problematic replies under condition A.
If we did implement the flow above the other way around, and the bot would route everything to condition A the whole time, the user would be stuck. It’s just a precaution to ensure the bot will keep working if something happens to the conditions.
Also note that it’s possible to give different fallback messages this way, like we did in the example above.
Now let’s test it to give you an example of how this flow will look:
The reason we tested this in the Web Widget instead of Try it Out, is that tags are not supported in the Try it Out, so this solution wouldn’t work in the testing preview. It will work on all other channels, so no worries.
Now we have explained all the aspects you’d need to take into consideration when using an Any Text, you might already get a bit of understanding why we recommend only instructing an Any Text that it should be a specific type if it’s important to know that information.
If the chatbot shouldn’t do anything specific with the answer, so the bot response after the Any Text will always be the same no matter what the user said, it might be better to just let any type of text match the Any Text, for the sake of simplicity.
Capturing user input in a single or multiple Any Texts
Let’s say you want to know more information about the phone of the user because they said your app isn’t working correctly on their phone. You might need to know the following:
- iOS or Android
- iOS/Android version
- Phone Model
There are 2 ways to ask for this information by using Any Text Triggers:
- Ask all questions at once and save them in a single Any Text
- Ask the questions one by one and save them in separate Any Texts
You could also use entities for gathering information, but the purpose of this example is to illustrate the 2 different options with the Any Text Trigger.
1. Ask all questions at once and save them in a single Any Text
This would be the easiest and fastest method, both for the chatbot development as for the user. You tell the user all the information you need and save everything in a single Any Text.
The problem here is that some people might respond in separate text messages, but the Any Text will already trigger at the first message.
To prevent this, you can make it more clear to the user that they should respond in a single message.
If the user still responds in multiple messages, it’s also not really a problem if the handover has been activated. The bot just doesn’t respond anymore and the agent will still see the messages.
However, the bot won’t activate a handover after the Any Text in all situations, so that’s something to take into consideration. If the user would then respond with multiple messages, the bot might trigger the Unknown if it doesn’t understand it.
2. Ask the questions one by one and save them in separate Any Texts
The solution to the possibility of some people still sending their answers in separate messages is to ask the questions one by one and save everything in different Any Texts:
This option would also allow you to add checks for the user input by instructing the Any Texts that they should be a specific entity, and you can use these entities to route the user to different branches depending on their answer.
We recommend using the first method if you only want to make it as easy as possible for the user, and use the second option if the chatbot needs the information to route the conversation.
Intents with or without context
Intents that should be accessible at any time during a conversation should be on the top of a separate flow, so without context.
Intents that should only be accessible within a specific context should never be on the top of a flow. They must always be in the context. Some examples of these types of intents are:
- That worked
- That didn’t work
A general Unknown or separate Unknowns within context
Unless you have a very specific edge case, you’ll always need a general Unknown in your chatbot, so the bot always has this fallback option.
Separate unknowns within the context can be useful for situations where you want the user to stay in the context, like we explained earlier.
Multiple intents or a single intent with entities
Your users can ask a lot of different questions that are very similar.
If the user would like to know if it’s possible to pay with Visa, the question would be very similar to if the user is asking if it’s possible to pay with Mastercard for example.
Since these kinds of questions are so alike, the NLP of the bot might get confused. This can result in the bot responding with the Visa information when someone actually asked about the Mastercard payment methods.
It’s also very inefficient to create and train a separate intent for every similar question.
The basic rule of thumb here is: If only one or more specific keywords are differentiating the intents, don’t create separate intents but use a single intent with entities for the keywords.
This can be useful for payment methods, products, dealers, stores, times, dates, cities, countries, persons, songs, social media channels, and so on. Basically everything that you can put in a list.
It’s very easy to set up. First create an entity list for all the options:
Next, add the entities to the training examples of your intent. You don’t need to add the same question for every entity type, like “Can I pay with Visa?”, “Can I pay with cash?” and “Can I pay with Mastercard?”.
However, to ensure it works really well, it’s important to add loads of training examples. The screenshot below shows just a few examples to give you an idea of how the intent can be trained. In real projects you’d probably want to add way more examples. Also make sure to use different examples of the entity.
You can then use conditions to branch the flow if some values of the entity require a different bot response:
A good handover procedure is also a key part of the strategy of the chatbot. For more information, read Chatbot Handoff - How to nail the bot to human handoff.
Chatbot launching procedures
It’s also very helpful to include a solid launching procedure into your chatbot strategy: 4 Steps to Launch Your Bot Successfully.
Chatbot Development Cheat Sheet
You can use this summary of our strategic chatbot recommendations as a useful reference when working on your bot projects, like a chatbot development cheat sheet.
Always first check the channel limitations for the channels you want to create a chatbot for.
Always make a backup of your project by exporting it before you make big changes or remove channels.
- Use Textual Selection Lists if your channels don’t support Rich UI.
- When creating an omnichannel bot for some channels that do support Rich UI and some that don’t, use Textual Selection Lists if you want the highest efficiency and consistency, but you can also make channel-specific changes if preferred.
- Use carousels on Messenger/Web if you want a more visual appealing interface.
- If your channels support Rich UI, but you have a lot of options, you might still want to use Textual Selection Lists so the user doesn’t have to navigate that much and has a better overview.
- For Messenger/Web, only use Quick Replies for simple responses like “Yes”, “No”, “Main Menu” and “Talk to Agent”.
Textual Selection Lists:
- Only include the numbers in the intents if they are only accessible within the context.
- Only add the intents in the context if they shouldn’t be possible to match outside of the context.
- Configure the Any Text that it should be a number or the entity.
- If you use entities, also include the numbers in the entities.
- Add another Any Text, that should be any type of text.
- Add another Else condition to your Textual Selection List flow.
- Reset the value of the parameter before giving it a (new) value.
- Loop the user back after an invalid response.
- Only combine Textual Selection Lists with Quick Replies on Twitter since it looks weird on other channels.
- Only ask for the name of the user if an agent really needs it.
- Don’t let the bot use a name in its replies.
Asking information with an Any Text that should be a specific type:
- Keep the Any Text on should be any text if the answer is not really necessary to use in the bot conversation.
- Add another Any Text as a fallback if you really need a valid answer.
- Only use an Unknown instead if you need an edge case solution for Unknown context.
- Provide escape routes with intents, buttons, and/or a fixed number of fallbacks.
Working with conditions:
- Place the least problematic option underneath Condition A as a precaution.
- Always include an Else Condition as a precaution as well.
- If you are combining conditions with tags, don’t test it in Try it Out since it doesn’t support tags.
Capturing user input in a single or multiple Any Texts:
- Use a single Any Text if only an agent will use the information.
- Use multiple Any Texts when the chatbot needs to route or give dynamic responses with specific required information.
- Use multiple Any Texts if the bot won’t trigger a handover after getting a response from the user.
Intents that should always be accessible should be placed on the top of a separate flow without context, and intents that should only make sense in a specific context, like “Yes” or “No”, should always be placed within the context.
Always use a generic Unknown flow, unless you have a really specific edge case.
Use a single intent with entities if the questions are very similar and the only difference is a specific keyword.