Answers based on the context (through slots and forms)

I’m making a bot that outputs a different date for application given the student’s degree (master or phd). The user can either ask for “date for application” and then the bot asks for the degree; or the user can ask for “date of application for master degree”. The bot answers successfully, as well through the form solution and saves the slots “service” (date of application), “degree” (master) and “date” (October 11st for example).

The problem is: Although I can make the bot save those slots, if the user asks the date of application again or asks for another service, he won’t make use of the context (in this case, the slot degree: master) and will ask for the degree again.

If you want I can post the files, it’s simply because its in my native language and it can be difficult to understand

How have you defined the slots in the domain?

If you want the content of a slot (is the degree “master” or “phd”?) to influence the conversation, the type of the slot needs to be set to categorical. If the type is text instead, Rasa Core will only know if the slot is set but not the content. Then the bot’s responses cannot depend on what’s in the slot. See the docs page about Slots for more explanation.

Also, what can always help to get better predictions is to add more stories. If the slot type is set to categorical but the bot still doesn’t behave like it should in a specific situation, add this situation including the desired response from the bot as another training story.

Does this help you with your question? If anything is unclear or if it still doesn’t work, let me know.

It didn’t work, it kept asking the degree even when the degree slot was filled. I’ve put the slot type as text, categorical and unfeaturized (for forms) and none worked although it saved the slots. Those are the stories I’ve put:

#application

  • search_application{“service”:“applicationdate”, “degree”:“masters”}
    • action_application
    • slot{“date”:“October 11st”}

#application+degree

  • search_application{“service”:“applicationdate”}
    • utter_ask_degree
  • inform_application{“degree”:“masters”}
    • action_application
    • slot{“date”:“October 11st”}
    • slot{“degree”:“masters”}

With the first story, it should directly answer the date when the slot is filled as “masters” through previous conversations (instead of asking the user’s degree), right?

No, the first intent in the first story (search_application{"service":"applicationdate", "degree":"masters"}) means something different than you think. It doesn’t mean "The degree slot is set to "masters", the service slot is set to "applicationdate" and the user sends a message with intent search_application". Instead, it means "The user sends a message with intent search_application that includes a service entity with value "applicationdate" and a degree entity with value "masters". So this story will only cover a situation where the user message includes both, e.g. “What’s the application date for a masters degree?”

A story that covers the situation you want is one where the slot is first set and then later used, e.g.

* search_application{“service”:“some_other_service”}
  - utter_ask_degree
* inform_application{“degree”:“masters”}
  - slot{“degree”:“masters”}
  - ...whatever else the other service sets or does
* search_application{“service”:“applicationdate”}
  - action_application
  - slot{“date”:“October 11st”}

This becomes trickier though if a lot of conversation can happen between the slot being set and used - you might have to write lots of different stories… Instead of manually writing those stories, you can also use Interactive Learning to teach your bot how to behave in those situations.

An alternative solution (probably easier and nicer) is this: Use a Form with required_slots service and degree. A form will only ask for those required slots that aren’t filled yet. So a story

* search_application{"service":"applicationdate"}
  - application_form
  - form{"name": "application_form"}
  - form{"name": null}
  - action_application
  - slot{"date":"October 11th"}

should cover all cases now. The form will make sure all slots are filled (= do nothing if the slots are already set, ask for the degree if the degree slot is not set yet), and then in your custom action action_application you can pick the date depending on the value of tracker.get_slot("degree").

Note that for this to work, the slots in the form need to have type unfeaturized. This means they won’t affect the prediction of the next action, which in this case is fine: no matter what the degree is, the bot should always run the form first to ask for all unfilled slots and then run action_application.

Sorry for the long text. I recommend you try the solution with the form, and let me know if you run into any more questions or issues.

The previous reply was actually flawed so I rewrote it for a more thoughtful post

@chkoss Yes, the solution with the form is the most preferable one since I’ll need the bot to scale and avoid predicting every single interaction. And by using the form it worked, thank you! : When I ask for the application date a second time it doesn’t ask for the degree anymore and answers the date directly.

The problem now is that by asking it again, it gives me the form utter_submit response alongside with the date. Should I put a duplicate story but without the form so it doesn’t go through the submit message, or even put a flag in the submit function in .py? Or is there any better approach?

Problem visualization:

User:Application date
Bot:What’s your name?
U:John
B: What’s your degree?
U:Masters
B: Your name is John and your degree is masters.
B: The application date for Masters is November 5th

U: Application Date
B: Your name is John and your degree is masters. (<- the problem)
B: The application date for Masters is November 5th

Adding a second story won’t work, because Rasa can’t know which of the two stories it should follow when. So it might sometimes skip the form when it shouldn’t.

Changing the submit function of your FormAction sounds like a good solution. That function has access to the Tracker, so you could e.g. look at the last event in the tracker

e = tracker.current_state().get("events")[-1]

and see if it was one of the slots being set:

e.event=="slot" & (e.name=="degree" | e.name=="name")

Then you can use that as a condition to do utter_submit.

I’m not sure if I understood correctly but it should look like this? It gives the error AttributeError: 'dict' object has no attribute 'events'

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

    e = tracker.current_state().get("events")[-1]

    if(e.event=="slot" & (e.name=="degree" or e.name=="name")):
        dispatcher.utter_message(template="utter_submit2", name=tracker.get_slot('name'), grau=tracker.get_slot('degree'))
    else:
        dispatcher.utter_message(template="utter_submit3", name=tracker.get_slot('name'), grau=tracker.get_slot('degree'))

    return []

Where utter_submit2 is empty and utter_submit3 says “Your name is John and your degree is masters.”