Deactivate a form based on a slot value via story or rule

Hi everyone,

I would like to discuss with you a situation that one or the other may have run into. I am using Rasa 2.2.

Imagine a simple Form that wants to collect the following slots from a certain user:

privacy_agreement:
    type: categorical
    influence_conversation: true
    values:
    - true
    - false
question_1:
    type: any
    influence_conversation: false
question_2:
    type: any
    influence_conversation: false
question_3:
    type: any
    influence_conversation: false

I am aware of the fact, that the following problematic situation can be easily solved via Custom Actions but that’s exactly what I want to avoid. My goal is to achieve the same result by using only either Rules and/or Stories in combination with a Form (because depending on the number of slots, writing “simple” stories would be a problem).

Basically you would start and end a form based on Rules e.g. like this:

- rule: Activate survey form
  steps:
    - intent: trigger_survey_form
    - action: survey_form
    - active_loop: survey_form

- rule: Deactivate survey_form
  condition:
    - active_loop: survey_form
  steps:
    - action: survey_form
    - slot_was_set:
        - requested_slot: null
    - active_loop: null
    - action: utter_submit_success

According to the documentation you can handle unhappy form paths like this:

- 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

My own desired result is a form, that deactivates itsself after the value of the slot privacy_agreement has been set to false. The difference to the above mentioned example is that the prediction needs to be based on the actual slot_value and not based on an intent.

First I tried to implement the following rules:

- rule: Deactivate survey_form because user doesn't agree to privacy agreement
  condition:
    - slot_was_set:
        - privacy_agreement: false
  steps:
    - active_loop: null
    - slot_was_set:
      - requested_slot: null
    - action: utter_no_privacy_agreement
- rule: Deactivate survey_form because user doesn't agree to privacy agreement
  condition:
    - active_loop: survey_form
  steps:
    - intent: deny
    - slot_was_set:
        - privacy_agreement: false
    - active_loop: null
    - slot_was_set:
        - requested_slot: null
    - action: utter_no_privacy_agreement

which actually did not work. I stumbled upon this documentation which seems to explain the problem.

Then I tried to follow the Handling unhappy stories in forms path like explained above (I actually implemented various permutations of the same idea here, picking only two such that you get the point):

- story: User interrupts the form and doesn't want to continue
  steps:
  - intent: trigger_survey_form
  - action: survey_form
  - active_loop: survey_form
  - intent: deny
  - slot_was_set:
      - privacy_agreement: false
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_no_participation
- story: User interrupts the form and doesn't want to continue
  steps:
  - intent: trigger_survey_form
  - action: survey_form
  - active_loop: survey_form
  - intent: deny
  - slot_was_set:
      - privacy_agreement: false
  - slot_was_set:
      - requested_slot: question_1
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_no_participation

which is again simply ignored. I also checked, that there is no rule that interferes with this storyline.

The verbose output of the bot is:

2021-01-05 19:38:43 DEBUG    rasa.core.policies.rule_policy  - Predicted loop 'survey_form'.
2021-01-05 19:38:43 DEBUG    rasa.core.policies.ensemble  - Made prediction using user intent.
2021-01-05 19:38:43 DEBUG    rasa.core.policies.ensemble  - Added `DefinePrevUserUtteredFeaturization(False)` event.
2021-01-05 19:38:43 DEBUG    rasa.core.policies.ensemble  - Predicted next action using policy_2_RulePolicy.
2021-01-05 19:38:43 DEBUG    rasa.core.processor  - Predicted next action 'survey_form' with confidence 1.00.
2021-01-05 19:38:43 DEBUG    rasa.core.actions.forms  - Validating user input 'UserUttered(text: Nein, intent: deny, use_text_for_featurization: False)'.
2021-01-05 19:38:43 DEBUG    rasa.core.actions.forms  - Trying to extract requested slot 'privacy_agreement' ...
2021-01-05 19:38:43 DEBUG    rasa.core.actions.forms  - Got mapping '{'intent': 'confirm', 'type': 'from_intent', 'value': True}'
2021-01-05 19:38:43 DEBUG    rasa.core.actions.forms  - Got mapping '{'intent': 'deny', 'type': 'from_intent', 'value': False}'
2021-01-05 19:38:43 DEBUG    rasa.core.actions.forms  - Successfully extracted 'False' for requested slot 'privacy_agreement'
2021-01-05 19:38:43 DEBUG    rasa.core.actions.forms  - Validating extracted slots: {'privacy_agreement': False}
2021-01-05 19:38:43 DEBUG    rasa.core.actions.forms  - Request next slot 'question_1'
2021-01-05 19:38:43 DEBUG    rasa.core.processor  - Current slot values:
        privacy_agreement: False
        question_1: None
        question_2: None
        question_3: None
        requested_slot: question_1

