Skip to content

Commit b64deaf

Browse files
committed
Added exception handling + improved response quality of chatbot
1 parent c0401ca commit b64deaf

File tree

7 files changed

+130
-37
lines changed

7 files changed

+130
-37
lines changed

project/app/BayesClassifier.hs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,46 @@
1+
-- CODE REFERENCE: http://vishnumenon.com/2012/06/07/naive-bayes-classifier-in-haskell/
2+
-- Source for the code listed below
3+
14
module BayesClassifier where
25
-- Text Classifier Using Bayes Formula
36
import Data.List
47
import Data.Char
58
type Category = String
69
newtype Classifier = Classifier { training :: [(Category, [String])] } deriving (Eq, Show)
10+
711
-- Get a new classifer with no training
812
classifier :: Classifier
913
classifier = Classifier []
14+
1015
-- classifier probabilities
1116
probabilityOfWordInCategory :: Classifier -> String -> Category -> Double
12-
probabilityOfCategory :: Classifier -> Category -> Double
1317
-- Adding + 1 for Laplacian Correction
1418
probabilityOfWordInCategory (Classifier training) word category = let allInCategory = filter (\(cat, _) -> cat == category) training
1519
allInCategoryContainingWord = filter (\(_, text) -> word `elem` text) allInCategory
1620
in (fromIntegral $ length allInCategoryContainingWord + 1) / (fromIntegral $ length allInCategory + 1)
21+
22+
probabilityOfCategory :: Classifier -> Category -> Double
1723
probabilityOfCategory (Classifier training) category = let allInCategory = filter (\(cat, _) -> cat == category) training
1824
in (fromIntegral $ length allInCategory) / (fromIntegral $ length training)
25+
1926
-- Train a classifier
2027
train :: Classifier -> String -> Category -> Classifier
2128
train (Classifier training ) text category = Classifier $ (category, cleanInput $ text):training
29+
2230
-- Categorize text with a classifier
2331
classify :: Classifier -> String -> Category
2432
classify classifier text = fst $ head $ sortBy (\(_, a) (_, b) -> b `compare` a) $ probabilities classifier text
33+
2534
-- Get Probability for each Category
2635
probabilities :: Classifier -> String -> [(Category, Double)]
2736
probabilities classifier@(Classifier training) text = map (\cat -> (cat, probabilityForCategory classifier text cat)) $ nub $ map (\(cat, _) -> cat) training
37+
2838
-- Get Probability for a passage in a certain category
2939
probabilityForCategory :: Classifier -> String -> Category -> Double
3040
probabilityForCategory classifier text category = (+) (log $ probabilityOfCategory classifier category) (sum $ map (\word -> log $ probabilityOfWordInCategory classifier word category) $ cleanInput text)
41+
3142
-- Lowercase, Remove Punctuation
3243
cleanInput :: String -> [String]
3344
cleanInput text = filter (\w -> not (w `elem` stopWords)) $ words $ filter (`elem` ' ':['a'..'z']) $ map toLower text
34-
where stopWords = ["a","about","above","after","again","against","all","am","an","and","any","are","aren't","as","at","be","because","been","before","being","below","between","both","but","by","can't","cannot","could","couldn't","did","didn't","do","does","doesn't","doing","don't","down","during","each","few","for","from","further","had","hadn't","has","hasn't","have","haven't","having","he","he'd","he'll","he's","her","here","here's","hers","herself","him","himself","his","how","how's","i","i'd","i'll","i'm","i've","if","in","into","is","isn't","it","it's","its","itself","let's","me","more","most","mustn't","my","myself","no","nor","not","of","off","on","once","only","or","other","ought","our","ours ","ourselves","out","over","own","same","shan't","she","she'd","she'll","she's","should","shouldn't","so","some","such","than","that","that's","the","their","theirs","them","themselves","then","there","there's","these","they","they'd","they'll","they're","they've","this","those","through","to","too","under","until","up","very","was","wasn't","we","we'd","we'll","we're","we've","were","weren't","what","what's","when","when's","where","where's","which","while","who","who's","whom","why","why's","with","won't","would","wouldn't","you","you'd","you'll","you're","you've","your","yours","yourself","yourselves"]
45+
where stopWords = [",","'","a","about","above","after","again","against","all","am","an","and","any","are","aren't","as","at","be","because","been","before","being","below","between","both","but","by","can't","cant","cannot","could","couldn't","did","didn't","do","does","doesn't","doing","don't","down","during","each","few","for","from","further","had","hadn't","has","hasn't","have","haven't","having","he","he'd","he'll","he's","her","here","here's","hers","herself","him","himself","his","how","how's","i","i'd","i'll","i'm","i've","if","in","into","is","isn't","it","it's","its","itself","let's","me","more","most","mustn't","my","myself","no","nor","not","of","off","on","once","only","or","other","ought","our","ours ","ourselves","out","over","own","same","shan't","she","she'd","she'll","she's","should","shouldn't","so","some","such","than","that","that's","the","their","theirs","them","themselves","then","there","there's","these","they","they'd","they'll","they're","they've","this","those","through","to","too","under","until","up","very","was","wasn't","we","we'd","we'll","we're","we've","were","weren't","what","what's","when","when's","where","where's","which","while","who","who's","whom","why","why's","with","won't","would","wouldn't","you","you'd","you'll","you're","you've","your","yours","yourself","yourselves"]
3546

