Forms

Forms

Note

There is an in-depth tutorial here about how to use Rasa Forms for slot filling.

One of the most common conversation patterns is to collect pieces of information from a user to perform actions such as booking a restaurant, calling an API, or searching a database. This process is known as slot filling.

When collecting multiple pieces of information sequentially, it is advisable to create a FormAction. This action incorporates the logic to iterate over the required slots and request information from the user. A full example of using forms can be found in the examples/formbot directory of Rasa Core.

Defining a Form

When defining a form, it is necessary to add it to your domain file. For example, if your form is named restaurant_form, your domain would look like this:

forms:
  - restaurant_form
actions:
  ...

To utilize forms, FormPolicy needs to be included in your policy configuration file:

policies:
  - name: "FormPolicy"

Form Basics

Using a FormAction, all happy paths can be described in a single story. A happy path occurs when the user provides requested information correctly.

Example for a restaurant bot:

## happy path
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
    - form{"name": null}

The FormAction will only request slots that haven’t been filled. If a user begins the conversation with specific requirements, they won't be asked for information they've already provided.

Note: For the above story to function, slots should be unfeaturized.

Custom Slot Mappings

If no slot mappings are defined, slots will only be filled by entities with matching names extracted from the user input. However, FormAction can support yes/no questions and free-text responses. The slot_mappings method defines how to extract slot values from user responses.

def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
    return {
        "cuisine": self.from_entity(entity="cuisine", not_intent="chitchat"),
        "num_people": [
            self.from_entity(entity="num_people", intent=["inform", "request_restaurant"]),
            self.from_entity(entity="number"),
        ],
        "outdoor_seating": [
            self.from_entity(entity="seating"),
            self.from_intent(intent="affirm", value=True),
            self.from_intent(intent="deny", value=False),
        ],
        "preferences": [
            self.from_intent(intent="deny", value="no additional preferences"),
            self.from_text(not_intent="affirm"),
        ],
        "feedback": [self.from_entity(entity="feedback"), self.from_text()],
    }

Validating User Input

After extracting a slot value, the form will try to validate it. Default validation checks if the slot was successfully extracted. Custom validation functions can be created with the name validate_{slot-name} for specific checks.

Example of validating a cuisine slot:

@staticmethod
def cuisine_db() -> List[Text]:
    return [
        "caribbean",
        "chinese",
        "french",
        "greek",
        "indian",
        "italian",
        "mexican",
    ]

def validate_cuisine(self, value: Text, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> Dict[Text, Any]:
    if value.lower() in self.cuisine_db():
        return {"cuisine": value}
    else:
        dispatcher.utter_message(template="utter_wrong_cuisine")
        return {"cuisine": None}

Handling Unhappy Paths

Users may not always provide the requested information. Common scenarios include asking questions, changing their minds, or making chitchat. It’s important to handle these potentially disruptive interactions in your stories. For instance:

## chitchat
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
* chitchat
    - utter_chitchat
    - restaurant_form
    - form{"name": null}

Debugging

To debug your bot, run it with the debug flag:

If you are new to Rasa, test your bot as soon as possible, as real user behavior will often differ from designed sequences.