Using a single entity for different form slots

I have a form in which I have slots which would use the same entities. If I make two separate entities for them and duplicate the data, it will become problematic as the NLU may classify “current_country” as “destination_country” and vice versa. Therefore I only use one entity, and try to map them in the slots_mapping function in my form actions. Example code:

return {
    'current_country': self.from_entity(
        entity='country',
        intent=['inform']
    ),
    'destination_country': self.from_entity(
        entity='country',
        intent=['inform']
    )
}

The problem with this is, since the intent is inform, it asks the first question: “What is your current country?” When I answer that, with lets say Estonia, it sets the value for both, current_country and destination_country, skipping the question for destination_country:

What is your current country?
Your input ->  Estonia
2020-05-18 13:59:14 DEBUG    rasa.core.processor  - Current slot values:
current_country: Estonia
destination_country: Estonia
requested_slot: current_country

Obviously I want to be able to answer both questions separately, with separate values. Is there any way to do this cleanly through the slot_mappings?

For example, something like this (pseudocode):

return {
    'current_country': self.from_entity(
        entity='country',
        response=['utter_ask_current_country'] # Only fill the slot on this response
    ),
    'destination_country': self.from_entity(
        entity='country',
        response=['utter_ask_destination_country'] # Only fill the slot on this response
    )
}

I’ve been trying different things and searching for solutions for hours, I’ve seen many similar questions, none of them have had a concrete answer.

I’m not sure if I’m missing something, as I can’t believe something like this, which should be an extremely common usecase, doesn’t have any official coverage at all.

Or is this the wrong approach for an usecase like this?

Hi @Ulmm,

I am using a simpler approach here. I am getting text from user and saving it in the slot currentlocation and destination. Below is my actions.py.

class CountryForm(FormAction):

    def name(self) -> Text:
        return "CountryForm"

    @staticmethod
    def required_slots(tracker: Tracker) -> List[Text]:
        """A list of required slots that the form has to fill"""
        return ["currentlocation", "destination"]

    def slot_mappings(self) -> Dict[Text, Any]:
        return {
                "currentlocation": self.from_text(),
                "destination": self.from_text()
                }

    def submit(self, 
                dispatcher: CollectingDispatcher, 
                tracker: Tracker, 
                domain: Dict[Text, Any]) -> List[Dict]:
        current = tracker.get_slot('currentlocation')
        des = tracker.get_slot('destination')
        dispatcher.utter_message("I am from {} and I want to go to {}".format(current, des))
        return []

stories.md

## country test 
* greet
- CountryForm
- form{"name": "CountryForm"}
- form{"name": null}

domain.yml

slots:
  currentlocation:
    type: unfeaturized
  destination:
    type: unfeaturized
forms:
  - CountryForm
actions:
  - utter_ask_currentlocation
  - utter_ask_destination
responses:
  utter_ask_currentlocation:
  - text: "Where are you from?"
  utter_ask_destination:
  - text: "What is your destination?

Hope this helps!

-Murali

I suppose that could work!

But I assume that using from_text() loses the NER capabilities? For example, if I were to type “I am from Sweden”, it would set the entire text value of the slot, making the slot value “I am from Sweden”.

Is there any way to avoid that?

Yes,It loses NER capabilities. As I want to mention here It can be done in multiple approaches. For now I added logic in the required slots function so that it uses single entity to fulfill different slots

Class CountryForm(FormAction):

def name(self) -> Text:
    return "CountryForm"

@staticmethod
def required_slots(tracker: Tracker) -> List[Text]:
    """A list of required slots that the form has to fill"""
    if (tracker.get_slot('currentlocation') is None):
        return ["currentlocation"]
    else:
        return ["destination"]

def slot_mappings(self) -> Dict[Text, Any]:
    return {
            "currentlocation":  self.from_entity(entity="country", intent= "currentlocation"),
            "destination": self.from_entity(entity="country", intent="currentlocation")
            }

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

    current = tracker.get_slot('currentlocation')
    des = tracker.get_slot('destination')
    #Testing purpose
    dispatcher.utter_message("I am from {} and I want to go to {}".format(current, des))
    return []

nlu.md

 ## intent:currentlocation
- I am from [sweden](country)
- I want to go [Estonia](country)
- I am from [UK](country)
- I am from [Russia](country)
- I want to go [United States](country)
- I want to go [sweden](country)
- I want to go [UK](country)
- I am from [Estonia](country)

Indeed, the required_slots modification, although a bit unintuitive, works!

Thanks!

hi @Ulmm - I solved almost the exact thing in carbon bot using this new feature: Introducing entity roles and groups

Thanks for the heads up! Good thing that feature was introduced!