form action可以变量么_智能问答-rasa框架02-自定义action

问答机器人框架中非常重要的几个概念为意图识别、槽位填充

通常使用机器学习算法进行意图的分类,但最终我们是让将用户的输入提取出我们想要的核心信息。--槽位提取

因此slot在rasa中极为重要, 如何我们想要深入自定义slot,以及solt前后逻辑,那么FormAction是必须要了解清楚的。

位于rasa_sdk/forms.py

class FormAction(Action):
    def name(self) -> Text:
        """Unique identifier of the form"""

        raise NotImplementedError("A form must implement a name")

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

        Use `tracker` to request different list of slots
        depending on the state of the dialogue
        """

        raise NotImplementedError(
            "A form must implement required slots that it has to fill"
        )

源码中可以看到name,required_slots,是必须实现的函数,作用是告诉rasa 此action都有哪些slot,此aciton名为什么,rasa官方demo中也如此进行了简介

class SalesForm(FormAction):
    """Collects sales information and adds it to the spreadsheet"""

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

    @staticmethod
    def required_slots(tracker) -> List[Text]:
        return [
            "job_function",
            "use_case",
            "budget",
            "person_name",
            "company",
            "business_email",
        ]

那么rasa将按照required_slots list顺序进行slot填充

流程是一致的,我们在story中定义意图以及匹配此意图是的机器人行为,

我们想要使用自定义的action,需要在 domain.yml 和stories.md中进行配置

## sales form
* contact_sales
    - sales_form                   <!--Run the sales_form action-->
    - form{"name": "sales_form"}   <!--Activate the form-->
    - form{"name": null}           <!--Deactivate the form-->

domain中定义fromaction

forms:
  - sales_form

当我们运行 rasa run, 和 新开一个窗口 rasa run actions时,会进行sales_form注册到rasa中

python ../start.py run actions
2020-09-07 14:03:54 INFO     rasa_sdk.endpoint  - Starting action endpoint server...
2020-09-07 14:03:54 INFO     rasa_sdk.executor  - Registered function for 'sales_form'.
2020-09-07 14:03:54 INFO     rasa_sdk.endpoint  - Action endpoint is up and running on http://localhost:5055

重点知识-story中的定义的何时触发action, domain.yml 中定义。

运行之后,发现会自动识别每一个slot槽位的,向用户提问。这里是因为formaction会自动循环required_slots中的slot,同时自动匹配 domain.yml utter_ask_+ 意图。

源码中此处进行了定义

    def request_next_slot(
        self,
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: Dict[Text, Any],
    ) -> Optional[List[EventType]]:
        """Request the next slot and utter template if needed,
            else return None"""

        for slot in self.required_slots(tracker):
            if self._should_request_slot(tracker, slot):
                logger.debug(f"Request next slot '{slot}'")
                dispatcher.utter_message(template=f"utter_ask_{slot}", **tracker.slots)
                return [SlotSet(REQUESTED_SLOT, slot)]

        # no more required slots to fill
        return None

因此我们可以重写request_next_slot,做前后slot信息验证

   def validate_business_email(
        self, value, dispatcher, tracker, domain
    ) -> Dict[Text, Any]:
        """Check to see if an email entity was actually picked up by duckling."""

        if any(tracker.get_latest_entity_values("email")):
            # entity was picked up, validate slot
            return {"business_email": value}
        else:
            # no entity was picked up, we want to ask again
            dispatcher.utter_message(template="utter_no_email")
            return {"business_email": None}

函数定义以 validate_ + slot rasa将自动进行输入校验

源码定义如下:

    async def validate_slots(
        self,
        slot_dict: Dict[Text, Any],
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: Dict[Text, Any],
    ) -> List[EventType]:
        """Validate slots using helper validation functions.

        Call validate_{slot} function for each slot, value pair to be validated.
        If this function is not implemented, set the slot to the value.
        """

        for slot, value in list(slot_dict.items()):
            validate_func = getattr(self, f"validate_{slot}", lambda *x: {slot: value})
            if utils.is_coroutine_action(validate_func):
                validation_output = await validate_func(
                    value, dispatcher, tracker, domain
                )
            else:
                validation_output = validate_func(value, dispatcher, tracker, domain)
            if not isinstance(validation_output, dict):
                logger.warning(
                    "Returning values in helper validation methods is deprecated. "
                    + f"Your `validate_{slot}()` method should return "
                    + "a dict of {'slot_name': value} instead."
                )
                validation_output = {slot: validation_output}
            slot_dict.update(validation_output)

        # validation succeed, set slots to extracted values
        return [SlotSet(slot, value) for slot, value in slot_dict.items()]

定义特定槽位的用户输入验证,有时候还是无法满足我们需求,我们得自定义request_next_slot, 方法中放飞自我

    def request_next_slot(
            self,
            dispatcher: "CollectingDispatcher",
            tracker: "Tracker",
            domain: Dict[Text, Any],
    ) -> Optional[List[EventType]]:
        """Request the next slot and utter template if needed,
            else return None"""
        for slot in self.required_slots(tracker):
            print(slot)
            if slot == "person_name" and tracker.get_slot("person_name") == "yxp":
                dispatcher.utter_message(text="对不起,不欢迎你,走开!!")
                return self.deactivate()

            if self._should_request_slot(tracker, slot):
                print("_should_request_slot")

                ## Condition of validated slot that triggers deactivation
                if slot == "person_name" and tracker.get_slot("person_name") == "yxp":
                    dispatcher.utter_message(text="对不起,不欢迎你,走开!!")
                    return self.deactivate()

                ## For all other slots, continue as usual
                logger.debug(f"Request next slot '{slot}'")
                dispatcher.utter_message(
                    template=f"utter_ask_{slot}", ** tracker.slots
                )
                REQUESTED_SLOT = "requested_slot"
                return [SlotSet(REQUESTED_SLOT, slot)]
            print("not _should_request_slot")
        return None

最后两个重要方法为 slot_mappings, 和 submit

slot_mappings 正如函数名称定义的那样,允许用户进行slot槽位提取时的复杂方式。

比如用户输入 确认意图,填充true,输入否定意图,填充false等

    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
        """A dictionary to map required slots to
            - an extracted entity
            - intent: value pairs
            - a whole message
            or a list of them, where a first match will be picked"""

        return {
            "job_function": [
                self.from_entity(entity="job_function"),
                self.from_text(intent="enter_data"),
            ],
            "use_case": [self.from_text(intent="enter_data")],
            "budget": [
                self.from_entity(entity="amount-of-money"),
                self.from_entity(entity="number"),
                self.from_text(intent="enter_data"),
            ],
            "person_name": [
                self.from_entity(entity="name"),
                self.from_text(intent="enter_data"),
            ],
            "company": [
                self.from_entity(entity="company"),
                self.from_text(intent="enter_data"),
            ],
            "business_email": [
                self.from_entity(entity="email"),
                self.from_text(intent="enter_data"),
            ],
        }

最后一个action结束时候,进行最后处理 submit函数为,当收集到所有的意图slot,最终处理方法。

    def submit(
            self,
            dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict[Text, Any],
    ) -> List[Dict]:
        person_name = tracker.get_slot("person_name")
        import datetime

        budget = tracker.get_slot("budget")
        company = tracker.get_slot("company")
        email = tracker.get_slot("business_email")
        job_function = tracker.get_slot("job_function")
        person_name = tracker.get_slot("person_name")
        use_case = tracker.get_slot("use_case")
        date = datetime.datetime.now().strftime("%d/%m/%Y")

        sales_info = [company, use_case, budget, date, person_name, job_function, email]
        print(sales_info)
        dispatcher.utter_message(template="utter_confirm_salesrequest", user_name=person_name)
        return []

以上,我们完成了FormAction 自定义处理, 可以帮助我们实现复杂的多轮次对话。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值