What if we wanted to have a question in our form that should be asked only if the answer to the previous question was "yes"?
This is where custom actions come in handy.
A custom action can run any code you want, including API calls, database queries etc. They can turn on the lights, add an event to a calendar, check a user's bank balance, or anything else you can imagine.
You can learn more about custom actions in the documentation.
We will add two questions to our existing newsletter subscription form. First one asking if the user is OK with telling us their age and second one asking the age. Naturally, we want to ask the second question only if the user is fine with it.
To store the information from the user, we need to have the corresponding slots.
# domain.yml
# ... previous content ...
slots:
# ... previous slots ...
can_ask_age:
type: bool
influence_conversation: false
age:
type: text
influence_conversation: false
We also need to add the slots to the form, so that the corresponding questions are asked during the form loop.
# domain.yml
# ... previous content ...
forms:
newsletter_form:
# ... previous slots ...
can_ask_age:
- type: from_intent
intent: affirm
value: true
- type: from_intent
intent: deny
value: false
age:
- type: from_entity
entity: age
can_ask_age is a bool slot. If the intent is affirm, the value assigned to that slot will be true. If the intent is deny, the value assigned to that slot will be false.
You can learn more about boolean slots in the documentation.
Notice that we can use any number of type keys telling how the slot should be filled. The first one from the top that is matched is used.
Now let's add the questions. Remember, the name must be either utter_ask_<form_name>_<slot_name>
or utter_ask_<slot_name>
.
# domain.yml
# ... previous content ...
responses:
# ... previous responses ...
utter_ask_can_ask_age:
- text: Can I ask what your age is?
utter_ask_age:
- text: What is your age?
Right now, the "What is your age?" question is always asked. This is because all slots that are part of a form are mandatory and have to be filled before the form ends. Luckily for us, there is an easy way to tell the chatbot what the mandatory slots are based on what we already know – like that the user does not want to share their age.
This is accomplished through custom actions. Custom actions live in actions/actions.py
file.
For our use case, we will use a form validation action.
Form validation action is a class that extends a FormValidationAction
class.
It has to implement a name
method, which returns the name of that action. If we name it validate_<form_name>
, it will be run automatically after each step of the form.
You can learn more about validation actions in the documentation.
We then override the required_slots
method to tell what slots are mandatory and have to be filled.
You can learn more about this dynamic behaviour in the documentation.
# actions/actions.py
class ValidateNewsletterForm(FormValidationAction):
def name(self) -> Text:
return "validate_newsletter_form"
async def required_slots(
self,
slots_mapped_in_domain: List[Text],
dispatcher: "CollectingDispatcher",
tracker: "Tracker",
domain: "DomainDict",
) -> Optional[List[Text]]:
if not tracker.get_slot("can_ask_age"):
slots_mapped_in_domain.remove("age")
return slots_mapped_in_domain
slots_mapped_in_domain
parameter is a list of names of slots in our form. We remove the slot age from required slots if the value of can_ask_age slot is not True.
Any custom action that we want to use in our stories must be added into the actions section of our domain.
# domain.yml
# ... previous content ...
actions:
- validate_newsletter_form
Lastly, uncomment the following code in endpoints.yml
. This tells Rasa where to look for custom actions.
# endpoints.yml
action_endpoint:
url: "http://localhost:5055/webhook"
Because we are using custom actions, we have to run an actions server. Run rasa run actions
in a new terminal window.
You can learn more about this command in the documentation.
In another terminal, run rasa train && rasa shell
.
We can see that when a user answers "no", the age is not asked, and the value is None.
You can learn more about the action server in the documentation.
In the next chapter, we will look at custom submit action.
Repository for this tutorial:
You can checkout the state of the repository at the end of this tutorial by running:
git clone --branch 17-custom-forms git@github.com:petr7555/rasa-dev-tutorial.git
Top comments (0)