From abdfe9d1b3f61969e1f6b5b2c2b2520e8f49f722 Mon Sep 17 00:00:00 2001 From: s464951 Date: Tue, 11 Jun 2024 18:10:51 +0200 Subject: [PATCH] working chatbot --- chatbot/main.py | 18 ++++++++------ chatbot/models/nlu_train2.py | 7 ++++-- chatbot/modules/generator.py | 42 +++++++++++++++++++++++++++----- chatbot/modules/nlu.py | 6 ++--- chatbot/modules/state_monitor.py | 11 +++++---- chatbot/modules/strategy.py | 32 ++++++++++++------------ 6 files changed, 75 insertions(+), 41 deletions(-) diff --git a/chatbot/main.py b/chatbot/main.py index b379079..ee78dc9 100644 --- a/chatbot/main.py +++ b/chatbot/main.py @@ -1,7 +1,8 @@ from pathlib import Path -from modules.nlu import NLU, Slot, UserAct +from modules.nlu import NLU, Slot, Act from modules.state_monitor import DialogStateMonitor from modules.generator import ResponseGenerator +from modules.strategy import DialoguePolicy from modules.config import Config import colorama from colorama import Fore, Style @@ -10,13 +11,15 @@ colorama.init(autoreset=True) def main(): + print(Fore.CYAN + "Starting chatbot. Please wait...") base_path = Path(__file__).resolve().parent config_path = base_path / 'config' / 'config.json' config = Config.load_config(config_path) nlu = NLU() dst = DialogStateMonitor() - generator = ResponseGenerator(config) + dp = DialoguePolicy() + generator = ResponseGenerator() print(Fore.CYAN + "Witaj w chatbocie! Rozpocznij rozmowę.") print(Fore.YELLOW + "Wpisz 'quit' aby zakończyć program.\n") @@ -28,13 +31,12 @@ def main(): break user_act = nlu.analyze(user_input) - # user_act = UserAct(intent='inform', - # slots=[Slot(name='item', value='laptop'), Slot(name='item', value='kot'),Slot(name='address', value='123 Main St')]) - dst.update(user_act) - print(dst.state) - # response = generator.generate(intent) - # print(Fore.CYAN + "Bot: " + response) + dst.update(user_act) + system_action = dp.next_action(dst) + response = generator.nlg(system_action) + + print(Fore.CYAN + "Bot: " + response) if __name__ == "__main__": diff --git a/chatbot/models/nlu_train2.py b/chatbot/models/nlu_train2.py index a7782dc..87c47d6 100644 --- a/chatbot/models/nlu_train2.py +++ b/chatbot/models/nlu_train2.py @@ -70,6 +70,8 @@ def conllu2flair_slot(sentences, label=None): def predict_frame(model, sentence, label_type): + if not sentence: + return 'unknown' csentence = [{'form': word, 'slot': 'O'} for word in sentence] fsentence = conllu2flair([csentence])[0] model.predict(fsentence) @@ -84,6 +86,8 @@ def predict_frame(model, sentence, label_type): def predict_slot(model, sentence, label_type): + if not sentence: + return {'form': '', 'slot': 'unknown'}, csentence = [{'form': word, 'slot': 'O'} for word in sentence] fsentence = conllu2flair([csentence])[0] model.predict(fsentence) @@ -112,10 +116,9 @@ class Model: trainset = list(parse_incr(f, fields=['id', 'form', 'frame', 'slot'], field_parsers=field_parsers)) with open(self.test_dataset, encoding='utf-8') as f: testset = list(parse_incr(f, fields=['id', 'form', 'frame', 'slot'], field_parsers=field_parsers)) - print('TRAINSET:', trainset) + corpus = Corpus(train=conllu2flair(trainset, label_type), test=conllu2flair(testset, label_type)) label_dictionary = corpus.make_label_dictionary(label_type=label_type) - print('LABEL:' ,label_dictionary) embedding_types = [ WordEmbeddings('pl'), FlairEmbeddings('pl-forward'), diff --git a/chatbot/modules/generator.py b/chatbot/modules/generator.py index b0742b3..6246831 100644 --- a/chatbot/modules/generator.py +++ b/chatbot/modules/generator.py @@ -8,11 +8,41 @@ class ResponseGenerator: def __init__(self, config: Config): with config.responses_path.open('r', encoding='utf-8') as file: self.responses: Dict[str, list] = json.load(file) - self.intent_to_response_key = { - "ask_name": "name_response", - "unknown": "unknown" + + def nlg(self, system_act): + intent = system_act.intent + slot = system_act.slots[0].name if system_act.slots else None + + responses = { + "inform": { + "item": "Nasz sklep oferuje szeroki wybór artykułów, takich jak\n artykuły spożywcze,\n ogrodowe\n oraz kosmetyki. Proszę podaj produkt.", + "address": "Nasz sklep nie ma fizycznego adresu. To sklep internetowy.", + "delivery_method": "Dostępne formy dostawy: INPOST, DPD, DHL.", + "payment_method": "Dostępne formy płatności: Karta, przy odbiorze", + "email": "Obsługa klienta: cs2137@gmail.com", + "card_nr": random.choice([ + "Dzięki karcie rabatowej zbierasz punkty, które poźniej przekładają się na rabat.", + "Dzisiaj z kartą rabatową meble ogrodowe 200zł taniej!", + "Dzisiaj z kartą rabatową szampony 2 w cenie 1!" + ]) + }, + "canthelp": { + "unknown": random.choice([ + "Przepraszam, nie rozumiem polecenia...", + "Możesz powtórzyć?", + "Powiedz proszę jeszcze raz.." + ]) + }, + "request": { + "card_nr": "Podaj proszę numer karty rabatowej", + "address": "Podaj proszę adres do wysyłki", + "item": "Jaki produkt chcesz kupić?", + "email": "Podaj proszę email.", + "delivery_method": "Jaką formą dostawy jesteś zainteresowany?", + "payment_method": "Jaką formą płatności jesteś zainteresowany?" + }, + "bye": "Miłego dnia!", + "confirmation": "Zamówienie zostało złożone!" } - def generate(self, response_key: str) -> str: - response_key = self.intent_to_response_key.get(response_key, "unknown") - return random.choice(self.responses.get(response_key, ["Przepraszam, nie rozumiem. Możesz to powtórzyć?"])) + return responses.get(intent, {}).get(slot, "Nieznane zapytanie.") diff --git a/chatbot/modules/nlu.py b/chatbot/modules/nlu.py index 42d4486..6aec590 100644 --- a/chatbot/modules/nlu.py +++ b/chatbot/modules/nlu.py @@ -17,7 +17,7 @@ class Slot: return f"Name: {self.name}, Value: {self.value}" -class UserAct: +class Act: def __init__(self, intent: str, slots: list[Slot] = []): self.slots = slots self.intent = intent @@ -62,6 +62,4 @@ class NLU: def analyze(self, text: str): intent = self.get_intent(text) slots = self.get_slot(text) - print({'intent': intent, - 'slots': slots}) - return UserAct(intent=intent, slots=slots) + return Act(intent=intent, slots=slots) diff --git a/chatbot/modules/state_monitor.py b/chatbot/modules/state_monitor.py index 95a0cb5..987d0d6 100644 --- a/chatbot/modules/state_monitor.py +++ b/chatbot/modules/state_monitor.py @@ -1,5 +1,5 @@ import copy -from modules.nlu import UserAct +from modules.nlu import Act import json @@ -39,11 +39,10 @@ class DialogStateMonitor: def find_first_empty_slot(self): for slot_name, slot_value in self.state['belief_state'].items(): - if slot_name != 'order-completed' and self.is_value_empty(slot_value): + if slot_name != 'order-completed' and slot_value in [None, '', [], {}]: return slot_name - def update(self, act: UserAct) -> None: - print(act) + def update(self, act: Act) -> None: if act.intent == 'inform': self.update_act(act.intent) slots_mapping = { @@ -55,7 +54,7 @@ class DialogStateMonitor: 'email': [] } for slot in act.slots: - if slot.name in slots_mapping and self.is_value_empty(self.state, slot.name): + if slot.name in slots_mapping and self.is_value_empty(self.state['belief_state'], slot.name): slots_mapping[slot.name].append(slot.value) # To do: normalization for slot_name, values in slots_mapping.items(): @@ -67,5 +66,7 @@ class DialogStateMonitor: self.update_slot_names(slots_names) elif act.intent == 'bye': self.update_act(act.intent) + elif act.intent == 'unknown': + self.update_act(act.intent) self.check_order_complete() diff --git a/chatbot/modules/strategy.py b/chatbot/modules/strategy.py index 27a59c2..8824f1d 100644 --- a/chatbot/modules/strategy.py +++ b/chatbot/modules/strategy.py @@ -1,24 +1,24 @@ -from modules.nlu import UserAct, Slot -from numpy.random.mtrand import random +from modules.nlu import Act, Slot +import random class DialoguePolicy: - def __init__(self, dst): - self.dialogue_state = dst.state - def next_action(self): - if not self.dialogue_state['belief_state']['order-complete']: - user_intent = self.dialogue_state['act'] + def next_action(self, dst): + if not dst.state['belief_state']['order-complete']: + user_intent = dst.state['act'] if user_intent == "inform": empty_slot = dst.find_first_empty_slot() - return UserAct(intent="request", slots=[Slot(name=empty_slot, value='')]) - if user_intent == "request": - if self.dialogue_state['slot_names']: - slot = random.choice(self.state['slot_names']) - return UserAct(intent="inform", slots=[Slot(name=slot, value='')]) + return Act(intent="request", slots=[Slot(name=empty_slot, value='')]) + elif user_intent == "request": + if dst.state['slot_names']: + slot = random.choice(dst.state['slot_names']) + return Act(intent="inform", slots=[Slot(name=slot, value='')]) else: - return UserAct(intent="inform", slots=[Slot(name='unknown', value='')]) - if user_intent == "bye": - return UserAct(intent="bye", slots=[Slot(name='', value='')]) + return Act(intent="canthelp", slots=[Slot(name='unknown', value='')]) + elif user_intent == "bye": + return Act(intent="bye", slots=[Slot(name='', value='')]) + else: + return Act(intent="canthelp", slots=[Slot(name='unknown', value='')]) else: - return UserAct(intent= "inform", slots=[Slot(name='confirmation', value='Zamówienie złożono poprawnie.')]) \ No newline at end of file + return Act(intent="inform", slots=[Slot(name='confirmation', value='Zamówienie złożono poprawnie.')]) \ No newline at end of file