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
# 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
For our use case, we will use a form validation action.
Form validation action is a class that extends a
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 firstname.lastname@example.org:petr7555/rasa-dev-tutorial.git