project/app/ChatClassifier.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module ChatClassifier where
33

44
import System.IO
55
import BayesClassifier
6+
import Data.List (intersect)
67

78
splitsep :: (a -> Bool) -> [a] -> [[a]]
89
splitsep s = foldr (\e (h:t) -> if s e then []:(h:t) else (e:h):t) [[]]
@@ -15,6 +16,9 @@ readCsv filename = do
1516
writeCsv :: FilePath -> [Char] -> [Char] -> IO ()
1617
writeCsv filename c q = appendFile filename ("\n" ++ c ++ "," ++ q)
1718

19+
writeCsvR :: FilePath -> [Char] -> [Char] -> IO ()
20+
writeCsvR filename c q = appendFile filename ("\n" ++ q ++ "," ++ c)
21+
1822
fetchAnswer :: FilePath -> Category -> IO String
1923
fetchAnswer filename c = do
2024
file <- readFile filename
@@ -32,4 +36,6 @@ trainedClassifier fp = do
3236
let trained_classifier = foldl trainline classifier trainingData
3337
return trained_classifier
3438

35-
39+
-- from https://stackoverflow.com/questions/27471710/checking-if-2-list-have-any-equal-element-haskell
40+
compareList :: (Eq a) => [a] -> [a] -> Bool
41+
compareList a = not . null . intersect a

project/app/Main.hs

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
-- CODE REFERENCE: https://wiki.haskell.org/Implement_a_chat_server
2+
13
module Main where
24

35
import Network.Socket
@@ -10,6 +12,7 @@ module Main where
1012
import ChatClassifier
1113
import BayesClassifier
1214

15+
-- intialize socket and create channel for reading responses
1316
main :: IO ()
1417
main = do
1518
sock <- socket AF_INET Stream 0
@@ -24,56 +27,108 @@ module Main where
2427

2528
type Msg = (Int, String)
2629

30+
-- create a thread to read the responses
2731
mainLoop :: Socket -> Chan Msg -> Int -> IO ()
2832
mainLoop sock chan msgNum = do
2933
conn <- accept sock
3034
forkIO (runConn conn chan msgNum)
3135
mainLoop sock chan $! msgNum + 1
3236

