Can't understand rules in Rasa 2.0

Hi Folks, I was a Rasa 1.x user for a few months and found it fairly intuitive to use and was up and going in a few days. I recently upgraded to Rasa 2.x and am having a really difficult time. After a week of toiling, I am not making meaningful progress. Take rules for example. There is clearly a subtle relationship between rules and stories and how they work together, yet there are virtually no examples in the documentation about how to use them together (and most examples are trivial snippets). Every example of rules I try doesn’t work, even when I simply copy and paste examples (e.g. the submit form example - this will either not fire or give me cryptic messages about conflicts between stories or other rules).

Here’s a specific example: I am trying to write a rule that will catch an intent from the user that they want to break out of the conversation (a FORM) and talk to a human.

rule: Unhappy path - speak to a human
  condition:
    active_loop: main_form
    slot_was_set:
      speak_to_a_human: True  
  steps:
    active_loop: null
    action: utter_will_redirect_you_to_human

I have tried many different combinations of the above rule and either nothing will fire or I will have a rule contradiction with the outer story. The form is simple and straight forward. When speak_to_a_human slot gets set, I would like this rule to fire (drop out of the form and call the utterance).

Please help - what am I not understanding about rules?

1 Like

Hi @jayb, welcome to the forum!

Also, thanks for your feedback. We have a bot in the examples directory that uses rules only. Maybe this helps?

Concerning your question about rules and forms: note that forms always take precidence and rules get ignored as long as the form is running. If you want to handle the situation where the user wants to stop filling the form and talk to a human, you must have the form reject the evaluation, as is explained here.

Thanks, @j.mosig. I have looked at all of the examples, including the rules-only example. I have studied the documentation carefully looking for a solution so I am familiar with this example. One question is if rules get ignored as long as the form is running, then why does this example condition on the form being active? Very confusing! Can you please explain?

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

As an alternative, I am trying to come at this problem from the story angle. I have managed to get the ‘talk to a human’ scenario to work with this:

- story:  transfer to human
  steps:
  - active_loop: main_form
  - intent: speak_to_human
  - action: utter_speak_to_human
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart

The reason why this works for me is the speak_to_human intent is not used by my form. I have other interrupt scenarios where I need to exit out of the form based up values of slots. For that I have been trying this approach:

- story:  doesn't want to schedule appointment
  steps:
  - active_loop: main_form
  - slot_was_set:
    - schedule_mamogram : False
  - action: utter_will_skip_appointment
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart

Unfortunately, this doesn’t trigger. For the life of me I can’t understand how slot_was_set works, what it is supposed to do, what types of slots do they need to be, etc.

I’d appreciate any help you could offer. In Rasa 1.x, I was putting the logic to exit forms gracefully in the custom Action classes. Rasa 2.x seems to offer a more elegant way of handling this logic, but they don’t work. Respectfully, the documentation would ideally be a lot more in-depth, showing the often subtle interplay between stories, forms, and rules, with real-world examples.

Thanks,

Jay

3 Likes

slots are set by your custom actions or entities with the same name, you need to put them in the stories after an action that sets them, because we don’t run actions during training. in rules, if you don’t care when some slot was set, you can put it in the condition. For example, these can be rules:

- rule:  transfer to human
  condition:
  - active_loop: main_form
  steps:
  - intent: speak_to_human
  - action: utter_speak_to_human
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart

in order for this rule to work, main_form should reject its execution for user input speak_to_human

- rule:  doesn't want to schedule appointment
  condition:
  - active_loop: main_form
  - slot_was_set:
    - schedule_mamogram : False
  steps:
  - action: utter_will_skip_appointment
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart

for this rule to work, some action should set a slot schedule_mamogram sometime in the past, and utter_will_skip_appointment should have been predicted by other policy or rule or it should have been triggered by followup action

Thanks, @Ghostvv for your reply. I have tried these rules prior to posting with no luck, but I did try them again based upon your reply. The top rule works, which isn’t a surprise given a similar story I created which does the same thing also worked. But the bottom rule does not work (and this is the one I really need to get working). I want to double-click on your statement below:

“for this rule to work, some action should set a slot schedule_mamogram sometime in the past, and utter_will_skip_appointment should have been predicted by other policy or rule or it should have been triggered by followup action”

What does this mean exactly? What would you expect this other policy to look like? My bot is actually pretty simple - it’s just a big form collecting data from the user. I am just looking for a proper way to exit the form based upon certain inputs from the user as slots are filled by their answers to the questions.

Thanks!

action utter_will_skip_appointment is the first action in the story or rule - it cannot be predicted

where utter_will_skip_appointment comes from? Can you give an example of a conversation, or rather full story from start to finish

Code is enclosed. Thanks.

code.zip (6.4 KB)

sorry, could you please post one story, I would like to avoid downloading project and going through the files

stories:
- story:  Happy path segment 1
  steps:
  - intent: greet
  - action: action_fetch_profile
  - action: main_form
  - active_loop: main_form
  - active_loop: null
  - action: utter_goodbye_patient_name
  - action: utter_collected_info
  - action: action_restart
  
- story:  Bail out because paitent is not home - this works I think because patient_not_available not trapped by main_form
  steps:
  - active_loop: main_form
  - intent: patient_not_available 
  - action: utter_will_call_back_later
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart
  
- story:  transfer to human - this works(?)
  steps:
  - active_loop: main_form
  - action: utter_speak_to_human
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart
  
