Beispiel #1
0
def test_tracker_update_slots_with_entity(default_domain: Domain):
    tracker = DialogueStateTracker("default", default_domain.slots)

    test_entity = default_domain.entities[0]
    expected_slot_value = "test user"

    intent = {"name": "greet", "confidence": 1.0}
    tracker.update(
        UserUttered(
            "/greet",
            intent,
            [
                {
                    "start": 1,
                    "end": 5,
                    "value": expected_slot_value,
                    "entity": test_entity,
                    "extractor": "manual",
                }
            ],
        ),
        default_domain,
    )

    assert tracker.get_slot(test_entity) == expected_slot_value
Beispiel #2
0
    def extract_other_slots(self, tracker: DialogueStateTracker,
                            domain: Domain) -> Dict[Text, Any]:
        """Extract the values of the other slots
        if they are set by corresponding entities from the user input
        else return `None`.
        """
        slot_to_fill = tracker.get_slot(REQUESTED_SLOT)

        entity_type_of_slot_to_fill = self._get_entity_type_of_slot_to_fill(
            slot_to_fill, domain)

        slot_values = {}
        for slot in self.required_slots(domain):
            # look for other slots
            if slot != slot_to_fill:
                # list is used to cover the case of list slot type
                other_slot_mappings = self.get_mappings_for_slot(slot, domain)

                for other_slot_mapping in other_slot_mappings:
                    # check whether the slot should be filled by an entity in the input
                    should_fill_entity_slot = (other_slot_mapping["type"]
                                               == str(SlotMapping.FROM_ENTITY)
                                               and self.intent_is_desired(
                                                   other_slot_mapping, tracker)
                                               and self.entity_is_desired(
                                                   other_slot_mapping,
                                                   slot,
                                                   entity_type_of_slot_to_fill,
                                                   tracker,
                                               ))
                    # check whether the slot should be
                    # filled from trigger intent mapping
                    should_fill_trigger_slot = (
                        tracker.active_loop.get("name") != self.name()
                        and other_slot_mapping["type"] == str(
                            SlotMapping.FROM_TRIGGER_INTENT) and
                        self.intent_is_desired(other_slot_mapping, tracker))
                    if should_fill_entity_slot:
                        value = self.get_entity_value(
                            other_slot_mapping["entity"],
                            tracker,
                            other_slot_mapping.get("role"),
                            other_slot_mapping.get("group"),
                        )
                    elif should_fill_trigger_slot:
                        value = other_slot_mapping.get("value")
                    else:
                        value = None

                    if value is not None:
                        logger.debug(
                            f"Extracted '{value}' for extra slot '{slot}'.")
                        slot_values[slot] = value
                        # this slot is done, check  next
                        break

        return slot_values
    def predict_action_probabilities(
        self, tracker: DialogueStateTracker, domain: Domain
    ) -> List[float]:
        """Predicts a fallback action if an unsupported intent is received.
        """

        if tracker.latest_action_name == self.fallback_action_name:
            logger.debug(
                "Predicted 'action_listen' after fallback action '{}'".format(
                    self.fallback_action_name
                )
            )
            result = self._default_predictions(domain)
            idx = domain.index_for_action(ACTION_LISTEN_NAME)
            result[idx] = 1.0

            return result

        if tracker.latest_action_name == ACTION_LISTEN_NAME:
            action_name = _get_previous_action_name(tracker)

            if action_name is None or not action_name in SUPPORTED_INTENTS_BY_ACTION:
                logger.debug("Skipping unsupported intent check")
                return self._default_predictions(domain)

            supported_intents = SUPPORTED_INTENTS_BY_ACTION[action_name]

            intent = _get_intent(tracker)

            if intent in supported_intents or intent == FALLBACK_INTENT:
                if (
                    tracker.get_slot(SELF_ASSESS_DONE_SLOT) is True
                    and intent == GET_ASSESSMENT_INTENT
                ):
                    logger.debug(
                        "Received {} intent but assessment is already done".format(
                            GET_ASSESSMENT_INTENT
                        )
                    )
                    return self.fallback_scores(domain, 1.0)

                logger.debug(
                    "No unexpected intent after action '{}'".format(action_name)
                )
                return self._default_predictions(domain)

            logger.debug(
                "Unexpected intent '{}' after action '{}'".format(intent, action_name)
            )
            return self.fallback_scores(domain, 1.0)

        logger.debug("Skipping unsupported intent check")
        return self._default_predictions(domain)
