def handle_match_found(self, agent_action: DialogAction): # If intent is match_found then fill the action informs with the matches informs (if there is a match) assert agent_action.inform_slots is None db_results = self.db_helper.get_db_results(self.current_informs) if db_results: item_idx, item = list(db_results.items())[0] agent_action.inform_slots = copy.deepcopy(item) agent_action.inform_slots[self.match_key] = str(item_idx) else: agent_action.inform_slots = {self.match_key: "no match available"} value = agent_action.inform_slots[self.match_key] self.current_informs[self.match_key] = value
def _return_init_action(self): self.state.intent = "request" if self.goal.inform_slots: # Pick all the required init. informs, and add if they exist in goal inform slots for inform_key in self.init_informs: if inform_key in self.goal.inform_slots: self.state.inform_slots[inform_key] = self.goal.inform_slots[ inform_key ] self.state.rest_slots.pop(inform_key) self.state.history_slots[inform_key] = self.goal.inform_slots[ inform_key ] # If nothing was added then pick a random one to add if not self.state.inform_slots: key, value = random.choice(list(self.goal.inform_slots.items())) self.state.inform_slots[key] = value self.state.rest_slots.pop(key) self.state.history_slots[key] = value req_key = self.get_request_key() self.state.request_slots[req_key] = "UNK" user_response = DialogAction( self.state.intent, self.state.inform_slots, self.state.request_slots, speaker=USER, ) return user_response
def infuse_error(self, action: DialogAction): """ Takes a semantic frame/action as a dict and adds 'error'. Given a dict/frame it adds error based on specifications in constants. It can either replace slot values, replace slot and its values, delete a slot or do all three. It can also randomize the intent. Parameters: frame (dict): format dict('intent': '', 'inform_slots': {}, 'request_slots': {}, 'round': int, 'speaker': 'User') """ informs_dict = action.inform_slots for key in list(action.inform_slots.keys()): assert key in self.slot2values if random.random() < self.slot_error_prob: if self.slot_error_mode == 0: # replace the slot_value only self._slot_value_noise(key, informs_dict) elif self.slot_error_mode == 1: # replace slot and its values self._slot_noise(key, informs_dict) elif self.slot_error_mode == 2: # delete the slot self._slot_remove(key, informs_dict) else: # Combine all three rand_choice = random.random() if rand_choice <= 0.33: self._slot_value_noise(key, informs_dict) elif rand_choice > 0.33 and rand_choice <= 0.66: self._slot_noise(key, informs_dict) else: self._slot_remove(key, informs_dict) if random.random() < self.intent_error_prob: # add noise for intent level action.intent = random.choice(self.intents)
def _rule_action(self) -> int: if self.rule_current_slot_index < len(RULE_REQUESTS): slot = RULE_REQUESTS[self.rule_current_slot_index] self.rule_current_slot_index += 1 rule_response = DialogAction("request", request_slots={slot: "UNK"}) elif self.rule_phase == "not done": rule_response = DialogAction("match_found") self.rule_phase = "done" elif self.rule_phase == "done": rule_response = DialogAction("done") else: raise Exception("Should not have reached this clause") index = map_action_to_index(rule_response) return index
def update_state_user(self, user_action: DialogAction): for key, value in user_action.inform_slots.items(): self.current_informs[key] = value user_action.turn = self.round_num self.history.append(user_action) self.round_num += 1
def update_state_agent(self, agent_action: DialogAction): if agent_action.intent == "inform": self.handle_inform_with_db_query(agent_action) elif agent_action.intent == "match_found": self.handle_match_found(agent_action) agent_action.turn = self.round_num self.history.append(agent_action)
def handle_inform_with_db_query(self, agent_action: DialogAction): assert agent_action.inform_slots slot_name = list(agent_action.inform_slots.keys())[0] value = self.db_helper.get_inform_value(slot_name, self.current_informs) agent_action.inform_slots = {slot_name: value} key, value = list(agent_action.inform_slots.items())[0] # Only one assert key != "match_found" assert value != "PLACEHOLDER", "KEY: {}".format(key) self.current_informs[key] = value
def step(self, agent_action: DialogAction): self.validate_action(agent_action) self.state.inform_slots.clear() self.state.intent = "" done = False success = NO_OUTCOME # First check round num, if equal to max then fail if agent_action.turn == self.max_round: done = True success = FAIL self.state.intent = "done" self.state.request_slots.clear() else: agent_intent = agent_action.intent if agent_intent == "request": self._response_to_request(agent_action) elif agent_intent == "inform": self._response_to_inform(agent_action) elif agent_intent == "match_found": self._response_to_match_found(agent_action.inform_slots) elif agent_intent == "done": success = self._response_to_done() self.state.intent = "done" self.state.request_slots.clear() done = True self.validate_state(self.state) user_response = DialogAction( self.state.intent, self.state.inform_slots, self.state.request_slots, speaker=USER, ) reward = reward_function( success, self.max_round ) # TODO(tilo): reward-calculation must not be done by user! return user_response, reward, done, True if success is 1 else False