Sunday, May 31
Shadow

How to use form action with your Rasa Chat-bot

In this post you will learn how to fill the slots and to use them further using the Forms. Now you must be thing that if we are going to fill the slots only then why are we using the Forms for that. So the reason is we want to build an effective and reliable chatbot with lesser efforts than before. So, what is forms and form action and how it builds an effective and reliable chatbot with less efforts.

What are Forms?

One of the most common conversation patterns is to collect few pieces of information from a user in order to do something (like booking a restaurant, calling an API, searching the database, etc.). This is also called slot filling. If you want to collect multiple pieces of information in a row, we recommended that you to create a FormAction. This FormAction is the single action which have the logic to loop over the required slots and ask the user for the information until all the slots are filled. So, this is the most important and the required logic in the chatbot to save many lines of code and also to save efforts for the same process.

So, how do we do it to with rasa chatbot to make it effective, reliable with less efforts. For the form action we need to make the updation in the project files, which are, config.yml, actions.py, stories.md and domain.yml.

Let’s start one by one and understand the basic functionality of each of them:

Setup Configuration

Firstly, we need to include the FormPolicy in to the configuration file to make the whole process function properly. In this way,

 policies: 
- name: FormPolicy

The FormPolicy is extremely simple and just always predicts the form action. This comes into action when the form action is called for the first time.

Adding the stories

To add the stories as per the forms in the most important step to call the FormAction and to repeatedly ask the user for the information until all the slots are filled. Now lets understand it more briefly with the example problem that we are going to cover in this post. Like we will add the below story or let’s say the happy path,

## happy path
* network_issue
  - form_info
  - form{"name": "form_info"}
  - form{"name": null}
* affirm
  - utter_goodbye

So, above is the happy path for calling the FormAction. In this story “network_issue” is the user intent to which the bot will redirect to the FormAction which is “form_info”. Here, form{“name”: “form_info”}” is used to activate the form and “form{“name”: null}” is used to deactivate the form again. Once the FormAction is activated, the boty can execute any kind of action outside the form. The above “happy path” means that once the FormAction is active it can fo outside of the form and perform any action until all requested slots are filled without interruption. For example, if the user starts the conversation and says “My name is Ashish and I am using One Plus mobile.” Then the slots “NAME” and “BRAND” are automatically filled so, the bot will not ask you the question related to these slots.

To make your story work in such a way your first step is to set your slots as unfeaturized. But if the slots are not of the type unfeaturized or let’s say it’s featurized, then in that case you have to include slot{} events to show these slots being set. Not it seems to be confusing like when to use form and when to use slot, So, to make it easier it’s not compulsory that you have to add the stories manually, you can also add them using the Interactive learning.

So this was all about adding the stories as per the FormAction. Similarly you can add more stories according to the chatbot architecture.

Updating the actions file
In the above story you have seen we have added the happy path as per the form action. Now we will learn what actually is happening when the form action is called. To to this you first need to define three methods:

  • name: the name of this action (form_info in our case)
  • required_slots: a list of slots that need to be filled for the submit method to work.
  • submit: what to do at the end of the form, when all the slots have been filled.

Here, what happens is firstly when the FormAction is called for the first time, then form gets activated and FormPolicy jumps in. The FormPolicy is extremely simple and just always predicts the form action. To identify which form has been called the first method is to be added to the to the actions.py file, i.e.,

def name(self) -> Text:
    """Unique identifier of the form"""
    return "form_info"

When the bot identifes which FormAction is to be called then it moves to the next static method which is requested_slots where all the slots are set in the order to be called when requested and here with respect to these requested slot names an utter_ask_{slot_name} is called from the domain.yml file where all the bot response are set. The static method that is to added to the action file is :

@staticmethod
def required_slots(tracker: Tracker) -> List[Text]:
    """A list of required slots that the form has to fill"""
    return ["NAME", "BRAND"]

Here you can see we are returning the list for the static method and this list specifies the order of the slots to be requested to fill values for each of them. So, it simply means firstly utter_ask_NAME will be called and vice versa.
Once all the slots are filled, submit() method is called where we can use the collected information in whatever way we want use it. For example, asking for booking confirmation with your details. This method will be added in actions.py file,

def submit(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
) -> List[Dict]:
    """Define what the form has to do
        after all required slots are filled"""
    # utter submit template
    dispatcher.utter_message(template="utter_submit", name=tracker.get_slot('NAME'),
                             handset=tracker.get_slot('BRAND'))
    return []