Beispiel #4
0
def test_tracker_does_not_modify_slots(slot_type: Type[Slot],
                                       initial_value: Any, value_to_set: Any):
    slot_name = "some-slot"
    slot = slot_type(slot_name, initial_value)
    tracker = DialogueStateTracker("some-conversation-id", [slot])

    # change the slot value in the tracker
    tracker._set_slot(slot_name, value_to_set)

    # assert that the tracker contains the slot with the modified value
    assert tracker.get_slot(slot_name) == value_to_set

    # assert that the initial slot has not been affected
    assert slot.value == initial_value
Beispiel #5
0
    async def _predict_and_execute_next_action(
        self, output_channel: OutputChannel, tracker: DialogueStateTracker
    ):
        # keep taking actions decided by the policy until it chooses to 'listen'
        should_predict_another_action = True
        num_predicted_actions = 0

        # action loop. predicts actions until we hit action listen
        message = ""
        while (
            should_predict_another_action
            and self._should_handle_message(tracker)
            and num_predicted_actions < self.max_number_of_predictions
        ):
            # this actually just calls the policy's method by the same name
            action, policy, confidence = self.predict_next_action(tracker)

            should_predict_another_action, messages = await self._run_action(
                action, tracker, output_channel, self.nlg, policy, confidence
            )
            if messages: 

                if len(messages) > 2:
                    messages = messages[0:2].capitalize() + messages[2:]

                message += messages + ". "
            num_predicted_actions += 1
        
        if message and not tracker.get_slot("first_name") is None:            
            message = message[:-2]
            message = message.strip()
            await output_channel.send_response(tracker.sender_id, { 'text': message })

        if self.is_action_limit_reached(
            num_predicted_actions, should_predict_another_action
        ):
            # circuit breaker was tripped
            logger.warning(
                "Circuit breaker tripped. Stopped predicting "
                f"more actions for sender '{tracker.sender_id}'."
            )
            if self.on_circuit_break:
                # call a registered callback
                self.on_circuit_break(tracker, output_channel, self.nlg)