- story:  will skip appointment
  steps:
  - active_loop: main_form
  - action: utter_will_skip_appointment
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart

rules:
- rule:  transfer to human - this works - speak to human is not trapped by main_form
  condition:
  - active_loop: main_form
  steps:
  - intent: speak_to_human
  - action: utter_speak_to_human
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart
  
- rule:  doesn't want to schedule appointment  - this does NOT work
  condition:
  - active_loop: main_form
  - slot_was_set:
    - schedule_mamogram : False
  steps:
  - action: utter_will_skip_appointment
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart
  
- rule:  doesn't want to talk to bot - this does NOT work
  condition:
  - active_loop: main_form
  - slot_was_set:
    - wont_speak_to_bot : "True"
  steps:
  - action: utter_speak_to_human
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart

you just pasted your stories, what I meant is how do you imagine a story go from start to finish to lead to utter_will_skip_appointment

I ask the user as part of a form flow if they want to schedule an appointment. if they do not want to schedule an appointment I want to exit the form and not ask any further questions. In Rasa 1.x, I did this check in a “validate_” function and if the answer was “no”, I would call self.deactivate() in an overridden request_next_slot function. I am struggling to understand the best way to do this in Rasa 2.x with stories, rules and the new constructs that 2.x offers. :confused:

there is 2 ways you can do that in 2.x, either add intent deny to not_intent in your slot mapping and create the rule or story with deny or you can still deactivate the form from validate_ action by either setting requested_slot to None (return [SlotSet("requested_slot", None)]) or setting active_loop to None (return [ActiveLoop(None)])

Thanks, @Ghostvv. I will go back to those techniques in the actions.py classes to exit the form.

I still would like to close the loop on an earlier part of our thread. @j.mosig said, “note that forms always take precedence and rules get ignored as long as the form is running.” But then you (@Ghostvv) gave this example of a rule that conditions on a form being active and a slot being set to a particular value.

- rule:  doesn't want to schedule appointment
  condition:
  - active_loop: main_form
  - slot_was_set:
    - schedule_mamogram : False
  steps:
  - action: utter_will_skip_appointment
  - action: action_deactivate_loop
  - active_loop: null
  - action: utter_collected_info
  - action: action_restart

and said:

for this rule to work, some action should set a slot schedule_mamogram sometime in the past, and utter_will_skip_appointment should have been predicted by other policy or rule or it should have been triggered by followup action

I am trying to understand the utility of this pattern and where it should be used. This is a place where an example consisting of story/rule/form pieces would be very helpful (and not just the individual fragments).

form get precedence until it rejects itself, then a rule or story can predict other actions, until the form is predicted again

@Ghostvv Respectfully, that answer is not super helpful. I continue to not understand how ‘slot_was_set’ works, after a tremendous amount of experimentation. I have also been through the documentation thoroughly. Here is a contrived, very simple example that is still perplexing:

I have an intent that expresses a user name to the bot:

- intent: inform_name
  examples: |
    - My name is [Joe](user_name)
    ....plus many more examples

I also have also specified in the domain file:

slots:
  user_name:
    type: text
    auto_fill: true
    influence_conversation: true
 
entities:
- user_name

My story is very simple:

- story: happy path
  steps:
  - intent: greet
  - action: utter_ask_name
  - intent: inform_name 
  - action: utter_greet_name
  - action: utter_goodbye_name
  - action: action_restart

Although I have many examples of the inform_name intent, understandably it doesn’t always work given the variation of names (I use spacy’s PERSON intent for more robust solutions). When the entity is not extracted from the inform_name intent, I simply want to have the bot ask for the user’s name again. I am trying to do this with a rule (although should be able to do this with a story too):

- rule: Didn't get user name
  steps:
  - intent: inform_name
  - slot_was_set:
    - user_name: None  # also tried null, 'null', 'None'
  - action: utter_ask_name_again

Based upon the documentation, I also tried:

- rule: Didn't get user name
  steps:
  - intent: inform_name
    entities:
    - user_name: None
  - action: utter_ask_name_again

I also tried checking for a particular name (e.g. “Jay”, assuming the intent fired correctly, which it often does), just to see the rule and condition fire correctly. No Luck.

I again respectfully request a thoughtful explanation that not only offers a solution to my contrived problem but also explains more rigorously how to use ‘slot_was_set’ (and ‘entities:’) in stories and rules.

Thanks in advance.

Sorry, I don’t understand what is the problem. As described here: Stories slot_was_set represents an event that happened at this time. It can happen in 2 cases:

  1. your custom action sets a slot
  2. extracted entity with the same name sets a slot

this part of the story means that you extracted the value None for the entity user_name

  - intent: inform_name
    entities:
    - user_name: None

If your slot user_name is featurized slot

  - intent: inform_name
  - slot_was_set:
    - user_name: None

should work

Thanks, @Ghostvv. I am glad you think it should work and I am not crazy. :crazy_face:

Unfortunately, though, it does not work (neither with “entities:” or “slot_was_set:” methods) The behavior is that with both methods, the rule fires every time - incorrectly asking for the name again when the slot was filled with inform_name intent. I commented-out the rule again and double-checked the intent I am giving the bot works (e.g. “My name is Jay” - user_name: “Jay”). You have all of the information above ^^^. I have not had any luck with these techniques, so interesting to see it doesn’t work with this simple bot too.

Any ideas?

I just tried with the following rule

- rule: Say hi
  steps:
  - intent: greet
  - slot_was_set:
    - user_name: None
  - action: utter_greet

and it works for me