DEV Community

loading...
Cover image for RASA - Custom forms

RASA - Custom forms

Petr Janik
I'm a student of applied informatics at Masaryk University in Brno. I love coding, especially in React, Java and Python.
・3 min read

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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?
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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.

Chat can ask age

Chat cannot ask age

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
Enter fullscreen mode Exit fullscreen mode

Discussion (0)