working chatbot
This commit is contained in:
parent
253320ce1d
commit
abdfe9d1b3
@ -1,7 +1,8 @@
|
|||||||
from pathlib import Path
|
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.state_monitor import DialogStateMonitor
|
||||||
from modules.generator import ResponseGenerator
|
from modules.generator import ResponseGenerator
|
||||||
|
from modules.strategy import DialoguePolicy
|
||||||
from modules.config import Config
|
from modules.config import Config
|
||||||
import colorama
|
import colorama
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
@ -10,13 +11,15 @@ colorama.init(autoreset=True)
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
print(Fore.CYAN + "Starting chatbot. Please wait...")
|
||||||
base_path = Path(__file__).resolve().parent
|
base_path = Path(__file__).resolve().parent
|
||||||
config_path = base_path / 'config' / 'config.json'
|
config_path = base_path / 'config' / 'config.json'
|
||||||
config = Config.load_config(config_path)
|
config = Config.load_config(config_path)
|
||||||
|
|
||||||
nlu = NLU()
|
nlu = NLU()
|
||||||
dst = DialogStateMonitor()
|
dst = DialogStateMonitor()
|
||||||
generator = ResponseGenerator(config)
|
dp = DialoguePolicy()
|
||||||
|
generator = ResponseGenerator()
|
||||||
|
|
||||||
print(Fore.CYAN + "Witaj w chatbocie! Rozpocznij rozmowę.")
|
print(Fore.CYAN + "Witaj w chatbocie! Rozpocznij rozmowę.")
|
||||||
print(Fore.YELLOW + "Wpisz 'quit' aby zakończyć program.\n")
|
print(Fore.YELLOW + "Wpisz 'quit' aby zakończyć program.\n")
|
||||||
@ -28,13 +31,12 @@ def main():
|
|||||||
break
|
break
|
||||||
|
|
||||||
user_act = nlu.analyze(user_input)
|
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__":
|
if __name__ == "__main__":
|
||||||
|
@ -70,6 +70,8 @@ def conllu2flair_slot(sentences, label=None):
|
|||||||
|
|
||||||
|
|
||||||
def predict_frame(model, sentence, label_type):
|
def predict_frame(model, sentence, label_type):
|
||||||
|
if not sentence:
|
||||||
|
return 'unknown'
|
||||||
csentence = [{'form': word, 'slot': 'O'} for word in sentence]
|
csentence = [{'form': word, 'slot': 'O'} for word in sentence]
|
||||||
fsentence = conllu2flair([csentence])[0]
|
fsentence = conllu2flair([csentence])[0]
|
||||||
model.predict(fsentence)
|
model.predict(fsentence)
|
||||||
@ -84,6 +86,8 @@ def predict_frame(model, sentence, label_type):
|
|||||||
|
|
||||||
|
|
||||||
def predict_slot(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]
|
csentence = [{'form': word, 'slot': 'O'} for word in sentence]
|
||||||
fsentence = conllu2flair([csentence])[0]
|
fsentence = conllu2flair([csentence])[0]
|
||||||
model.predict(fsentence)
|
model.predict(fsentence)
|
||||||
@ -112,10 +116,9 @@ class Model:
|
|||||||
trainset = list(parse_incr(f, fields=['id', 'form', 'frame', 'slot'], field_parsers=field_parsers))
|
trainset = list(parse_incr(f, fields=['id', 'form', 'frame', 'slot'], field_parsers=field_parsers))
|
||||||
with open(self.test_dataset, encoding='utf-8') as f:
|
with open(self.test_dataset, encoding='utf-8') as f:
|
||||||
testset = list(parse_incr(f, fields=['id', 'form', 'frame', 'slot'], field_parsers=field_parsers))
|
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))
|
corpus = Corpus(train=conllu2flair(trainset, label_type), test=conllu2flair(testset, label_type))
|
||||||
label_dictionary = corpus.make_label_dictionary(label_type=label_type)
|
label_dictionary = corpus.make_label_dictionary(label_type=label_type)
|
||||||
print('LABEL:' ,label_dictionary)
|
|
||||||
embedding_types = [
|
embedding_types = [
|
||||||
WordEmbeddings('pl'),
|
WordEmbeddings('pl'),
|
||||||
FlairEmbeddings('pl-forward'),
|
FlairEmbeddings('pl-forward'),
|
||||||
|
@ -8,11 +8,41 @@ class ResponseGenerator:
|
|||||||
def __init__(self, config: Config):
|
def __init__(self, config: Config):
|
||||||
with config.responses_path.open('r', encoding='utf-8') as file:
|
with config.responses_path.open('r', encoding='utf-8') as file:
|
||||||
self.responses: Dict[str, list] = json.load(file)
|
self.responses: Dict[str, list] = json.load(file)
|
||||||
self.intent_to_response_key = {
|
|
||||||
"ask_name": "name_response",
|
def nlg(self, system_act):
|
||||||
"unknown": "unknown"
|
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:
|
return responses.get(intent, {}).get(slot, "Nieznane zapytanie.")
|
||||||
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ć?"]))
|
|
||||||
|
@ -17,7 +17,7 @@ class Slot:
|
|||||||
return f"Name: {self.name}, Value: {self.value}"
|
return f"Name: {self.name}, Value: {self.value}"
|
||||||
|
|
||||||
|
|
||||||
class UserAct:
|
class Act:
|
||||||
def __init__(self, intent: str, slots: list[Slot] = []):
|
def __init__(self, intent: str, slots: list[Slot] = []):
|
||||||
self.slots = slots
|
self.slots = slots
|
||||||
self.intent = intent
|
self.intent = intent
|
||||||
@ -62,6 +62,4 @@ class NLU:
|
|||||||
def analyze(self, text: str):
|
def analyze(self, text: str):
|
||||||
intent = self.get_intent(text)
|
intent = self.get_intent(text)
|
||||||
slots = self.get_slot(text)
|
slots = self.get_slot(text)
|
||||||
print({'intent': intent,
|
return Act(intent=intent, slots=slots)
|
||||||
'slots': slots})
|
|
||||||
return UserAct(intent=intent, slots=slots)
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import copy
|
import copy
|
||||||
from modules.nlu import UserAct
|
from modules.nlu import Act
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@ -39,11 +39,10 @@ class DialogStateMonitor:
|
|||||||
|
|
||||||
def find_first_empty_slot(self):
|
def find_first_empty_slot(self):
|
||||||
for slot_name, slot_value in self.state['belief_state'].items():
|
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
|
return slot_name
|
||||||
|
|
||||||
def update(self, act: UserAct) -> None:
|
def update(self, act: Act) -> None:
|
||||||
print(act)
|
|
||||||
if act.intent == 'inform':
|
if act.intent == 'inform':
|
||||||
self.update_act(act.intent)
|
self.update_act(act.intent)
|
||||||
slots_mapping = {
|
slots_mapping = {
|
||||||
@ -55,7 +54,7 @@ class DialogStateMonitor:
|
|||||||
'email': []
|
'email': []
|
||||||
}
|
}
|
||||||
for slot in act.slots:
|
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
|
slots_mapping[slot.name].append(slot.value) # To do: normalization
|
||||||
|
|
||||||
for slot_name, values in slots_mapping.items():
|
for slot_name, values in slots_mapping.items():
|
||||||
@ -67,5 +66,7 @@ class DialogStateMonitor:
|
|||||||
self.update_slot_names(slots_names)
|
self.update_slot_names(slots_names)
|
||||||
elif act.intent == 'bye':
|
elif act.intent == 'bye':
|
||||||
self.update_act(act.intent)
|
self.update_act(act.intent)
|
||||||
|
elif act.intent == 'unknown':
|
||||||
|
self.update_act(act.intent)
|
||||||
|
|
||||||
self.check_order_complete()
|
self.check_order_complete()
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
from modules.nlu import UserAct, Slot
|
from modules.nlu import Act, Slot
|
||||||
from numpy.random.mtrand import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
class DialoguePolicy:
|
class DialoguePolicy:
|
||||||
def __init__(self, dst):
|
|
||||||
self.dialogue_state = dst.state
|
|
||||||
|
|
||||||
def next_action(self):
|
def next_action(self, dst):
|
||||||
if not self.dialogue_state['belief_state']['order-complete']:
|
if not dst.state['belief_state']['order-complete']:
|
||||||
user_intent = self.dialogue_state['act']
|
user_intent = dst.state['act']
|
||||||
if user_intent == "inform":
|
if user_intent == "inform":
|
||||||
empty_slot = dst.find_first_empty_slot()
|
empty_slot = dst.find_first_empty_slot()
|
||||||
return UserAct(intent="request", slots=[Slot(name=empty_slot, value='')])
|
return Act(intent="request", slots=[Slot(name=empty_slot, value='')])
|
||||||
if user_intent == "request":
|
elif user_intent == "request":
|
||||||
if self.dialogue_state['slot_names']:
|
if dst.state['slot_names']:
|
||||||
slot = random.choice(self.state['slot_names'])
|
slot = random.choice(dst.state['slot_names'])
|
||||||
return UserAct(intent="inform", slots=[Slot(name=slot, value='')])
|
return Act(intent="inform", slots=[Slot(name=slot, value='')])
|
||||||
else:
|
else:
|
||||||
return UserAct(intent="inform", slots=[Slot(name='unknown', value='')])
|
return Act(intent="canthelp", slots=[Slot(name='unknown', value='')])
|
||||||
if user_intent == "bye":
|
elif user_intent == "bye":
|
||||||
return UserAct(intent="bye", slots=[Slot(name='', value='')])
|
return Act(intent="bye", slots=[Slot(name='', value='')])
|
||||||
|
else:
|
||||||
|
return Act(intent="canthelp", slots=[Slot(name='unknown', value='')])
|
||||||
else:
|
else:
|
||||||
return UserAct(intent= "inform", slots=[Slot(name='confirmation', value='Zamówienie złożono poprawnie.')])
|
return Act(intent="inform", slots=[Slot(name='confirmation', value='Zamówienie złożono poprawnie.')])
|
Loading…
Reference in New Issue
Block a user