Slot Mapping
If you do not define slot mappings in the actions file, slots will be only filled by the entities with the same name as the slot that are picked up from the user input. Slots can be picked with the single entity but FormAction supports inputs like yes/no questions and free-text input. The slot_mappings method defines how to extract slot values from user responses. Add the below method to your action file to extract the required information from the user response.

def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
    """A dictionary to map required slots to
        - an extracted entity
        - intent: value pairs
        - a whole message
        or a list of them, where a first match will be picked"""
    return {
        "name": self.from_entity(entity="NAME", intent='my_name_is'),
        "headset": self.from_entity(entity="BRAND", intent="headset"),
    }

The predefined functions work as follows:

  • self.from_entity(entity=entity_name, intent=intent_name) will look for an entity called entity_name to fill a slot slot_name regardless of user intent if intent_name is None else only if the users intent is intent_name.
  • self.from_intent(intent=intent_name, value=value) will fill slot slot_name with value if user intent is intent_name. Note: Slot will not be filled with user intent of message triggering the form action. Use self.from_trigger_intent below.
  • self.from_trigger_intent(intent=intent_name, value=value) will fill slot slot_name with value if form was triggered with user intent intent_name.
  • self.from_text(intent=intent_name) will use the next user utterance to fill the text slot slot_name regardless of user intent if intent_name is None else only if user intent is intent_name.
  • If you want to allow a combination of these, provide them as a list as in the example above

Once you understand this just add the above methods inside a class as shown below:

class ActionFormInfo(FormAction):
    def name(self) -> Text:
        """Unique identifier of the form"""
        return "form_info"
    @staticmethod
    def required_slots(tracker: Tracker) -> List[Text]:
        """A list of required slots that the form has to fill"""
        return ["NAME", "BRAND"]
    def submit(
            self,
            dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict[Text, Any],
    ) -> List[Dict]:
        """Define what the form has to do
            after all required slots are filled"""
        # utter submit template
        dispatcher.utter_message(template="utter_submit", name=tracker.get_slot('NAME'),
                                 handset=tracker.get_slot('BRAND'))
        return []
    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
        """A dictionary to map required slots to
            - an extracted entity
            - intent: value pairs
            - a whole message
            or a list of them, where a first match will be picked"""
        return {
            "name": self.from_entity(entity="NAME", intent='my_name_is'),
            "headset": self.from_entity(entity="BRAND", intent="headset"),
        }

Updating Domain file
Now to link the core and nlu to the action file add the following line of code to your existing chatbot in domain.yml file

forms:
- form_info
slots:
  BRAND:
    type: unfeaturized
  NAME:
    type: unfeaturized
  requested_slot:
    type: unfeaturized
templates:
utter_ask_BRAND:
- text: Sir, I would like to tell you that your issues will be resolved. Could I
    please know which handset you are using?
utter_ask_NAME:
- text: Ok. Please provide your fist name?
utter_submit:
- text: Your name is {name} and your handset is {handset}

Execution
When you are done with the above steps and have understood all the things clearly then train your model and test it

$ rasa train
$ rasa x 
or 
$ rasa shell –debug  # to check the backend functionality of the form action.

You will see the output something like this in the debug mode.

After this execution, you will really feel that you have reduced the number of of lines of code and have built the effective and reliable chatbot with different features added to it.
This is it for this post, I hope you have understood all thething very clearly but still if you have any queries related to the topic then feel free to leave a comment below in the comment section.
Stay tuned and Enjoy learning. 🙂

5 Comments

  • Kajenthirakumar

    Sir i try to create a payload button action but i did not get any response and did not show any error can u help me

    here my code
    ——————————–

    class ActionHelloWorld(Action):

    def name(self) -> Text:
    return “action_mysql”

    def run(self, dispatcher: CollectingDispatcher,
    tracker: Tracker,
    domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:

    buttons = [{ “payload”: “Yes”,”title”: “Yes” }, { “payload”: “No” ,”title”: “No”}]
    dispatcher.button_resp(“Do you have any other query” ,buttons)

    return []

  • Patrick

    Hello sir
    great job
    please i have a concern.
    I have two CSV files one for the possible user questions and one for the answers.
    these files contain an incalculable number of data how I can manage that with RASA because I wish to set up a rasa chatbot with its data.

Leave a Reply

Your email address will not be published. Required fields are marked *