Can I bind entities/slots to intents/forms?

TL;DR: Two entities are very similar (5 digits vs 6 digits) and it mistakes one for the other.

Hey, I’m relatively new to RASA (started just on Monday) and it’s awesome.

I’m currently trying to create a Chat Bot that can give a customer general information but also information on their order at our online shop. To get that status, the user has to give the bot the order_id and the postcode of the shipping address.

Now each of them work fine independently of each other, but now I chained them together (i.e. first order_id, then postcode, then check if it’s correct and reply with information).

If the bot asks for the order ID, if the user inputs “Order 123456” everything works. But if the user just inputs “123456” it mistakes that number for the postcode (which is just 5 digits in length).

Here’s my custom_slots.py: class OrdernumberSlot(Slot): def feature_dimensionality(self): return 2

    def as_feature(self):
        r = [0.0] * self.feature_dimensionality()
        if self.value:
            match = re.match("\d{6}", self.value)
            if match:
                r[0] = 1.0
            else:
                r[1] = 1.0

        return r

class PostcodeSlot(Slot):
    def feature_dimensionality(self):
        return 2

    def as_feature(self):
        r = [0.0] * self.feature_dimensionality()
        if self.value:
            match = re.match("\d{5}", self.value)
            if match:
                r[0] = 1.0
            else:
                r[1] = 1.0

        return r

I assumed that using the regex there would tell the system what kind of input it is (on top of the intent).

The relevant part of my nlu.md:

## intent:inform_order
- Meine Bestellnr ist [323321](ordernumber)
- Die Bestellnummer lautet [378879](ordernumber)
- Meine Bstnr is [377876](ordernumber)
- Bestellnummer [355898](ordernumber)
- Bestellnummer ist [355652](ordernumber)
- [398752](ordernumber)
- [364651](ordernumber)
- Das ist die [336336](ordernumber)
- Das wäre die [369878](ordernumber)
- Bestesllung [254325](ordernumber)
- Bestellung [345621](ordernumber)
- Bst [445544](ordernumber)
- Bstnr [123123](ordernumber)
- [324654](ordernumber)
- Meine Bestellnummer ist [321344](ordernumber)
- [345123](ordernumber)
- Bstnr ist die [345678](ordernumber)
- DAs wäre die [314312](ordernumber)
- Bestellnr [278899](ordernumber)
- [387122](ordernumber)
- Bstnr. [345561](ordernumber)
- [337831](ordernumber)
- [389009](ordernumber)
- Bestellung [311178](ordernumber)
- [340909](ordernumber)
- Bestellnummer ist [320222](ordernumber)
- Best. [301010](ordernumber)
- Nr [304449](ordernumber)
- Nr. [309919](ordernumber)
- Nr[321188](ordernumber)
- No. [326969](ordernumber)
- Bestellung ist [343411](ordernumber)
- Bestellung [324632](ordernumber)
- Meine Bestellnummer ist [321000](ordernumber)
- [321322333](ordernumber)
- [334411](ordernumber)
- Bestellnr ist [112211](ordernumber)
- Bestellung [388309](ordernumber) .
- [10283701237](ordernumber)
- [366666](ordernumber)
- [32132](ordernumber)
- [388812](ordernumber)
- Bestllnr [345110](ordernumber)
- [21215](ordernumber)
- Bstnr [311344](ordernumber)
- [402873](postcode)
- Bestellnummer [402873](ordernumber)
- Bst [402873](ordernumber)
- bst [402873](ordernumber)
- [402879](postcode)

and

