From 588e00981e058b1d48ee79d0b0dc21b3e03418ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20J=C4=99dyk?= Date: Fri, 18 Jun 2021 15:26:21 +0200 Subject: [PATCH] DP, DST and NLG fixes --- DialoguePolicy.py | 44 +++---- DialogueStateTracker.py | 6 - NaturalLanguageGeneration.py | 232 +++++++++++++++++------------------ data/train.conllu | 32 ++++- main.py | 4 +- 5 files changed, 161 insertions(+), 157 deletions(-) diff --git a/DialoguePolicy.py b/DialoguePolicy.py index fe46bcb..2407aaf 100644 --- a/DialoguePolicy.py +++ b/DialoguePolicy.py @@ -5,17 +5,11 @@ from collections import defaultdict class DP: - """ - Moduł decydujący o wyborze kolejnego aktu, który ma podjąć system prowadząc rozmowę. - Wejście: Reprezentacja stanu dialogu (rama) - Wyjście: Akt systemu (rama) - """ - def __init__(self, dst): self.DST = dst self.meeting_to_update = False - def chooseTactic(self) -> SystemAct: + def choose_tactic(self) -> SystemAct: dialogue_state, last_user_act, last_system_act = self.DST.get_dialogue_state() slots = self.DST.get_dialogue_slots() # stan dodawania spotkania @@ -89,11 +83,11 @@ class DP: elif dialogue_state == UserActType.UPDATE_MEETING: if not last_system_act: if 'date' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['date']) + system_act = SystemAct(SystemActType.REQUEST, ['date', False]) self.DST.system_update(system_act) return system_act elif 'time' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['time']) + system_act = SystemAct(SystemActType.REQUEST, ['time', False]) self.DST.system_update(system_act) return system_act else: @@ -102,11 +96,11 @@ class DP: elif last_system_act.getActType() == SystemActType.REQUEST: if not self.meeting_to_update: if 'date' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['date']) + system_act = SystemAct(SystemActType.REQUEST, ['date', False]) self.DST.system_update(system_act) return system_act elif 'time' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['time']) + system_act = SystemAct(SystemActType.REQUEST, ['time', False]) self.DST.system_update(system_act) return system_act else: @@ -119,23 +113,23 @@ class DP: slot_type = last_system_act.getActParams()[0] self.DST.insert_empty_slot(slot_type) if 'date' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['date']) + system_act = SystemAct(SystemActType.REQUEST, ['date', True]) self.DST.system_update(system_act) return system_act elif 'time' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['time']) + system_act = SystemAct(SystemActType.REQUEST, ['time', True]) self.DST.system_update(system_act) return system_act elif 'place' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['place']) + system_act = SystemAct(SystemActType.REQUEST, ['place', True]) self.DST.system_update(system_act) return system_act elif 'description' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['description']) + system_act = SystemAct(SystemActType.REQUEST, ['description', True]) self.DST.system_update(system_act) return system_act elif 'participants' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['participants']) + system_act = SystemAct(SystemActType.REQUEST, ['participants', True]) self.DST.system_update(system_act) return system_act else: @@ -144,23 +138,23 @@ class DP: return system_act else: if 'date' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['date']) + system_act = SystemAct(SystemActType.REQUEST, ['date', True]) self.DST.system_update(system_act) return system_act elif 'time' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['time']) + system_act = SystemAct(SystemActType.REQUEST, ['time', True]) self.DST.system_update(system_act) return system_act elif 'place' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['place']) + system_act = SystemAct(SystemActType.REQUEST, ['place', True]) self.DST.system_update(system_act) return system_act elif 'description' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['description']) + system_act = SystemAct(SystemActType.REQUEST, ['description', True]) self.DST.system_update(system_act) return system_act elif 'participants' not in slots: - system_act = SystemAct(SystemActType.REQUEST, ['participants']) + system_act = SystemAct(SystemActType.REQUEST, ['participants', True]) self.DST.system_update(system_act) return system_act else: @@ -180,7 +174,7 @@ class DP: if last_user_act == UserActType.CONFIRM: self.meeting_to_update = True self.DST.clear_slots() - system_act = SystemAct(SystemActType.REQUEST, ['date']) + system_act = SystemAct(SystemActType.REQUEST, ['date', True]) self.DST.system_update(system_act) return system_act elif last_user_act == UserActType.NEGATE: @@ -236,8 +230,7 @@ class DP: return SystemAct(SystemActType.REQMORE, ['meeting_list']) else: if 'date' in slots: - system_act = SystemAct(SystemActType.MEETING_LIST, slots) - self.DST.clear() + system_act = SystemAct(SystemActType.MEETING_LIST, {'date': slots['date']}) return system_act else: system_act = SystemAct(SystemActType.REQUEST, ['date']) @@ -250,8 +243,7 @@ class DP: return SystemAct(SystemActType.REQMORE, ['free_time']) else: if 'date' in slots: - system_act = SystemAct(SystemActType.FREE_TIME, slots) - self.DST.clear() + system_act = SystemAct(SystemActType.FREE_TIME, {'date': slots['date']}) return system_act else: system_act = SystemAct(SystemActType.REQUEST, ['date']) diff --git a/DialogueStateTracker.py b/DialogueStateTracker.py index 0eddd49..aad08e8 100644 --- a/DialogueStateTracker.py +++ b/DialogueStateTracker.py @@ -4,12 +4,6 @@ from UserAct import UserAct class DST: - """ - Moduł odpowiedzialny za śledzenie stanu dialogu. Przechowuje informacje o tym jakie dane zostały uzyskane od użytkownika w toku prowadzonej konwersacji. - Wejście: Akt użytkownika (rama) - Wyjście: Reprezentacja stanu dialogu (rama) - """ - def __init__(self): self.state = None self.last_user_act = None diff --git a/NaturalLanguageGeneration.py b/NaturalLanguageGeneration.py index cbe8f1c..1212339 100644 --- a/NaturalLanguageGeneration.py +++ b/NaturalLanguageGeneration.py @@ -1,154 +1,144 @@ from SystemAct import SystemAct from SystemActType import SystemActType from UserActType import UserActType +import random class NLG: - """ - Moduł, który tworzy reprezentację tekstową aktu systemowego wybranego przez taktykę dialogu. - Wejście: Akt systemu (rama) - Wyjście: Tekst - """ - def __init__(self, dst): self.DST = dst - def generateResponse(self, systemAct: SystemAct) -> str: - dialogue_state, last_user_act, _ = self.DST.get_dialogue_state() - slots = self.DST.get_dialogue_slots() + def format_meeting(self, date, time, place, participants, description, format_type): + if participants: + participants = ', '.join(participants) + if format_type == 'create': + return f'Data: {date}\nCzas: {time}\nMiejsce: {place}\nUczestnicy: {participants}\nOpis: {description}'.replace('None', 'BRAK') + elif format_type == 'update': + return f'Data: {date}\nCzas: {time}\nMiejsce: {place}\nUczestnicy: {participants}\nOpis: {description}'.replace('None', 'BEZ ZMIAN') + else: + return f'Data: {date}\nCzas: {time}\nMiejsce: {place}\nUczestnicy: {participants}\nOpis: {description}' + + def generate_response(self, systemAct: SystemAct) -> str: + dialogue_state, _, _ = self.DST.get_dialogue_state() + print(f'Stacja dialogowa: {dialogue_state}') if dialogue_state == UserActType.CREATE_MEETING: if systemAct.getActType() == SystemActType.REQUEST: - if "date" in systemAct.getActParams(): - return "W jakim dniu ma się odbyć to spotkanie?" - if "time" in systemAct.getActParams(): - return "W jakim czasie ma się odbyć to spotkanie?" - if "place" in systemAct.getActParams(): - return "Gdzie ma się odbyć to spotkanie?" - if "description" in systemAct.getActParams(): - return "Czy mam dodać jakiś opis do tego spotkania?" - if "participants" in systemAct.getActParams(): - return "Kto ma wziąć udział w spotkaniu?" - if systemAct.getActType() == SystemActType.CONFIRM_DOMAIN: - date = slots['date'] - time = slots['time'] - place = slots['place'] - part_list = slots['participants'] - part = "" - for p in part_list: - part += p - part += ", " - part = part[:-2] - desc = slots['description'] - return f'Czy mam dodać te spotkanie do kalendarza?\n' \ - f'Dzień: {date}\nCzas: {time}\nMiejsce: {place}\nUczestnicy: {part}\nOpis: {desc}' + if 'date' in systemAct.getActParams(): + return random.choice(['W jakim dniu ma się odbyć to spotkanie?', 'Jakiego dnia odbędzie się to spotkanie?']) + elif 'time' in systemAct.getActParams(): + return random.choice(['W jakim czasie ma się zacząć to spotkanie?', 'O której godzinie rozpoczyna się to spotkanie?']) + elif 'place' in systemAct.getActParams(): + return random.choice(['W jakim miejscu odbywać się będzie to spotkanie?', 'Gdzie ma się odbyć to spotkanie?']) + elif 'description' in systemAct.getActParams(): + return random.choice(['Czy mam dodać jakiś opis do tego spotkania?', 'Czy to spotkanie ma posiadać jakiś opis?']) + elif 'participants' in systemAct.getActParams(): + return random.choice(['Kto będzie brał udział w spotkaniu?', 'Kto będzie uczestnikiem tego spotkania?']) + elif systemAct.getActType() == SystemActType.CONFIRM_DOMAIN: + slots = systemAct.getActParams() + meeting = self.format_meeting(slots['date'], slots['time'], slots['place'], slots['participants'], slots['description'], 'create') + return random.choice([f'Czy mam dodać te spotkanie do kalendarza?\n{meeting}', f'Czy chcesz, abym dodał to spotkanie do kalendarza?\n{meeting}']) - # TODO: nie sprawdzone - trudno wejść do tego stanu elif dialogue_state == UserActType.UPDATE_MEETING: if systemAct.getActType() == SystemActType.REQUEST: - if "date" in systemAct.getActParams(): - return "W jakim dniu miało się odbyć to spotkanie?" - if "time" in systemAct.getActParams(): - return "W jakim czasie miało się odbyć to spotkanie?" - # TODO dopracować po dodaniu DB + if False in systemAct.getActParams(): + if 'date' in systemAct.getActParams(): + return random.choice(['W jakim dniu odbywa się spotkanie, które chcesz edytować?', 'Podaj datę spotkania, które chcesz zmienić.']) + elif 'time' in systemAct.getActParams(): + return random.choice(['O której godzinie zaczyna się spotkanie, które chcesz zmienić?', 'Podaj godzinę spotkania, które chcesz edytować.']) + elif True in systemAct.getActParams(): + if 'date' in systemAct.getActParams(): + slot_name = 'datę' + slot_question = 'podaj nową datę.' + elif 'time' in systemAct.getActParams(): + slot_name = 'godzinę' + slot_question = 'podaj nową godzinę rozpoczęcia tego spotkania.' + elif 'place' in systemAct.getActParams(): + slot_name = 'miejsce' + slot_question = 'podaj nowe miejsce, w jakim odbywa się to spotkanie.' + elif 'description' in systemAct.getActParams(): + slot_name = 'opis' + slot_question = 'podaj nowy opis tego spotkania.' + else: + slot_name = 'uczestników' + slot_question = 'podaj kim będą nowi uczestnicy tego spotkania.' + return f'Czy chcesz zmienić {slot_name} tego spotkania? Jeśli tak, to {slot_question}' if systemAct.getActType() == SystemActType.CONFIRM_DOMAIN: - date = slots['date'] - time = slots['time'] - place = slots['place'] - part_list = slots['participants'] - part = "" - for p in part_list: - part += p - part += ", " - part = part[:-2] - desc = slots['description'] - return f'Spotkanie:\n' \ - f'Dzień: {date}\nCzas: {time}\nMiejsce: {place}\nUczestnicy: {part}\nOpis: {desc}' + if 'meeting_to_update' in systemAct.getActParams(): + response = random.choice([f'Czy to jest spotkanie które chcesz edytować?', f'Czy chcesz wprowadzić zmiany do następującego spotkania?']) + # TODO: pokazać prawdziwe spotkanie po ew. dodaniu DB + meeting = self.format_meeting('24.06.2021', '10:00', 'Kawiarnia Portowa', ['Andrzej Duda', 'Aleksander Kwaśniewski'], 'Spotkanie biznesowe w sprawie tarczy antyrakietowej', None) + return f'{response}\n{meeting}' + else: + slots = systemAct.getActParams() + meeting = self.format_meeting(slots['date'], slots['time'], slots['place'], slots['participants'], slots['description'], 'update') + return random.choice([f'Czy chcesz wprowadzić następujące zmiany do tego spotkania?\n{meeting}', f'Czy potwierdzasz następujące zmiany w tym spotkaniu?\n{meeting}']) elif dialogue_state == UserActType.CANCEL_MEETING: if systemAct.getActType() == SystemActType.REQUEST: - if "date" in systemAct.getActParams(): - return "W jakim dniu miało się odbyć to spotkanie?" - if "time" in systemAct.getActParams(): - return "W jakim czasie miało się odbyć to spotkanie?" + if 'date' in systemAct.getActParams(): + return random.choice(['W jakim dniu odbywa się spotkanie, które chcesz anulować?', 'Podaj datę spotkania, które chcesz usunąć z kalendarza.']) + elif 'time' in systemAct.getActParams(): + return random.choice(['O której godzinie zaczyna się spotkanie, które chcesz usunąć z kalendarza?', 'Podaj godzinę spotkania, które chcesz anulować.']) # TODO dopracować po dodaniu DB if systemAct.getActType() == SystemActType.CONFIRM_DOMAIN: - date = slots['date'] - time = slots['time'] - # place = slots['place'] - # part_list = slots['participants'] - # part = "" - # for p in part_list: - # part += p - # part += ", " - # part = part[:-2] - # desc = slots['description'] - return f'Spotkanie:\n' \ - f'Dzień: {date}\nCzas: {time}' + response = random.choice([f'Czy na pewno chcesz anulować następujące spotkanie?', f'Czy potwierdzasz usunięcie następującego spotkania?']) + # TODO: pokazać prawdziwe spotkanie po ew. dodaniu DB + meeting = self.format_meeting('24.06.2021', '10:00', 'Kawiarnia Portowa', ['Andrzej Duda', 'Aleksander Kwaśniewski'], 'Spotkanie biznesowe w sprawie tarczy antyrakietowej', None) + return f'{response}\n{meeting}' elif dialogue_state == UserActType.MEETING_LIST: if systemAct.getActType() == SystemActType.REQUEST: if "date" in systemAct.getActParams(): - return "Z jakiego okresu chcesz przejrzeć spotkania?" - # TODO: dopracować po dodaniu DB - if systemAct.getActType() == SystemActType.MEETING_LIST: - response = "" - for s in slots: - date = s['date'] - time = s['time'] - place = s['place'] - part_list = s['participants'] - part = "" - for p in part_list: - part += p - part += ", " - part = part[:-2] - desc = s['description'] - response += f'Spotkanie:\nDzień: {date}\nCzas: {time}\nMiejsce: {place}\nUczestnicy: {part}\nOpis: {desc}\n' - response += "--------------------" - return response + return random.choice(['Z jakiego dnia chcesz przejrzeć spotkania?', 'Spotkania z jakiego dnia chciałbyś zobaczyć?']) + elif systemAct.getActType() == SystemActType.MEETING_LIST: + date = systemAct.getActParams()['date'] + response = random.choice([f'Dnia {date} masz zaplanowane następujące spotkania:', f'W dniu {date} odbywają się następujące spotkania:']) + # TODO: pokazać prawdziwe spotkania po ew. dodaniu DB + meetings = self.format_meeting(date, '10:00', 'Kawiarnia Portowa', ['Andrzej Duda', 'Aleksander Kwaśniewski'], 'Spotkanie biznesowe w sprawie tarczy antyrakietowej', None) + self.DST.clear() + return f'{response}\n{meetings}' elif dialogue_state == UserActType.FREE_TIME: if systemAct.getActType() == SystemActType.REQUEST: - if "date" in systemAct.getActParams(): - return "W jakim okresie chcesz znaleźć wolny czas?" - # TODO: dopracować po dodaniu DB - if systemAct.getActType() == SystemActType.FREE_TIME: - response = "" - for s in slots: - date = s['date'] - time = s['time'] - response += f'Spotkanie:\nDzień: {date}\nCzas: {time}\n' - return response - + if 'date' in systemAct.getActParams(): + return random.choice(['Z jakiego dnia chcesz zobaczyć wolne godziny?', 'Z jakiego dnia chciałbyś zobaczyć godziny, w których nie masz spotkań?']) + elif systemAct.getActType() == SystemActType.FREE_TIME: + date = systemAct.getActParams()['date'] + response = random.choice([f'W następujących godzinach, dnia {date} nie masz zaplanowanych spotkań:', f'W dniu {date} następujące godziny są wolne od spotkań:']) + # TODO: pokazać prawdziwe godziny po ew. dodaniu DB + meeting_hours = '00:00-08:00\n10:00-16:00\n18:00-24:00' + self.DST.clear() + return f'{response}\n{meeting_hours}' + elif systemAct.getActType() == SystemActType.AFFIRM: - if "create_meeting" in systemAct.getActParams(): - return "Spotkanie zostało dodane" if "update_meeting" in systemAct.getActParams(): - return "Spotkanie zostało zaktualizowane" - if "cancel_meeting" in systemAct.getActParams(): - return "Spotkanie zostało odwołane" - + task_type_1 = 'zaktualizowane' + task_type_2 = 'Zaktualizowanie' + elif "cancel_meeting" in systemAct.getActParams(): + task_type_1 = 'odwołane' + task_type_2 = 'Odwołanie' + else: + task_type_1 = 'dodane' + task_type_2 = 'Dodanie' + return random.choice([f'Spotkanie zostało pomyślnie {task_type_1}.', f'{task_type_2} spotkania przebiegło pomyślnie.']) + elif systemAct.getActType() == SystemActType.REQMORE: if "create_meeting" in systemAct.getActParams(): - return "Spotkanie zostało odrzucone. Mogę pomóc w czymś jeszcze?" - if "update_meeting" in systemAct.getActParams(): - return "Aktualizacja spotkania została anulowana. Mogę pomóc w czymś jeszcze?" - if "cancel_meeting" in systemAct.getActParams(): - return "Odwoływanie spotkania zostało anulowane. Mogę pomóc w czymś jeszcze?" - if "meeting_list" in systemAct.getActParams() or "free_time" in systemAct.getActParams(): - return "Mogę pomóc w czymś jeszcze?" - + response = 'Tworzenie spotkania zostało przerwane. ' + elif "update_meeting" in systemAct.getActParams(): + response = 'Aktualizacja spotkania została przerwana. ' + elif "cancel_meeting" in systemAct.getActParams(): + response = 'Odwoływanie spotkania zostało przerwane. ' + else: + response = '' + return random.choice([f'{response}Czy mogę Ci w czymś jeszcze pomóc?', f'{response}Czy jest jeszcze coś co mogę dla Ciebie zrobić?']) + else: if systemAct.getActType() == SystemActType.WELCOME_MSG: - return "Cześć" - if systemAct.getActType() == SystemActType.REQMORE: - return "Czy mogę Ci w czymś jeszcze pomóc?" - if systemAct.getActType() == SystemActType.BYE: - return "Do widzenia." - if systemAct.getActType() == SystemActType.NOT_UNDERSTOOD: - return "Nie rozumiem o czym mówisz." - - # TODO: Not implemented in DP - # if systemAct.getActType() == SystemActType.INFORM: - # if "name" in systemAct.getActParams(): - # return "Nazywam się Janusz" + introduction = 'Nazywam się Janusz i jestem twoim asystentem kalendarza spotkań.' + return random.choice([f'Cześć. {introduction}', f'Dzień dobry. {introduction}', f'Witam. {introduction}']) + elif systemAct.getActType() == SystemActType.BYE: + return random.choice(['Do widzenia.', 'Miłego dnia.']) + elif systemAct.getActType() == SystemActType.NOT_UNDERSTOOD: + try_again = 'Spróbuj swoją wypowiedź sformułować w inny sposób.' + return random.choice([f'Nie rozumiem o czym mówisz. {try_again}', f'Nie zrozumiałem twojej ostatniej wypowiedzi. {try_again}', f'Twoja ostatnia prośba była dla mnie nie zrozumiała. {try_again}']) raise Exception("SystemAct:{} not recognized".format(systemAct)) diff --git a/data/train.conllu b/data/train.conllu index 26d8ff0..2221bfd 100644 --- a/data/train.conllu +++ b/data/train.conllu @@ -23,7 +23,7 @@ # text: cześć # intent: hello # slots: -1 siema hello NoLabel +1 cześć hello NoLabel # text: do widzenia # intent: bye @@ -484,6 +484,18 @@ 7 prac inform/description I-description 8 projektowych inform/description I-description +# text: Proszę jako opis dodać spotkanie dotyczące zakupu firmy +# intent: inform/description +# slots: +1 Proszę inform/description NoLabel +2 jako inform/description NoLabel +3 opis inform/description NoLabel +4 dodać inform/description NoLabel +5 spotkanie inform/description B-description +6 dotyczące inform/description I-description +7 zakupu inform/description I-description +8 firmy inform/description I-description + # text: Spotkanie będzie dotyczyło omówienia strategii biznesowej # intent: inform/description # slots: @@ -641,6 +653,22 @@ 10 salka meeting/create B-place 11 A meeting/create I-place +# text: proszę ustawić spotkanie z Jan Nowak 24 grudnia 2022 o godzinie 21.30 +# intent: meeting/create +# slots: +1 proszę meeting/create NoLabel +2 ustawić meeting/create NoLabel +3 spotkanie meeting/create NoLabel +4 z meeting/create NoLabel +5 Jan meeting/create B-participant +6 Nowak meeting/create I-participant +7 24 meeting/create B-date +8 grudnia meeting/create I-date +9 2022 meeting/create I-date +10 o meeting/create NoLabel +11 godzinie meeting/create NoLabel +11 21.30 meeting/create B-time + # text: chciałbym umówić spotkanie dzisiaj o godzinie 17.00 # intent: meeting/create # slots: @@ -650,7 +678,7 @@ 4 dzisiaj meeting/create B-date 5 o meeting/create B-date 6 godzinie meeting/create NoLabel -7 17:00 meeting/create B-time +7 17.00 meeting/create B-time # text: zapisz mi proszę spotkanie z 3 marca o 17 wizyta u lekarza # intent: meeting/create diff --git a/main.py b/main.py index 7673aa1..92eba71 100644 --- a/main.py +++ b/main.py @@ -20,7 +20,7 @@ if __name__ == "__main__": dst.user_update(user_frame) state, last_user_act, last_system_act = dst.get_dialogue_state() slots = dst.get_dialogue_slots() - system_act = dp.chooseTactic() + system_act = dp.choose_tactic() print('------ stan ------') print(state, last_user_act, last_system_act) @@ -28,7 +28,7 @@ if __name__ == "__main__": print(slots) print('------ wybrana akcja systemu ------') print(system_act) - system_response = nlg.generateResponse(system_act) + system_response = nlg.generate_response(system_act) print('------ wygenerowana odpowiedź systemu ------') print(system_response) print('-----------------------------------')