Warning: This document is for an old version of Rasa Core. The latest version is 0.9.0.

Common Patterns

Note

Here we go through some typical conversation patterns and explain how to implement them with Rasa Core.

Rasa Core uses ML models to predict actions, and slots provide important information these models rely on to keep track of context and handle multi-turn dialogue. Slots are for storing information that’s relevant over multiple turns. For example, in our restaurant example, we would want to keep track of things like the cuisine and number of people for the duration of the conversation.

Collecting Information to Complete a Request

For collecting a set of preferences, you can use TextSlot s like in the restaurant example:

slots:
  cuisine:
    type: text
 people:
    type: text
 ...

When Rasa sees an entity with the same name as one of the slots, this value is automatically saved. For example, if your NLU module detects the entity people=8 in the sentence “I’d like a table for 8”, this will be saved as a slot,

>>> tracker.slots
{'people': <TextSlot(people: 8)>}

When Rasa Core predicts the next action to take, the only information it has about the TextSlot s is whether or not they are defined. So you have enough information to know that you don’t have to ask for this information again, but the value of a TextSlot has no impact on which actions Rasa Core predicts. This is explained in more detail below.

The full set of slot types and their behaviour is described here: Slots.

Using Slot Values to Influence Which Actions are Predicted

Custom Slots

Maybe your restaurant booking system can only handle bookings for up to 6 people, so the request above isn’t valid. In this case you want the value of the slot to influence the next selected action (not just whether it’s been specified). You can achieve this using a custom slot class.

The way we defined it below, if the number of people is less than or equal to 6, we return (1,0), if it’s more we return (0,1), and if it’s not set (0,0).

Rasa Core can use that information to distinguish between different situations - so long as you have some training stories where the appropriate responses take place, e.g.:

# story1
...
* inform{"people": "3"}
- action_book_table
...
# story2
* inform{"people": "9"}
- action_explain_table_limit
from rasa_core.slots import Slot

class NumberOfPeopleSlot(Slot):

  def feature_dimensionality(self):
      return 2

  def as_feature(self):
      r = [0.0] * self.feature_dimensionality()
      if self.value:
          if self.value <= 6:
              r[0] = 1.0
          else:
              r[1] = 1.0
      return r

If you want to store something like the price range, this is actually a little simpler. Variables like price range usually take on one-of-n values, e.g. low, medium, high. For these cases you can use a categorical slot.

slots:
  price_range:
    type: categorical
    values: low, medium, high

Rasa automatically represents (featurises) this as a one-hot encoding of the values: (1,0,0), (0,1,0), or (0,0,1).

Slot features

When Rasa Core runs trains a dialogue model using your stories the presence of a Slot entry will be used to help determine the next action that should be taken. This works best with CategoricalSlot slot types.

A TextSlot can have any value, but it only has a single feature. It can be set, in which case the feature has a value (1), or if it is not set it will have a value (0).

A CategoricalSlot has a number of values each of which is a feature. Taking the example below, when the restaurant_availability slot is set Rasa Core will be able to determine whether or not the restaurant in question is available and choose radically different actions to perform based on the value.

restaurant_availability:
    type: categorical
    values:
    - unknown
    - booked-out
    - waiting-list
    - available

A Slot will be set by Rasa Core if its name and the name of the entity detected by the NLU module match. The value of the slot will influence the story dialogue if you add the slot to the training stories - this is explained in the examples below. Slots can also be set explicitly from our own custom Action and influence the dialogue based on real-world information.

class ActionMakeBooking(Action):

    def run(self, dispatcher, tracker, domain):
        restaurant_name=tracker.get_slot("restaurant_name")
        location=tracker.get_slot("location")
        num_people=tracker.get_slot("people")
        date=tracker.get_slot("date")
        # this will fetch the availability of the restaurant from your DB or an API
        availability=restaurantService.check_availability(restaurant_name, location, num_people, date)
        return [SlotSet("restaurant_availability", availability)]

The snippet of code above from a hypothetical Action shows that the value of the slot restaurant_availability is determined by querying a database or API. The restaurant availability is not something that is known when we train the dialogue model, the Slot value is the only way we can alter the course of the conversation based on information from the outside world.

The data fetched from an API call can also be stored for later use without altering the outcome of a conversation as detailed in Storing API responses in the tracker.

Slot Features Example

Note

These example stories have been constructed manually for illustrative purposes. While this is a valid approach to training your model the preferred approach is to use interactive learning which generates stories that are much less error-prone.

In this first story we will try and make a booking for 5 people in a restaurant on the night of 21st August 2018. In this case the restaurant is booked out so we want to apologize to the customer and suggest similar restaurants. It is assumed that the Rasa Core model has been trained to recognise a message like “Book Murphys Bistro on August 21 for 5 people”

# restaurant unavailable
* _make_booking{"people":"5", "date":"2018-08-21T19:30:00+00:00", "restaurant_id":"145"}
- slot{"restaurant_availability": "booked-out"}
- utter_sorry_unavailable
- action_show_similar

This second story details the flow when the restaurant is available. We will tell the customer we have booked the restaurant and ask if any further help is required.

In this last example, the intent make_booking was found but either Rasa Core failed to parse a date or the date was not provided. In this case we would need to ask for more information.

Note: this last story is using the fact that date is a TextSlot and therefore has a single feature that is set or not.

# restaurant request without date
* _make_booking{"people":"5", "restaurant_id":"145"}
- slot{"date": null}
- utter_date_required
* _inform{"date":"2018-08-22T19:30:00+00:00"}
- action_make_booking
- utter_restaurant_booked
- utter_anything_more
* _bye
- utter_thank_you

Storing API responses in the tracker

The result from an API call can be stored in a Slot as explained above. In that case the data is stored in a Slot that is featurized, influencing the flow of the dialogue.

A slot of type unfeaturized can be used to store the results from a database query or API call so that it will not influence the course of a dialogue. An exaple unfeaturized slot defined in a domain file:

slots:
    api_result:
        type: unfeaturized

You can set this value in a custom Action:

from rasa_core.actions import Action
from rasa_core.events import SlotSet
import requests

class ApiAction(Action):
    def name(self):
        return "api_action"

    def run(self, tracker, dispatcher):
        data = requests.get(url).json
        return [SlotSet("api_result", data)]

This is especially useful when you are persisting your tracker in Redis or another data store. You could cache the API or database responses separately, but storing them in the tracker means they will be persisted automatically with the rest of the dialogue state, and will be restored along with the rest of the state should the system require a reboot.