## intent:inform_postcode
- Meine PLZ ist [24963](postcode)
- Die Postleitzahl lautet [547896](postcode)
- Meine PLZ is [54778](postcode)
- Postleitzahl [25447](postcode)
- Postleitzahl ist [56658](postcode)
- [11245](postcode)
- [54482](postcode)
- Das ist die [25548](postcode)
- Das wäre die [33598](postcode)
- PLZ [65789](postcode)
- Postleitzahl [05478](postcode)
- Meine Postleitzahl ist [09785](postcode)
- [12558](postcode)
- PLZ ist die [23585](postcode)
- DAs wäre die [349465](postcode)
- PLZ [87448](postcode)
- [658235](postcode)
- PLZ: [63558](postcode)
- [04588](postcode)
- [99255](postcode)
- Postleitzahl: [05547](postcode)
- [06858](postcode)
- Postleitzahl ist [11547](postcode)
- PLZ: [22485](postcode)
- PLZ [855374](postcode)
- PLZ: [95587](postcode)
- PLZ[65442](postcode)
- PLZ: [23344](postcode)
- Postleitzahl ist [25548](postcode)
- Postleitzahl [233145](postcode)
- Meine Postleitzahl ist [36587](postcode)
- [78542](postcode)
- [85475](postcode)
- Postleitzahl ist [25478](postcode)
- Postleitzahl [32558](postcode).
- [15478](postcode)
- [87452](postcode)
- [24598](postcode)
- [54725](postcode)
- PLZ [95147](postcode)
- [75369](postcode)
- PLZ [74523](postcode)
- PLZ [53340](postcode)
- plz [53340](postcode)
- [53340](postcode)
- [53341](postcode)

The reason why I added some ordernumbers and postcodes with the wrong amount of digits is so that it would trigger a utter_incorrect_ordernumber or utter_incorrect_postcode. Is this the right way to do it? I have 16 stories, all set up right, but when it comes to testing it after training, it still thinks I want to send a postcode to the intent inform_order.

Is there a way to bind an entity to an intent or a slot to a form? So it listens specifically for one entity?

Thank you guys so much in advance! It’s a ton of fun working with Rasa / Rasa X!

EDIT: More information that might be relevant

domain.yml intents, entities, slots and forms:

intents:
- greet
- deny
- cancel
- affirm
- orderstatus
- inform_order
- inform_postcode
- bye
- thanks
entities:
- ordernumber
- postcode
slots:
  ordernumber:
    type: unfeaturized
  postcode:
    type: unfeaturized
  requested_slot:
    type: unfeaturized

config.yml

%YAML 1.1
---
language: de_core_news_sm
pipeline: pretrained_embeddings_spacy
policies:
  - name: KerasPolicy
    epochs: 175
    max_history: 5
  - name: FallbackPolicy
    fallback_action_name: 'action_default_fallback'
  - name: MemoizationPolicy
    max_history: 5
  - name: FormPolicy

actions.py OrdernumberForm class:

class OrdernumberForm(FormAction):
    def name(self):
        """ Unique identifier of the form"""
        return "ordernumber_form"

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

    def validate_ordernumber(self, value: Text, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> Optional[Text]:
        match = re.match("([^0-9]|^)[0-9]{6}([^0-9]|$)", value)
        if match:
            ordernumber = match.group(0)
            dispatcher.utter_template('utter_ask_postcode', tracker)
            return {'ordernumber': ordernumber}
        else:
            dispatcher.utter_template('utter_wrong_ordernumber', tracker)
            return None

    def submit(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> List[Dict]:
        """Define what the form does after all required slots are filled"""

        # dispatcher.utter_template('utter_submit', tracker)
        # dispatcher.utter_template('utter_ask_for_more_questions', tracker)
        return []

actions.py PostcodeForm class:

class PostcodeForm(FormAction):
    def name(self):
        """ Unique identifier of the form"""
        return "postcode_form"

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

    def validate_postcode(self, value: Text, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> Optional[Text]:
        match = re.match("^([0]{1}[1-9]{1}|[1-9]{1}[0-9]{1})[0-9]{3}$", value)
        ordernumber = tracker.get_slot("ordernumber")
        #(and then some api calls)#

Alright, I figured it out.

Basically, one of my intent:inform_order entries said (postcode). I also added regex for both postcode and ordernumber.

The other thing I changed completely was the actions.py. Instead of two classes, I now have a GetOrderForm, which requires both ordernumber and postcode to proceed. A lot cleaner.