Beispiel #6
0
    def predict_action_probabilities(self, tracker: DialogueStateTracker,
                                     domain: Domain) -> List[float]:
        """Predicts the assigned action."""

        # 更新对话ID
        if tracker.sender_id != self.dialogue_id:
            self.dialogue_id = tracker.sender_id
            self.latest_agent_ask_utter = 'utter_ask_own_name'
            self.chat_num = 0

        prediction = self._default_predictions(domain)

        action = None
        intent = tracker.latest_message.intent.get('name')
        if intent == 'greet':
            if self.chat_num == 0:
                if tracker.latest_action_name == ACTION_LISTEN_NAME:
                    action = 'utter_greet'
                elif tracker.latest_action_name == 'utter_greet':
                    # 镜像表达问候之后,
                    # 第一次表达greet,追问之前的问题
                    action = self.latest_agent_ask_utter
                else:
                    # 这一轮闲聊结束,进入用户监听模式
                    # 追问之后就应该agent就应该去监听用户的回答
                    action = ACTION_LISTEN_NAME
                    # 更新最新的询问, 其实不需要更新,继续保持原样就好
                    self.latest_agent_ask_utter = self.latest_agent_ask_utter
                    # 闲聊计数,一轮闲聊结束
                    self.chat_num += 1
            else:
                # 不予许闲聊两次,greet也算一种chat
                if tracker.latest_action_name == ACTION_LISTEN_NAME:
                    action = 'utter_bye'
                else:
                    action = ACTION_LISTEN_NAME
        elif intent == 'bye':
            # 遇到intent bye 直接弹出
            if tracker.latest_action_name == ACTION_LISTEN_NAME:
                action = 'utter_bye'
            else:
                action = ACTION_LISTEN_NAME
        elif intent == 'thanks':
            # user
            if tracker.latest_action_name == ACTION_LISTEN_NAME:
                action = 'utter_welcome'
            elif tracker.latest_action_name == 'utter_welcome':
                action = 'utter_bye'
            else:
                action = ACTION_LISTEN_NAME
                # 更新最新的询问, 其实不需要更新,继续保持原样就好
                self.latest_agent_ask_utter = self.latest_agent_ask_utter
        elif intent == 'chat':
            if self.chat_num == 0:
                if tracker.latest_action_name == ACTION_LISTEN_NAME:
                    action = 'utter_chat'
                elif tracker.latest_action_name == 'utter_chat':
                    # 第一次表达chaet之意,追问前一个agent的提问
                    action = self.latest_agent_ask_utter
                else:
                    # 此轮闲聊结束,进入用户监听状态
                    action = ACTION_LISTEN_NAME
                    self.latest_agent_ask_utter = self.latest_agent_ask_utter
                    # 闲聊计数
                    self.chat_num += 1
            else:
                if tracker.latest_action_name == ACTION_LISTEN_NAME:
                    action = 'utter_chat'
                elif tracker.latest_action_name == 'utter_chat':
                    action = 'utter_bye'
                else:
                    action = ACTION_LISTEN_NAME
        elif intent == 'inform':
            entities = tracker.latest_message.entities
            if entities:
                if not tracker.get_slot('is_valid_password') is None:
                    # 已经经历了密码匹配
                    if tracker.get_slot('is_valid_password'):
                        # 成功
                        if tracker.latest_action_name == 'utter_validation_pass':
                            action = ACTION_LISTEN_NAME
                        else:
                            # 表达进门,后面再说什么也不在做其他回复,一直回复这句话
                            # 都是为了简化逻辑
                            action = 'utter_validation_pass'
                    elif not tracker.get_slot('is_valid_password'):
                        # 失败
                        if tracker.latest_action_name == 'utter_password_error':
                            action = ACTION_LISTEN_NAME
                        else:
                            # 表达请离开,不管后面在回答任何内容
                            action = 'utter_password_error'
                elif tracker.get_slot('is_own'):
                    # 第二步,匹配密码
                    if tracker.get_slot('password'):
                        # 已经收集到密码,匹配密码
                        action = 'action_match_password'
                    elif tracker.latest_action_name == 'utter_ask_setting_password':
                        # 收集密码的第二步,等待用户的回答
                        action = ACTION_LISTEN_NAME
                    else:
                        # 没有收集到password, 收集密码第一步,提问
                        action = 'utter_ask_setting_password'
                        # 更新最新agent的 ask
                        self.latest_agent_ask_utter = action
                elif tracker.get_slot('name'):
                    # 第一步,匹配姓名
                    if tracker.latest_action_name == ACTION_LISTEN_NAME:
                        action = 'action_match_name'
                    elif tracker.latest_action_name == "utter_own_name_error":
                        # 只要有agent的utter,都将进入agent监状态
                        action = ACTION_LISTEN_NAME
                    elif not tracker.get_slot('is_own'):
                        action = 'utter_own_name_error'
            else:
                if tracker.latest_action_name == ACTION_LISTEN_NAME:
                    # 没有收集到任何实体信息,重新追问上一个问题
                    action = self.latest_agent_ask_utter
                else:
                    action = ACTION_LISTEN_NAME
                    # 更新最新的询问, 其实不需要更新,继续保持原样就好
                    self.latest_agent_ask_utter = self.latest_agent_ask_utter
        else:
            # every dialogue start, no collected any intent(or user utter)
            if tracker.latest_action_name == ACTION_LISTEN_NAME:
                action = self.latest_agent_ask_utter
            else:
                action = ACTION_LISTEN_NAME

        # 获取action的index
        idx = domain.index_for_action(action)
        prediction[idx] = 1

        return prediction