How to handle chitchat while inside a form action?

Hi,

I am having trouble handling user deviations while inside a form. I have uncooperative paths where any chitchat is followed by the appropriate chitchat action, in the training data. But the bot seems to always predict form_action, even though the intent is identified well as chitchat.

I tried many things but it seems the bot doesn’t follow the path in the training data once form action is started.

Has anyone solved any similar issue?

@MaZe I have the same issue, do you solved it?

Hi there,

So I got it working in a way that was fine for my requirements. But I am not sure this is best practice.

The problem was the form policy was always predicting the form_action as the next action with a confidence of 1 even if unhappy paths were in the training data.

My solution was to throw an error that would exit the form like this:

if slot == ‘birthdate’:

try:

slot_values[slot] = self.birthdate_calculation(self, dispatcher, tracker, slot_values, slot, Date)

except ValueError:

print(value + “IS THE WRONG TYPE FOR BIRTHDATE”) dispatcher.utter_template(‘utter_default’, tracker) # validation failed, set slot to None slot_values[slot] = None

And then Keras policy came into play, and this policy would predict the next action correctly following what happens in the training data (chitchat). I have a chitchat_action that handles all chitchats and returns a followup action. This followup action would be the same action that happened in the event that lead to action_chitchat.

1 Like

yeah, this is a solution, but if chitchat twice and more, and the response is the same, it’s always utter_default, In my application:

the bot will ask: How much about your HBV-DNA? the user may ask: what is HBV-DNA?

and so i want to anwser the question, it’s not in my formaction,

At present, may be i only handle it by identify the intent a and return different resp.

I don’t think it is a graceful practice!

Anyway, Thank you very much!

Hi @red-frog could you post your form action and also your stories?

OK, the formaction is:

class SymptomForm(FormAction):
    """Example of a custom form action"""

    def name(self):
        return "symptom_form"

    @staticmethod
    def required_slots(tracker):
        """
        A list of required slots that the form has to fill
        :param tracker:
        :return:
        """
        positive = tracker.get_slot('positive')
        cirrhosis = tracker.get_slot('cirrhosis')
        if positive is False:
            return ['positive']
        elif cirrhosis is True:
            return ['positive', 'cirrhosis']
        else:
            return ['positive', 'cirrhosis', 'age', 'GPT', 'GOT', 'DNA']

    def slot_mappings(self):
        return dict(
            positive=[self.from_entity(entity='logic'),
                      self.from_intent(intent='symptom_info', value="no additional preferences")],
            cirrhosis=[self.from_entity(entity='logic'),
                       self.from_intent(intent='symptom_info', value="no additional preferences")],
            age=self.from_entity(entity='number'),
            GPT=self.from_entity(entity='number'),
            GOT=self.from_entity(entity='number'),
            DNA=self.from_entity(entity='number')
        )

    def submit(self, dispatcher, tracker, domain):
        """
        Define what the form has to do
           after all required slots are filled
        :param dispatcher:
        :param tracker:
        :param domain:
        :return:
        """
        Instruct().run(dispatcher, tracker, domain)
        return []

    def validate(self,
                 dispatcher,
                 tracker,
                 domain):
        """Validate extracted requested slot
            else reject the execution of the form action
        """
        slot_values = self.extract_other_slots(dispatcher, tracker, domain)
        slot_to_fill = tracker.get_slot(REQUESTED_SLOT)
        if slot_to_fill:
            slot_values.update(self.extract_requested_slot(dispatcher,
                                                           tracker, domain))
            if slot_to_fill == 'positive' or slot_to_fill == 'cirrhosis':
                value = tracker.get_slot('logic')
                if not value or value not in ['yes', 'no']:
                    dispatcher.utter_template('utter_wrong_bool', tracker)
                    # validation failed, set slot to None
                    slot_values[slot_to_fill] = None
                else:
                    isTrue = True if value == "yes" else False
                    slot_values[slot_to_fill] = isTrue
                slot_values['logic'] = None
                # validation succeed, set the slots values to the extracted values
            else:
                value = tracker.get_slot('number')
                if not value or not value.isdigit():
                    dispatcher.utter_template('utter_wrong_number', tracker)
                    slot_values[slot_to_fill] = None
                slot_values['number'] = None
        return [SlotSet(slot, value) for slot, value in slot_values.items()]

and the stories

## unhappy_path

* greeting
    - utter_greeting
    - symptom_form
    - form{"name": "symptom_form"}
    - slot{"requested_slot": "positive"}
    
* form: symptom_info{"logic":"yes"}
    - slot{"logic":"yes"}
    - form: symptom_form
    - slot{"positive":true}
    - slot{"requested_slot": "cirrhosis"}

* form: symptom_info{"logic":"no"}
    - slot{"logic":"no"}
    - form: symptom_form
    - slot{"cirrhosis":false}
    - slot{"requested_slot": "age"}

* form: symptom_info{"number":"25"}
    - slot{"number": "25"}
    - form: symptom_form
    - slot{"age":"25"}
    - slot{"requested_slot": "GOT"}
    
* question{"item":"转氨酶"}
    - slot{"item":"转氨酶"}
    - answer
    - action_listen

* symptom_info{"number":"40"}
    - slot{"number": "40"}
    - form: symptom_form
    - slot{"GOT":"40"}
    - slot{"requested_slot": "GPT"}

* form: symptom_info{"number":"60"}
    - slot{"number":"60"}
    - form: symptom_form
    - slot{"GPT":"60"}
    - slot{"requested_slot": "DNA"}

* question{"item":"DNA"}
    - slot{"item":"DNA"}
    - answer
    - action_listen

* symptom_info{"number":"2000"}
    - slot{"number": "2000"}
    - form: symptom_form
    - slot{"DNA":"2000"}
    - form{"name": null}
    - slot{"requested_slot": null}
    - action_restart

* bye
    - utter_bye
    - action_restart

example: i want to get the number about GOT.

the bot ask: How much about your GOT? the user may ask: What is the GOT

This, the bot should answer the question. and then wait the user answer. and every time, the NLU could correct classifier the intent is question.

But the action is always formaction in interactive, I don’t know how to solve it.

@akelad

@red-frog those stories look wrong. Have you looked at our documentation for how to write form stories?