---
id: forms
sidebar_label: Forms
title: Forms
description: Follow a rule-based process of information gathering using forms in open source bot framework Rasa.
abstract: One of the most common conversation patterns is to collect a few pieces of information from a user in order to do something (book a restaurant, call an API, search a database, etc.). This is also called **slot filling**.
---

## Usage

To use forms with Rasa you need to make sure that the
[Rule Policy](./policies.mdx#rule-policy) is added to your policy configuration.
For example:

```yaml-rasa
policies:
- name: RulePolicy
```

### Defining a Form

Define a form by adding it to the `forms` section in your [domain](./domain.mdx).
The name of the form is also the name of the action which you can use in
[stories](./stories.mdx) or [rules](./rules.mdx) to handle form executions.
You will need to specify a list of slot names to the mandatory `required_slots` key.

The following example form `restaurant_form` will fill the slot
`cuisine` and slot `num_people`.

```yaml-rasa
entities:
- cuisine
- number
slots:
  cuisine:
    type: text
    mappings:
    - type: from_entity
      entity: cuisine
  num_people:
    type: any
    mappings:
    - type: from_entity
      entity: number
forms:
  restaurant_form:
    required_slots:
        - cuisine
        - num_people
```

You can define a list of intents to ignore for the whole form under the 
`ignored_intents` key. Intents listed under `ignored_intents` will be added to the
`not_intent` key of each slot mapping.

For example, if you do not want any of the required slots of a form to be filled when
the intent is `chitchat`, then you would need to define the following (after the form
name and under the `ignored_intents` keyword):

```yaml-rasa
entities:
- cuisine
- number
slots:
  cuisine:
    type: text
    mappings:
    - type: from_entity
      entity: cuisine
  num_people:
    type: any
    mappings:
    - type: from_entity
      entity: number
forms:
  restaurant_form:
    ignored_intents: 
    - chitchat
    required_slots:
        - cuisine
        - num_people
```

Once the form action gets called for the first time, the form gets activated and will
prompt the user for the next required slot value. It does this by
looking for a [response](./responses.mdx) called
`utter_ask_<form_name>_<slot_name>` or `utter_ask_<slot_name>` if the former isn't
found. Make sure to define these responses in your domain file for
each required slot.

### Activating a Form

To activate a form you need to add a [story](./stories.mdx) or [rule](./rules.mdx),
which describes when the assistant should run the form. In the case a specific intent
triggering a form, you can for example use the following rule:

```yaml-rasa
rules:
- rule: Activate form
  steps:
  - intent: request_restaurant
  - action: restaurant_form
  - active_loop: restaurant_form
```

:::note
The `active_loop: restaurant_form` step indicates that the form should be activated after
`restaurant_form` was run.
:::

### Deactivating a Form

A form will automatically deactivate itself once all required slots are filled.
You can describe your assistant's behavior for the end of a form with a rule or a story.
If you don't add an applicable story or rule, the assistant will automatically listen
for the next user message after the form is finished.
The following example runs the utterances `utter_submit` and `utter_slots_values` as soon as the form
`your_form` filled all required slots.

```yaml-rasa
rules:
- rule: Submit form
  condition:
  # Condition that form is active.
  - active_loop: restaurant_form
  steps:
  # Form is deactivated
  - action: restaurant_form
  - active_loop: null
  - slot_was_set:
    - requested_slot: null
  # The actions we want to run when the form is submitted.
  - action: utter_submit
  - action: utter_slots_values
```

Users might want to break out of a form early. Please see
[Writing Stories / Rules for Unhappy Form Paths](./forms.mdx#writing-stories--rules-for-unhappy-form-paths) on how to
write stories or rules for this case.

### Slot Mappings

:::caution Changed in 3.0
As of 3.0, [slot mappings](./domain.mdx#slot-mappings) are defined in the `slots` section of the domain.
This change allows the same slot mapping to be reused across multiple forms, removing any unnecessary duplication.
Please follow the [migration guide](./migration-guide.mdx#slot-mappings) to update your assistant.

Note specifically the role of [Mapping Conditions](./domain.mdx#mapping-conditions) and the
[unique entity mapping](./domain.mdx#unique-from_entity-mapping-matching) constraint.
:::

### Writing Stories / Rules for Unhappy Form Paths

Your users will not always respond with the information you ask of them.
Typically, users will ask questions, make chitchat, change their mind, or otherwise
stray from the happy path. 

While a form is active, if a user's input does not fill the requested slot, the execution of 
the form action will be rejected i.e. the form will automatically raise an `ActionExecutionRejection`. 
These are the specific scenarios in which a form will raise an `ActionExecutionRejection`:

* a slot was requested, but the user didn't fill the slot with their last message and
  you didn't define a custom action for
  [validating slots](forms.mdx#validating-form-input) or
  [extracting slots](forms.mdx#custom-slot-mappings).
* a slot was requested, but your custom action for
  [validating slots](forms.mdx#validating-form-input) or
  [extracting slots](forms.mdx#custom-slot-mappings) didn't return any `SlotSet` events.

To intentionally reject the form execution, you can also return an `ActionExecutionRejected` event as part of your
custom validations or slot mappings.

To handle situations that might cause a form's execution to be rejected, you can write rules
or stories that include the expected interruptions. For example, if you expect your users to chitchat with your bot,
you could add a rule to handle this:

```yaml-rasa
rules:
- rule: Example of an unhappy path
  condition:
  # Condition that form is active.
  - active_loop: restaurant_form
  steps:
  # This unhappy path handles the case of an intent `chitchat`.
  - intent: chitchat
  - action: utter_chitchat
  # Return to form after handling the `chitchat` intent
  - action: restaurant_form
  - active_loop: restaurant_form
```

In some situations, users may change their mind in the middle of the form action
and decide not to go forward with their initial request. In cases like this, the
assistant should stop asking for the requested slots.

You can handle such situations
gracefully using a default action `action_deactivate_loop` which will deactivate
the form and reset the requested slot. An example story of such conversation could
look as follows:

```yaml-rasa
stories:
- story: User interrupts the form and doesn't want to continue
  steps:
  - intent: request_restaurant
  - action: restaurant_form
  - active_loop: restaurant_form
  - intent: stop
  - action: utter_ask_continue
  - intent: stop
  - action: action_deactivate_loop
  - active_loop: null
```

It is **strongly** recommended that you build these rules or stories using
[interactive learning](./writing-stories.mdx#using-interactive-learning).
If you write these rules / stories by hand you will likely miss important
things.

## Advanced Usage

Forms are fully customizable using [Custom Actions](./actions.mdx#custom-actions).

### Validating Form Input

After extracting a slot value from user input, you can validate the extracted slots.
By default Rasa only validates if any slot was filled after requesting
a slot.

You can implement a [Custom Action](./actions.mdx#custom-actions) `validate_<form_name>`
to validate any extracted slots. Make sure to add this action to the `actions`
section of your domain:

```yaml-rasa
actions:
- validate_restaurant_form
```

When the form is executed it will run your custom action after every user turn to validate the latest filled slots.

This custom action can extend `FormValidationAction` class to simplify
the process of validating extracted slots. In this case, you need to write functions
named `validate_<slot_name>` for every extracted slot.

The following example shows the implementation of a custom action
which validates that the slot named `cuisine` is valid.

```python
from typing import Text, List, Any, Dict

from rasa_sdk import Tracker, FormValidationAction
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.types import DomainDict


class ValidateRestaurantForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_restaurant_form"

    @staticmethod
    def cuisine_db() -> List[Text]:
        """Database of supported cuisines"""

        return ["caribbean", "chinese", "french"]

    def validate_cuisine(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate cuisine value."""

        if slot_value.lower() in self.cuisine_db():
            # validation succeeded, set the value of the "cuisine" slot to value
            return {"cuisine": slot_value}
        else:
            # validation failed, set this slot to None so that the
            # user will be asked for the slot again
            return {"cuisine": None}
```

You can also extend the `Action` class and retrieve extracted slots with `tracker.slots_to_validate`
to fully customize the validation process.

### Custom Slot Mappings

:::caution Changed in 3.0
The `slots_mapped_in_domain` argument provided to the `required_slots` method of `FormValidationAction`
has been replaced by the `domain_slots` argument, please update your custom actions to the new argument name.
:::

If none of the predefined [Slot Mappings](./domain.mdx#slot-mappings) fit your use
case, you can use the
[Custom Action](./actions.mdx#custom-actions) `validate_<form_name>` to write your own
extraction code. Rasa will trigger this action when the form is run.

If you're using the Rasa SDK we recommend you to extend the provided
`FormValidationAction`. When using the `FormValidationAction`, three steps are required
to extract customs slots:

1. Define a method `extract_<slot_name>` for every slot that should be mapped in a custom way.
   Each slot which has been defined in the `domain.yml` file with a custom mapping **must** have its own independent
   implementation of an `extract_<slot_name>` method.
2. In your domain file, for your form's `required_slots`, list all required slots, with both predefined and custom mappings.

In addition, you can override the `required_slots` method to add dynamically requested slots: you can read more in the
[Dynamic Form Behavior](./forms.mdx#dynamic-form-behavior) section.

:::note
If you have added a slot with a custom mapping in the `slots` section of the domain file which you only
want to be validated within the context of a form by a custom action extending `FormValidationAction`,
please make sure that this slot has a mapping of type `custom` and that the slot name is included in the
form's `required_slots`.
:::

The following example shows the implementation of a form which extracts a slot
`outdoor_seating` in a custom way, in addition to the slots which use predefined mappings.
The method `extract_outdoor_seating` sets the slot `outdoor_seating` based on whether
the keyword `outdoor` was present in the last user message.

```python
from typing import Dict, Text, List, Optional, Any

from rasa_sdk import Tracker
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.forms import FormValidationAction


class ValidateRestaurantForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_restaurant_form"

    async def extract_outdoor_seating(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
    ) -> Dict[Text, Any]:
        text_of_last_user_message = tracker.latest_message.get("text")
        sit_outside = "outdoor" in text_of_last_user_message

        return {"outdoor_seating": sit_outside}
```

By default the `FormValidationAction`
will automatically set the [`requested_slot`](forms.mdx#the-requested_slot-slot) to the
first slot specified in `required_slots` which is not filled.

### Dynamic Form Behavior

By default, Rasa will ask for the next empty slot from the slots
listed for your form in the domain file. If you use
[custom slot mappings](forms.mdx#custom-slot-mappings) and the `FormValidationAction`,
it will ask for the first empty slot returned by the `required_slots` method. If all
slots in `required_slots` are filled the form will be deactivated.

You can update the required slots of your form dynamically.
This is, for example, useful when you need to fill additional slots based on how
a previous slot was filled or when you want to change the order in which slots are requested.

If you are using the Rasa SDK, we strongly recommend that you use the `FormValidationAction` and
override `required_slots` to fit your dynamic behavior. You must implement
a method `extract_<slot name>` for every slot which doesn't use a predefined mapping,
as described in [Custom Slot Mappings](forms.mdx#custom-slot-mappings).
The example below will ask the user if they want to sit in
the shade or in the sun in case they said they want to sit outside.

```python
from typing import Text, List, Optional

from rasa_sdk.forms import FormValidationAction

class ValidateRestaurantForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_restaurant_form"

    async def required_slots(
        self,
        domain_slots: List[Text],
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: "DomainDict",
    ) -> List[Text]:
        additional_slots = ["outdoor_seating"]
        if tracker.slots.get("outdoor_seating") is True:
            # If the user wants to sit outside, ask
            # if they want to sit in the shade or in the sun.
            additional_slots.append("shade_or_sun")

        return additional_slots + domain_slots
```

If conversely, you want to remove a slot from the form's `required_slots` defined in the domain file under certain conditions,
you should copy the `domain_slots` over to a new variable and apply changes to that new variable instead of directly modifying
`domain_slots`. Directly modifying `domain_slots` can cause unexpected behaviour. For example:

```python
from typing import Text, List, Optional

from rasa_sdk.forms import FormValidationAction

class ValidateBookingForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_booking_form"

    async def required_slots(
        self,
        domain_slots: List[Text],
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: "DomainDict",
    ) -> List[Text]:
        updated_slots = domain_slots.copy()
        if tracker.slots.get("existing_customer") is True:
            # If the user is an existing customer,
            # do not request the `email_address` slot
            updated_slots.remove("email_address")

        return updated_slots
```

### The requested_slot slot

The slot `requested_slot` is automatically added to the domain as a
slot of type [`text`](domain.mdx#text-slot). The value of the `requested_slot` will be
ignored during conversations. If you want to change this behavior, you need to add
the `requested_slot`  to your domain file as a categorical slot with
`influence_conversation` set to `true`.
You might want to do this if you
want to handle your unhappy paths differently, depending on what slot is
currently being asked from the user. For example, if your users respond
to one of the bot's questions with another question, like *why do you need to know that?*
The response to this `explain` intent depends on where we are in the story.
In the restaurant case, your stories would look something like this:

```yaml-rasa
stories:
- story: explain cuisine slot
  steps:
  - intent: request_restaurant
  - action: restaurant_form
  - active_loop: restaurant
  - slot_was_set:
    - requested_slot: cuisine
  - intent: explain
  - action: utter_explain_cuisine
  - action: restaurant_form
  - active_loop: null

- story: explain num_people slot
  steps:
  - intent: request_restaurant
  - action: restaurant_form
  - active_loop: restaurant
  - slot_was_set:
    - requested_slot: cuisine
  - slot_was_set:
    - requested_slot: num_people
  - intent: explain
  - action: utter_explain_num_people
  - action: restaurant_form
  - active_loop: null
```

Again, it is **strongly** recommended that you use
[interactive learning](./writing-stories.mdx#using-interactive-learning) to build these stories.

### Using a Custom Action to Ask For the Next Slot

As soon as the form determines which slot has to be filled next by the user, it will
execute the action `utter_ask_<form_name>_<slot_name>` or `utter_ask_<slot_name>`
to ask the user to provide the necessary information. If a regular utterance is not
enough, you can also use a custom action `action_ask_<form_name>_<slot_name>` or
`action_ask_<slot_name>` to ask for the next slot.

```python
from typing import Dict, Text, List

from rasa_sdk import Tracker
from rasa_sdk.events import EventType
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk import Action


class AskForSlotAction(Action):
    def name(self) -> Text:
        return "action_ask_cuisine"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
    ) -> List[EventType]:
        dispatcher.utter_message(text="What cuisine?")
        return []
```

If there is more than one asking option for the slot, Rasa prioritizes in the following order:

1. `action_ask_<form_name>_<slot_name>`
2. `utter_ask_<form_name>_<slot_name>`
3. `action_ask_<slot_name>`
4. `utter_ask_<slot_name>`

