Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
d4100eb063 | |||
![]() |
777e6fa497 | ||
![]() |
de703d016c | ||
![]() |
22b61e0dd7 | ||
![]() |
d63f926b49 | ||
![]() |
5bf25d0b2b | ||
![]() |
cdd9ce17e9 | ||
![]() |
18858dddc8 | ||
![]() |
283f26a164 | ||
![]() |
be761cfb54 | ||
6cd5dcad5c | |||
e9e8eae277 | |||
60dbfcd478 | |||
258d8b3be0 | |||
b0e4a5bb2e | |||
1cce41a8c6 | |||
0595bba4d8 | |||
2f0d3350e9 | |||
e3cfaf2573 | |||
00c4ee5c5f | |||
240ba12397 | |||
b6411ae7d0 | |||
c11e6ea284 | |||
1b2f1f6dc3 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.idea/
|
||||
__pycache__/
|
18
SystemActType.py
Normal file
18
SystemActType.py
Normal file
@ -0,0 +1,18 @@
|
||||
SystemActType = dict(
|
||||
affirm='affirm',
|
||||
bye='bye',
|
||||
canthear='canthear',
|
||||
confirm_domain='confirm-domain',
|
||||
negate='negate',
|
||||
repeat='repeat',
|
||||
reqmore='reqmore',
|
||||
welcomemsg='welcomemsg',
|
||||
canthelp='canthelp',
|
||||
canthelp_missing_slot_value='canthelp.missing_slot_value',
|
||||
expl_conf='expl-conf',
|
||||
impl_conf='inform-conf',
|
||||
inform='infomr',
|
||||
offer='offer',
|
||||
request='request',
|
||||
select='select'
|
||||
)
|
23
UserActType.py
Normal file
23
UserActType.py
Normal file
@ -0,0 +1,23 @@
|
||||
UserActType = dict(
|
||||
ack='ack',
|
||||
affirm='affirm',
|
||||
bye='bye',
|
||||
hello='hello',
|
||||
help='help',
|
||||
negate='negate',
|
||||
null='null',
|
||||
repeat='repeat',
|
||||
requalts='requalts',
|
||||
reqmore='reqmore',
|
||||
restart='restart',
|
||||
silence='silence',
|
||||
thankyou='thankyou',
|
||||
confirm='confirm',
|
||||
deny='deny',
|
||||
inform='inform',
|
||||
request='request',
|
||||
order='order',
|
||||
delivery='delivery',
|
||||
payment='payment',
|
||||
price='price'
|
||||
)
|
39
chatbot.py
39
chatbot.py
@ -1,39 +0,0 @@
|
||||
from nltk.chat.util import Chat
|
||||
|
||||
reflections = {"": ""}
|
||||
quit_msg = "do widzenia"
|
||||
pairs = [
|
||||
[r"mam na imię (.*)", ["cześć %1.",],],
|
||||
[r"co tam u ciebie?", ["dobrze, rozmawiam z ciekawą osobą.","źle, serwer nie wyrabia","może być."]],
|
||||
[r"chcesz coś powiedzieć?", ["mam sekret... lubię rozmawiać :).","dobrze mi się rozmawia."]],
|
||||
[r"jesteś (.*)", ["Dlaczego uważasz, że jestem %1?", "Uważam, że ty też jesteś %1"]],
|
||||
[r"nie jestem (.*)", ["Dlaczego nie jesteś %1?", "Na pewno nie jesteś %1?"]],
|
||||
[r'jestem (.*)', ["Jak długo byłeś %1?", "Jak się czujesz będąc %1?", "Dlaczego twierdzisz, że jesteś %1?"]],
|
||||
[r"czy potrafisz (.*)\?", ["Czemu pytasz czy potrafię %1?", "A co jeśli bym potrafiła?"]],
|
||||
[r"potrzebuję (.*)", ["Czy na pewno tego właśnie potrzebujesz?", "Na pewno by Ci to pomogło?"]],
|
||||
[r"(.*)\?", ["Czemu o to pytasz?", "Co masz na myśli?", "Chyba nie jestem w stanie na to odpowiedzieć."]],
|
||||
[f"{quit_msg}", ["żegnaj.", "Dziękuję, to będzie 150 złotych. Miłego dnia!",],],
|
||||
[r'mój (.*)', [ "Twój %1, rozumiem.", "Dlaczego twierdzisz, że twój %1?"]],
|
||||
[r'jak (.*)', [ "Dlaczego pytasz?", "W jaki sposób odpowiedź na to pomoże?", "Co myślisz?"]],
|
||||
[r'ponieważ (.*)', [ "Czy to jest prawdziwy powód?", "Jakie inne powody przychodzą Ci do głowy?", "Czy ten powód dotyczy czegoś innego?"]],
|
||||
[r'myślę (.*)', [ "Czy wątpisz %1?", "Czy naprawdę tak sądzisz?", "Ale nie jesteś pewny %1?"]],
|
||||
[r'tak', [ "Wydajesz się być całkiem pewny.", "Czy mógłbyś powiedzieć coś więcej?"]],
|
||||
[r'czy to (.*)', [ "Czy myślisz, że to %1?", "Może to %1 - co myślisz?", "Gdyby to było %1, co byś zrobił?"]],
|
||||
[r'czy możesz (.*)', [ "Co sprawia, że myślisz, że nie mogę %1?", "Gdybym mogła %1, co wtedy?", "Dlaczego pytasz czy mogę %1?"]],
|
||||
[r'czy mogę*)', [ "Być może nie chcesz %1.", "Czy chcesz móc %1?", "Gdybyś mógł %1, zrobiłbyś to?"]],
|
||||
[r'czuję (.*)', [ "Dobrze, opowiedz mi więcej o tych uczuciach.", "Czy często czujesz %1?", "Kiedy zwykle czujesz %1?", "Kiedy czujesz %1, co robisz?"]],
|
||||
[r'mam (.*)',[ "Dlaczego mi mówisz, że masz %1?", "Teraz, gdy masz %1, co zrobisz dalej?"]],
|
||||
[r'nie dostałem(.*)',[ "Przykro nam, że w Twoim zamówieniu zabrakło %1", "Podaj numer reklamowanego zamówienia z brakuącym %1"]],
|
||||
]
|
||||
|
||||
|
||||
def grazyna_chat():
|
||||
print(f' cześć!\n',
|
||||
f'mam na imię Grażyna. Jestem botem zaprogramowanym do rozmowy w języku polskim.\n',
|
||||
f'w trakcie rozmowy proszę używaj polskich znaków.')
|
||||
chat = Chat(pairs=pairs, reflections=reflections)
|
||||
chat.converse(quit=quit_msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
grazyna_chat()
|
27
dp.py
Normal file
27
dp.py
Normal file
@ -0,0 +1,27 @@
|
||||
from SystemActType import SystemActType
|
||||
from UserActType import UserActType
|
||||
|
||||
|
||||
class DP:
|
||||
def update_system_action(self, state, last_user_act, last_system_act, slots):
|
||||
if state == UserActType['order']:
|
||||
if 'kind' not in slots[state]:
|
||||
return {'act': SystemActType['request'], 'slot': 'kind'}
|
||||
elif 'size' not in slots[state]:
|
||||
return {'act': SystemActType['request'], 'slot': 'size'}
|
||||
elif 'plates' not in slots[state]:
|
||||
return {'act': SystemActType['request'], 'slot': 'plates'}
|
||||
else:
|
||||
return {'act': SystemActType['confirm_domain']}
|
||||
|
||||
else:
|
||||
if last_user_act == UserActType['hello']:
|
||||
return {'act': SystemActType['welcomemsg']}
|
||||
elif last_user_act == UserActType['bye']:
|
||||
return {'act': SystemActType['bye']}
|
||||
else:
|
||||
return {'act': SystemActType['canthelp']}
|
||||
|
||||
|
||||
|
||||
|
51
dst.py
Normal file
51
dst.py
Normal file
@ -0,0 +1,51 @@
|
||||
from UserActType import UserActType
|
||||
|
||||
class DST:
|
||||
def __init__(self):
|
||||
self.state = None
|
||||
self.last_user_act = None
|
||||
self.last_system_act = None
|
||||
self.slots = self.init_slots()
|
||||
|
||||
def update(self, user_act=None):
|
||||
act = user_act['act']
|
||||
self.last_user_act = act
|
||||
if not self.state:
|
||||
if act in [UserActType['order'],
|
||||
UserActType['delivery'],
|
||||
UserActType['payment'],
|
||||
UserActType['price']]:
|
||||
self.state = act
|
||||
|
||||
for slot, value in user_act['slots']:
|
||||
slot = slot.lower()
|
||||
value = value.lower()
|
||||
|
||||
self.slots[act][slot] = value
|
||||
|
||||
return self.state
|
||||
|
||||
def get_dialogue_state_tracker_state(self):
|
||||
return self.state, self.last_user_act, self.last_system_act, self.slots
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def get_last_user_act(self):
|
||||
return self.last_user_act
|
||||
|
||||
def get_last_system_act(self):
|
||||
return self.last_system_act
|
||||
|
||||
def get_slots(self):
|
||||
return self.slots
|
||||
|
||||
def update_last_user_act(self, new_user_act):
|
||||
self.last_user_act = new_user_act
|
||||
|
||||
def update_last_system_act(self, new_system_act):
|
||||
self.last_system_act = new_system_act
|
||||
|
||||
def init_slots(self):
|
||||
return dict(order={}, delivery={}, payment={}, price={})
|
||||
|
66
evaluate.py
Normal file
66
evaluate.py
Normal file
@ -0,0 +1,66 @@
|
||||
import jsgf
|
||||
from os import listdir
|
||||
from os.path import isfile, join
|
||||
|
||||
|
||||
gram_dir = './gramatyki/'
|
||||
grammar_files = [file for file in listdir(gram_dir) if isfile(join(gram_dir, file))]
|
||||
|
||||
grammars = []
|
||||
|
||||
for grammarFile in grammar_files:
|
||||
grammar = jsgf.parse_grammar_file(gram_dir + grammarFile)
|
||||
grammars.append(grammar)
|
||||
|
||||
lines = []
|
||||
|
||||
data_dir = './data/'
|
||||
data_files = [file for file in listdir(data_dir) if isfile(join(data_dir, file))]
|
||||
|
||||
detected = 0
|
||||
|
||||
for file in data_files:
|
||||
f = open(f'{data_dir}{file}', "r")
|
||||
for line in f:
|
||||
sep = line.split('\t')
|
||||
if sep[0] == 'user':
|
||||
lines.append([sep[1], sep[2]])
|
||||
|
||||
|
||||
def get_dialog_act(rule):
|
||||
slots = []
|
||||
get_slots(rule.expansion, slots)
|
||||
global detected
|
||||
detected = detected + 1
|
||||
return {'act': rule.grammar.name, 'slots': slots}
|
||||
|
||||
|
||||
def get_slots(expansion, slots):
|
||||
if expansion.tag != '':
|
||||
slots.append((expansion.tag, expansion.current_match))
|
||||
return
|
||||
|
||||
for child in expansion.children:
|
||||
get_slots(child, slots)
|
||||
|
||||
if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef):
|
||||
get_slots(expansion.referenced_rule.expansion, slots)
|
||||
|
||||
|
||||
def nlu(utterance):
|
||||
matched = None
|
||||
for _grammar in grammars:
|
||||
matched = _grammar.find_matching_rules(utterance)
|
||||
if matched:
|
||||
break
|
||||
|
||||
if matched:
|
||||
return get_dialog_act(matched[0])
|
||||
else:
|
||||
return {'act': 'null', 'slots': []}
|
||||
|
||||
|
||||
for line in lines:
|
||||
nlu(line[0])
|
||||
|
||||
print(f'{round((detected/len(lines)*100),2)}%')
|
8
evaluation.md
Normal file
8
evaluation.md
Normal file
@ -0,0 +1,8 @@
|
||||
## WNIOSKI
|
||||
|
||||
- System był zrozumiały dla większości użytkowników
|
||||
- Tempo reakcji było bardziej niż zadowalające według większości badanych
|
||||
- Trzeba rozszerzyć system gramatyk, gdyż nie rozpoznaje on dostatecznie często wypowiedzi użytkowników
|
||||
- Wśród badanych panuje duża niezgoda, czy system zachowuje się tak, jak oczekiwano, warto przeredagować odpowiedzi systemu by były bardziej zrozumiałe dla użytkownika
|
||||
- Część użytkowników nie wiedziała co powiedzieć na każdym etapie dialogu, ponownie należy rozwinąć system gramatyk oraz przeredagować wypowiedzi systemu by bardziej nakierowywały użytkowników.
|
||||
- Wśród badanych zauważalny był odzew negatywny, należy wprowadzić wspomniane poprawki do systemu i ponownie przeprowadzić ewaluację by zobaczyć nowy stan odczuć użytkowników w stosunku do chęci korzystania z systemu.
|
8
gramatyki/delivery.jsgf
Normal file
8
gramatyki/delivery.jsgf
Normal file
@ -0,0 +1,8 @@
|
||||
#JSGF V1.0 UTF-8 pl;
|
||||
|
||||
grammar delivery;
|
||||
|
||||
|
||||
<deliverys> = <droga> ;
|
||||
|
||||
public <droga> = (na | <NULL>) (dowoz | miejscu | wynos);
|
8
gramatyki/hello.jsgf
Normal file
8
gramatyki/hello.jsgf
Normal file
@ -0,0 +1,8 @@
|
||||
#JSGF V1.0 UTF-8 pl;
|
||||
|
||||
grammar hello;
|
||||
|
||||
|
||||
<hello> = <greeting> ;
|
||||
|
||||
public <greeting> = (dzień dobry | cześć | siema | witam);
|
13
gramatyki/order.jsgf
Normal file
13
gramatyki/order.jsgf
Normal file
@ -0,0 +1,13 @@
|
||||
#JSGF V1.0 UTF-8 pl;
|
||||
|
||||
grammar order;
|
||||
|
||||
public <zamow> = chciałbym zamowic <rodzaj> <rozmiar> <liczba_osob> ;
|
||||
|
||||
<rodzaj> = (pizze | <NULL>) (vesuvio | hawajska | amerykanska | grecka) {kind};
|
||||
|
||||
<rozmiar> = (mala | srednia | duza | XXL) {size};
|
||||
|
||||
<liczba_osob> = (na | dla) <liczba> {plates} (osób | osoby);
|
||||
|
||||
<liczba> = (dwie | dwóch | trzy | trzech | cztery | czterech | pięć | pieciu);
|
8
gramatyki/payment.jsgf
Normal file
8
gramatyki/payment.jsgf
Normal file
@ -0,0 +1,8 @@
|
||||
#JSGF V1.0 UTF-8 pl;
|
||||
|
||||
grammar payment;
|
||||
|
||||
|
||||
<payment> = <platnosc> ;
|
||||
|
||||
public <platnosc> = (płacę | zapłacę | płatność | <NULL>) (kartą | gotówką | online | przy odbiorze);
|
9
gramatyki/price.jsgf
Normal file
9
gramatyki/price.jsgf
Normal file
@ -0,0 +1,9 @@
|
||||
#JSGF V1.0 UTF-8 pl;
|
||||
|
||||
grammar price;
|
||||
|
||||
|
||||
<price> = <cena> ;
|
||||
|
||||
public <cena> = (ile | jaki jest | jaka jest | w jakiej jest | <NULL>) (całkowity | <NULL>)
|
||||
(koszt | kosztuje | cena | cenie) (pizza | pizze | pizzy | zamówienia | <NULL>);
|
38
main.py
Normal file
38
main.py
Normal file
@ -0,0 +1,38 @@
|
||||
from UserActType import UserActType
|
||||
from nlu import nlu
|
||||
from dst import DST
|
||||
from dp import DP
|
||||
from nlg import NLG
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
dst = DST()
|
||||
dp = DP()
|
||||
nlg = NLG()
|
||||
|
||||
print("Witamy w restauracji πzza. W czym mogę pomóc?")
|
||||
|
||||
while True:
|
||||
user_input = input("$")
|
||||
# get user act frame from user input with Natural Language Understanding
|
||||
user_act_frame = nlu(user_input)
|
||||
# print('NLU', user_act_frame)
|
||||
# update Dialogue State Tracker with new user act frame
|
||||
dst.update(user_act_frame)
|
||||
state, last_user_act, last_system_act, slots = dst.get_dialogue_state_tracker_state()
|
||||
# print('state', state)
|
||||
# print('last_user_act', last_user_act)
|
||||
# print('last_system_act', last_system_act)
|
||||
# print('slots', slots)
|
||||
|
||||
# get system act frame which decides what's next from Dialogue Policy
|
||||
system_act_frame = dp.update_system_action(state, last_user_act, last_system_act, slots)
|
||||
dst.update_last_system_act(system_act_frame)
|
||||
# print('system_act_frame', system_act_frame)
|
||||
|
||||
# generate response based on system act frame
|
||||
system_response = nlg.generate_response(state, last_user_act, last_system_act, slots, system_act_frame)
|
||||
print('BOT:', system_response)
|
||||
|
||||
if user_act_frame['act'] == UserActType['bye']:
|
||||
break
|
51
mockup.py
Normal file
51
mockup.py
Normal file
@ -0,0 +1,51 @@
|
||||
acts_list = {'hello': 'welcomemsg',
|
||||
'null': 'canthear'}
|
||||
|
||||
|
||||
class Act_frame:
|
||||
text = ''
|
||||
act = []
|
||||
|
||||
|
||||
class Dialogue_state_frame:
|
||||
state_frame = []
|
||||
|
||||
|
||||
def NLU(text):
|
||||
user_frame = Act_frame()
|
||||
user_frame.text = text
|
||||
if text == 'Cześć, jak masz na imię?':
|
||||
user_frame.act = 'hello'
|
||||
print(user_frame.act)
|
||||
else:
|
||||
user_frame.act = 'null'
|
||||
print(user_frame.act)
|
||||
return user_frame
|
||||
|
||||
|
||||
def DST(user_frame):
|
||||
dialogue_frame = Dialogue_state_frame()
|
||||
dialogue_frame.state_frame.append((user_frame.text, user_frame.act))
|
||||
print(dialogue_frame.state_frame)
|
||||
return dialogue_frame
|
||||
|
||||
|
||||
def DP(dialogue_frame):
|
||||
system_frame = Act_frame()
|
||||
system_frame.act = acts_list[dialogue_frame.state_frame[-1][1]]
|
||||
print(system_frame.act)
|
||||
return system_frame
|
||||
|
||||
|
||||
def NLG(system_frame):
|
||||
answer = ''
|
||||
if system_frame.act == 'welcomemsg':
|
||||
answer = 'Witaj, nazywam się Igrek Iksiński.'
|
||||
elif system_frame.act == 'canthear':
|
||||
answer = 'Nie zrozumiałem.'
|
||||
return answer
|
||||
|
||||
|
||||
text = 'Cześć, jak masz na imię?'
|
||||
#text = 'Niezrozumiałe'
|
||||
print(NLG(DP(DST(NLU(text)))))
|
21
nlg.py
Normal file
21
nlg.py
Normal file
@ -0,0 +1,21 @@
|
||||
from SystemActType import SystemActType
|
||||
from UserActType import UserActType
|
||||
|
||||
|
||||
class NLG:
|
||||
def generate_response(self, state, last_user_act, last_system_act, slots, system_act):
|
||||
if state == UserActType['order']:
|
||||
if system_act['act'] == SystemActType['request']:
|
||||
if system_act['slot'] == 'kind':
|
||||
return 'Jaką pizzę chcesz zamówić?'
|
||||
elif system_act['slot'] == 'size':
|
||||
return 'Jakiego rozmiaru chcesz pizzę?'
|
||||
elif system_act['slot'] == 'plates':
|
||||
return 'Dla ilu osób ma to być?'
|
||||
elif system_act['act'] == SystemActType['confirm_domain']:
|
||||
return 'Czy mam dodać tę pizzę do zamówienia?\nPizza: {}\nRozmiar: {}\nIlość osób: {}'.\
|
||||
format(slots['order']['kind'], slots['order']['size'], slots['order']['plates'])
|
||||
elif last_user_act == UserActType['hello']:
|
||||
return 'Dzień dobry, w czym mogę pomóc?'
|
||||
else:
|
||||
return 'Przepraszam. Zdanie nie jest mi zrozumiałe. Spróbuj je sformułować w inny sposób.'
|
49
nlu.py
Normal file
49
nlu.py
Normal file
@ -0,0 +1,49 @@
|
||||
import jsgf
|
||||
from os import listdir
|
||||
from os.path import isfile, join
|
||||
|
||||
|
||||
gram_dir = './gramatyki/'
|
||||
grammar_files = [file for file in listdir(gram_dir) if isfile(join(gram_dir, file))]
|
||||
|
||||
grammars = []
|
||||
|
||||
for grammarFile in grammar_files:
|
||||
grammar = jsgf.parse_grammar_file(gram_dir + grammarFile)
|
||||
grammars.append(grammar)
|
||||
|
||||
|
||||
def get_dialog_act(rule):
|
||||
slots = []
|
||||
get_slots(rule.expansion, slots)
|
||||
return {'act': rule.grammar.name, 'slots': slots}
|
||||
|
||||
|
||||
def get_slots(expansion, slots):
|
||||
if expansion.tag != '':
|
||||
slots.append((expansion.tag, expansion.current_match))
|
||||
return
|
||||
|
||||
for child in expansion.children:
|
||||
get_slots(child, slots)
|
||||
|
||||
if not expansion.children and isinstance(expansion, jsgf.NamedRuleRef):
|
||||
get_slots(expansion.referenced_rule.expansion, slots)
|
||||
|
||||
|
||||
def nlu(utterance):
|
||||
matched = None
|
||||
for _grammar in grammars:
|
||||
matched = _grammar.find_matching_rules(utterance)
|
||||
if matched:
|
||||
break
|
||||
|
||||
if matched:
|
||||
return get_dialog_act(matched[0])
|
||||
else:
|
||||
return {'act': 'null', 'slots': []}
|
||||
|
||||
|
||||
# print(nlu('chciałbym zamowic pizze vesuvio XXL na dwie osoby'))
|
||||
# print(nlu('na dowoz'))
|
||||
# print(nlu('dowoz'))
|
Loading…
Reference in New Issue
Block a user