So actually everything works as expected but even after recognizing that privacy_agreement has been set to False and the requested_slot has been set to question_1 the form doesn’t get deactivated.

Any ideas on this? I am asking because I think this would be a great benefit for e.g. Rasa X because it allows / offers more non programmatical control. Or I am simply missing something here. :thinking:

Kind regards
Julian

Hi there,

for everyone who stumbles upon this issue and needs to solve it, no matter if there is a more elegant way, here is a solution shown as a brief example on how the achieve the key objective:

Content of domain.yml

slots:
  cancel_reason:
    type: any
    influence_conversation: false
  privacy_agreement:
    type: bool
    influence_conversation: true

forms:
  privacy_form:
    privacy_agreement:
      - intent: confirm
        type: from_intent
        value: true
      - intent: deny
        type: from_intent
        value: false
  cancel_reason_form:
    cancel_reason:
      - type: from_text

Content of rules.yml

- rule: Activate privacy_form
  steps:
    - intent: trigger_form_cycle
    - action: privacy_form
    - slot_was_set:
        - privacy_agreement: null
    - active_loop: privacy_form

Now I simply split my desired storyline into several forms. Actually I want to use forms because I can’t set slots inside stories and I want to make use of the intent-based auto-filling option that forms provide - meaning a more fine-grained control.

- rule: Deactivate privacy_form and end with utterance
  condition:
    - active_loop: privacy_form
  steps:
    - action: privacy_form
    - slot_was_set:
        - requested_slot: null
    - active_loop: null
    - slot_was_set:
        - privacy_agreement: false
    - action: utter_no_privacy_agreement

Actually I wrote a rule that checks the slot and it’s value (not just if the slot has been set) after the form has ended. If the value was set to false I simply utter something (or maybe you want to do a custom action as a follow up) and if the value was set to true, I continue with the survey form.

- rule: Deactivate privacy_form  move to cancel_reason_form
  condition:
    - active_loop: privacy_form
  steps:
    - action: privacy_form
    - slot_was_set:
        - requested_slot: null
    - active_loop: null
    - slot_was_set:
        - privacy_agreement: true
    - action: cancel_reason_form
    - active_loop: cancel_reason_form

Of course you need to write a rule that processes the end of that form, but I think you got the point. The workaround here is to simply define several forms instead of one larger. I think one could argue if that’s a good solution or not, but since I wanted to avoid working with ActionExecutionRejection (which possibly could have solved my problem) I think it works.

However… I still think that it would be a good idea to think about making rules applicable while there is an active loop. I am aware of the fact, that possibly this has to be considered while working with a FormAction or CustomActions to make sure everything fits together.

Kind regards
Julian

what do you mean? Rules takes precedence over automatic prediction of the form? We explicitly didn’t want it.

Please keep in mind that these 2 rules are exactly the same:

- rule: Activate privacy_form
  steps:
    - intent: trigger_form_cycle
    - action: privacy_form
    - slot_was_set:
        - privacy_agreement: null
    - active_loop: privacy_form

- rule: Activate privacy_form
  steps:
    - intent: trigger_form_cycle
    - action: privacy_form
    - active_loop: privacy_form
    - slot_was_set:
        - privacy_agreement: null

I think having several forms is a valid method as an alternative to writing custom validate that would deactivate the form depending on a slot value

please, i have the same problem, how can i deactivate a form based on a slot value via story, i want to stop the form via a slot then i want to activate it