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
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)
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
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)
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