37+
-- run thread and listen for any response
3338
runConn :: (Socket, SockAddr) -> Chan Msg -> Int -> IO ()
3439
runConn (sock, _) chan msgNum = do
3540
let broadcast msg = writeChan chan (msgNum, msg)
3641
hdl <- socketToHandle sock ReadWriteMode
3742
curdDir <- getCurrentDirectory
38-
chat_classifier <- trainedClassifier (curdDir ++ "/project/src/questions.csv")
43+
3944
hSetBuffering hdl NoBuffering
4045

46+
-- All code below is written by our group
47+
hPutStrLn hdl "Would you like to enable training mode (y/n)?"
48+
training <- fmap init (hGetLine hdl)
49+
4150
-- Initial Message
4251
hPutStrLn hdl "Chatbot: Hello, what did you need assistance with?"
4352

4453
-- Message Loop
4554
handle (\(SomeException _) -> return ()) $ fix $ \loop -> do
55+
-- fetch question from command line
4656
question <- fmap init (hGetLine hdl)
4757
case question of
4858
-- If an exception is caught, send a message and break the loop
4959
"quit" -> hPutStrLn hdl "Chatbot: Goodbye!"
5060
-- else, continue looping.
5161
_ -> do
62+
-- reload the classifier with updated training data
63+
chat_classifier <- trainedClassifier (curdDir ++ "/project/src/questions.csv")
5264
hPutStrLn hdl ("User: " ++ question)
5365

54-
-- get answer
55-
let category = classify chat_classifier question
56-
hPutStrLn hdl ("Category: " ++ category)
57-
58-
let prob = probabilityForCategory chat_classifier question category
59-
-- hPutStrLn hdl ("Prob: " ++ (show prob))
66+
-- clean input by removing connectives and fetch categories
67+
let cleanq = cleanInput question
68+
cats <- readCsv (curdDir ++ "/project/src/categories.csv")
6069

61-
let ansDir = (curdDir ++ "/project/src/answers.csv")
62-
str <- fetchAnswer ansDir (dropWhile (==' ') category)
70+
-- check if question contains a reference to a category
71+
let incat = compareList (cats !! 0) cleanq
6372

64-
hPutStrLn hdl ("Prob: " ++ show prob)
65-
hPutStrLn hdl ("Chatbot: " ++ str)
66-
67-
hPutStrLn hdl "Chatbot: To help us improve, was this answer helpful? (y/n)"
68-
answer <- fmap init (hGetLine hdl)
69-
if answer == "y"
73+
-- if it does not contain a reference we can not give a valid answer so return and reloop]
74+
-- otherwise get appropriate response
75+
if (not incat)
7076
then do
71-
--- if answer is good, add question with category to questions.csv
72-
write <- writeCsv (curdDir ++ "/project/src/questions.csv") (dropWhile (==' ') category) question
73-
hPutStrLn hdl "Chatbot: If you need anymore help feel free to ask another question, otherwise type 'quit' to exit"
74-
else
75-
-- nothing
76-
hPutStrLn hdl "Chatbot: Sorry, please ask the question again for a more relevant answer"
77+
hPutStrLn hdl "Sorry, I can't answer that please make sure your question is part of acceptable categories"
78+
hPutStrLn hdl (show (cats !! 0))
79+
else do
80+
-- DEBUG CODE: REMOVE BEFORE DEMO
81+
--let probs = foldr (\h acc -> ((classify chat_classifier question),(probabilityForCategory chat_classifier question (classify chat_classifier question))):acc) [] cleanq
82+
--hPutStrLn hdl (show probs)
83+
84+
-- get category, prob of valid answer
85+
let category = classify chat_classifier question
86+
let prob = probabilityForCategory chat_classifier question category
87+
let ansDir = (curdDir ++ "/project/src/answers.csv")
7788

89+
-- if prob of answer is above a threshold return a response
90+
-- otherwise return fail message for no valid response found with training data
91+
if prob > -5.0
92+
then do
93+
-- fetch answer
94+
str <- fetchAnswer ansDir (dropWhile (==' ') category)
95+
hPutStrLn hdl ("Chatbot: " ++ str)
96+
97+
-- check if answer was good to improve training data
98+
hPutStrLn hdl "Chatbot: To help us improve, was this answer helpful? (y/n)"
99+
answer <- fmap init (hGetLine hdl)
100+
101+
if answer == "y"
102+
then do
103+
--- if answer is good, add question with category to questions.csv to improve future responses
104+
write <- writeCsvR (curdDir ++ "/project/src/questions.csv") (dropWhile (==' ') category) question
105+
hPutStrLn hdl "Chatbot: If you need anymore help feel free to ask another question, otherwise type 'quit' to exit"
106+
else
107+
-- nothing
108+
hPutStrLn hdl "Chatbot: Sorry, please ask the question again for a more relevant answer"
109+
else do
110+
-- if training mode is enabled we can directly add new categories/questions/answers
111+
if training == "y"
112+
then do
113+
hPutStrLn hdl "Add to existing category (y/n)?"
114+
existing <- fmap init (hGetLine hdl)
115+
if existing == "y"
116+
then do
117+
hPutStrLn hdl "Training Enabled please enter a Category"
118+
cat <- fmap init (hGetLine hdl)
119+
write <- writeCsvR (curdDir ++ "/project/src/questions.csv") cat question
120+
hPutStrLn hdl "Question added under category, you may ask another question"
121+
else do
122+
hPutStrLn hdl "Training Enabled please enter a answer"
123+
ans <- fmap init (hGetLine hdl)
124+
hPutStrLn hdl "Training Enabled please enter a Category"
125+
cat <- fmap init (hGetLine hdl)
126+
hPutStrLn hdl ("Category: " ++ cat)
127+
write <- writeCsvR (curdDir ++ "/project/src/questions.csv") cat question
128+
write <- writeCsv (curdDir ++ "/project/src/answers.csv") cat ans
129+
hPutStrLn hdl "Question answer pair added, you may ask another question"
130+
else
131+
hPutStrLn hdl "No relevant answer found, you may ask another question"
132+
-- finally reloop to continue waiting for respones from the socket
78133
loop
79134
hClose hdl -- close the handle

project/src/answers.csv

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
login,Try logging in again
2-
password,Please reenter your password
2+
password,Please re-enter your password
33
email,Please make sure your entering your email correctly
4-
leaving_flight,9:30pm
5-
flight_number,22
4+
leaving flight,9:30pm
5+
flight number,22

project/src/categories.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
login,password,email,flight

project/src/questions.csv

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
1-
I can't login, login
2-
login, login
3-
my login dosen't work, login
4-
I completely forgot what password i used to login was, login
5-
I forgot my password, password
6-
Whats my password, password
7-
My email doesn't work, email
8-
When is my flight, leaving_flight
9-
What is my flight number, flight_number
10-
password,My password
1+
login,login
2+
cant login,login
3+
how do i login to my account,login
4+
password,password
5+
password,password
6+
I cant login to my password,login
7+
I forgot my password,password
8+
I forgot my password,password
9+
I cant login,login
10+
How do I enter the login,login
11+
how do I password my login account,login
12+
How do i access my password,password
13+
flight number,flight number
14+
flight number,flight number
15+
how to find my flight number,flight number
16+
what is my flight number,flight number
17+
flight time,leaving flight
18+
leaving flight,leaving flight
19+
what time is my flight leaving,leaving flight
20+
when does my flight leave,leaving flight
21+
when does my flight leave,leaving flight
22+
something leave,leaving flight
23+
how do i login,login
24+
how do i login,login
25+
when do i enter the flight to leave,leaving flight
26+
What time is my flight,leaving flight
27+
login,login
28+
can i login,login
29+
how do i login,login
30+
sorry when does my flight leave,leaving flight

0 commit comments

Comments
 (0)