<div class="notebook"> <div class="nb-cell markdown" name="md1"> # CPSC 312 Chatbot Project Shiv Vashisht, Kabir Chattopadhyay, Nam Hee Gordon Kim ## What is the problem? Our goal is to determine whether Prolog is a suitable platform for creating a limited-scope chatbot. As it is known, a chatbot essentially parses a natural language input (e.g. what day is it?) and provides a response to the user. The response that the chatbot produces is expected to make sense in context of the user's input. Usually, how these chatbots are implemented is through identifying keywords in the natural language and then selecting appropriate response phrases after combining them using a natural language interface. We plan to research existing implementations of chatbots in Prolog (e.g Eliza) and attempt to build our own so that we can determine whether Prolog is a feasible language. ## What is the something extra? In our class examples and within the scope of this course, we have only worked with programs with finite queries. In other words, we only asked questions of the natural language interface - however, in a chatbot, the possible questions a user can ask are quite ambiguous and in fact, the chatbot might just ask the user a question. Further, the goal is to provide a response that isn't arbitrary but makes sense in terms of the context of the user's input. We want to use keywords from the input to identify a subject that the user is discussing and then choose a response for that particular subject. This implementation provides the something "extra" by going beyond just querying a static knowledge base and instead, adding to the knowledge base and generating the chatbot's output based off of the user's input. </div> <div class="nb-cell program" name="p3"> % static knowledge coded in triples % type hierarchy % super(P,C) is true if P is parent class of child C. super(plant, flower). super(food, condiment). % sameas(A, B) is true if A is same as B sameas(X, X). % itself is same as itself sameas(you, chatbot). sameas(is, are). % synonymous % roses are red, violets are blue prop(rose, color, red). prop(rose, type, flower). prop(violet, color, blue). prop(violet, type, flower). prop(sky, color, blue). prop(mayonnaise, type, condiment). prop(mayonnaise, taste, delicious). prop(mayonnaise, color, white). prop(clarinet, type, instrument). prop(life, fun, true). prop(ice, cold, true). prop(beer, cool, true). % Self-awareness prop(chatbot, identity, "CPSC 312 Chatbot"). % linguistics isgreeting(hello). isgreeting(hi). isgreeting(hey). isgreeting([H | _]) :- isgreeting(H). isgreeting([_ | T]) :- isgreeting(T). % istype(O, T) is true if object O is of type T or its superclasses istype(O, T) :- prop(O, type, T). istype(O, T) :- super(T, S), istype(O, S). % phrase parsers: get the actual object from phrases like "the sky", "a rose" % noun(N, NP) is true if N is the main object of noun phrase NP (list) noun(N, [N]). noun(N, [the | N1]) :- noun(N, N1). noun(N, [a | N1]) :- noun(N, N1). noun(N, [an | N1]) :- noun(N, N1). % adjective(V, ADJ) adjective(V, [V]). adjective(V, [H|T]) :- adjective(V, H) ; adjective(V, T). % askers % askis(NP, ADJ) is true if object O of noun phrase NP has some property of value V of adjective phrase ADJ askis(NP, ADJ) :- noun(O, NP), adjective(V, ADJ), prop(O, _, V), print_list(["Yes", O, "is", V, "."]), nl. % askis(NP, ADJ) is true if object O of noun phrase NP is of type T of adjective phrase ADJ askis(NP, ADJ) :- noun(O, NP), noun(T, ADJ), istype(O, T), print_list(["Yes", O, "is", T, "."]), nl. askis(NP, ADJ) :- noun(O, NP), noun(T, ADJ), istype(O, TT), dif(T, TT), print_list(["No, ", NP, "is not a", T, ". It's a ", TT, "."]), nl. askis(NP, ADJ) :- noun(O, NP), noun(V, ADJ), \+prop(O, _, V), print_list(["I can't tell whether ", NP, "is", ADJ, "."]), nl. % askwhatis(NP) is true if object O of noun phrase NP has some property P that has value V askwhatis(NP) :- noun(O, NP), istype(O, T), atomic(T), print_list([O, "is", T, "."]), nl. askwhatis(NP) :- noun(O, NP), istype(O, T), prop(O, TT, V), dif(TT, type), atomic(T), print_list([O, "is", V, T, "."]), nl. askwhatis(NP) :- noun(O, NP), prop(O, identity, I), atomic(I), print_list([O, "is", I, "."]), nl. % askwhatis(PPofNP) is true if PPofNP is delimited by "of" to PP and NP and askprop(NP, PP) is true. askwhatis(PPofNP) :- append(PP, _, PPofNP), append(PP, [of], L), append(L, NP, PPofNP), askprop(NP, PP). % askwhyis(NP, V) is true if object of NP has some property P whose value is V. askwhyis(NP, ADJ) :- noun(O, NP), adjective(V, ADJ), prop(O, P, V), print_list([the, P, of, NP, is, an, intricate, subject, "."]), nl. askwhyis(NP, ADJ) :- noun(O, NP), adjective(V, ADJ), \+prop(O, _, V), print_list([wait, NP, is, not, ADJ, "."]), nl. % askprop(OP, PP) is true if object O of noun phrase OP has some property P of noun phrase PP. The value V is printed as byproduct. % If O does not have a property P, suggestions are offered. askprop(NP, PP) :- noun(O, NP), noun(P, PP), prop(O, P, V), atomic(P), print_list(["The", P, "of", NP, "is", V, "."]), nl. askprop(NP, PP) :- noun(O, NP), noun(P, PP), \+prop(O, P, _), prop(O, X, _), dif(X, P), print_list([NP, has, no, P, but, it, does, have, X, "."]), nl. askprop(NP, PP) :- noun(O, NP), noun(P, PP), \+prop(O, _, _), prop(OO, P, _), print_list(["I don't know about", NP, "but I can tell you about", PP, "of", OO, "."]), nl. % ask: sentence parsing ask([Be | NpAdj]) :- sameas(Be, is), append(NP, ADJ, NpAdj), askis(NP, ADJ). ask([what, is | O]) :- askwhatis(O). ask([why, is | NpAdj]) :- append(NP, ADJ, NpAdj), askwhyis(NP, ADJ). ask([who, are, you]) :- whoami. % stateiam(NP) is true if the object O in noun phrase NP has some property P of value V. % A witty response is generated. stateiam(NP) :- noun(O, NP), istype(O, V), print_list(["If you are", NP, "then you must be a", V, "."]), nl. stateiam(NP) :- noun(O, NP), prop(O, P, V), dif(P, type), dif(V, true), print_list(["If you are", NP, "then you must be", V, "."]), nl. stateiam(NP) :- noun(O, NP), prop(O, P, true), print_list(["If you are", NP, "then you must be", P, "."]), nl. % stateiam(ADJ) is true if there is an object O that satisfies value V for some property P in ADJ stateiam(ADJ) :- adjective(V, ADJ), dif(V, true), prop(O, _, V), print_list(["If you are", ADJ, "then you must be", O, "."]), nl. % stateiam(X) is true if the statement is of the form "I am X" % where X is a list containing an adjective or a modifying noun phrase stateiam(X) :- append([since, when, are, you], X, R), append(R, [?], R1), print_list(R1), nl. % stateyouare(ADJ) stateyouare(ADJ) :- noun(O, ADJ), prop(O, P, V), dif(P, type), dif(V, true), print_list(["I am not", V, "enough to be", ADJ, "."]), nl. stateyouare(ADJ) :- adjective(V, ADJ), prop(O, P, V), dif(P, type), dif(V, true), print_list(["I might be", V, "but I am not a", O, "."]), nl. stateyouare(ADJ) :- adjective(V, ADJ), prop(O, V, true), print_list(["I might be", V, "but I am not a", O, "."]), nl. stateyouare(ADJ) :- print_list(["Are you sure I am", ADJ, "?"]), nl. % state(G) is true is G is a greeting from user. state(G) :- isgreeting(G), greet. state([i, am | ADJ]) :- stateiam(ADJ). state([you, are | ADJ]) :- stateyouare(ADJ). % subjunctive clause handling % state([i, V | S]) is true if the statement starts with "i, V" where V is a subjunctive verb state([i, V | S]) :- subv(V), state(S). % subv is true if the verb in consideration is used in subjunctive clauses like "I think", "I suppose" subv(think). subv(believe). subv(suppose). subv(guess). subv(say). % misc., including greetings and hails greet :- print_list(["Hi."]), nl. goodbye :- print_list(["Goodbye."]), nl. whoami :- prop(chatbot, identity, I), print_list(["I am", I, "."]), nl. % print_list from https://stackoverflow.com/a/6877478 % flatten from https://stackoverflow.com/a/9059827 flatten([], []) :- !. flatten([L|Ls], FlatL) :- !, flatten(L, NewL), flatten(Ls, NewLs), append(NewL, NewLs, FlatL). flatten(L, [L]). print_list([X]):- is_list(X), flatten(X, Fx), print_list(Fx). print_list([X]):- \+is_list(X), write(X), !. print_list([H|T]):- is_list(H), flatten(H, Fh), print_list(Fh), write(' '), print_list(T). print_list([H|T]):- \+is_list(H), write(H), write(' '), print_list(T). </div> <div class="nb-cell query" name="q23"> ask([who, are, you]). </div> <div class="nb-cell markdown" name="md11"> # Part 1: Static Knowledge Query </div> <div class="nb-cell markdown" name="md18"> ## Basic Property and Type Queries </div> <div class="nb-cell markdown" name="md2"> What is blue? </div> <div class="nb-cell query" name="q1"> prop(X, Y, blue). </div> <div class="nb-cell markdown" name="md3"> Is mayonnaise an instrument? </div> <div class="nb-cell query" name="q2"> prop(mayonnaise, type, instrument). </div> <div class="nb-cell markdown" name="md4"> Is mayonnaise food? </div> <div class="nb-cell query" name="q3"> prop(mayonnaise, type, food). </div> <div class="nb-cell markdown" name="md5"> Although food is superclass of condiment, this query returns false. Checking the triplet atom doesn't let us accurately check for superclass membership. Hence we define a syntactic helper to establish superclass membership: </div> <div class="nb-cell query" name="q4"> istype(mayonnaise, food). </div> <div class="nb-cell markdown" name="md6"> ### Humanized Interactions Now we have minimum viable components for asking a bit more natural questions in the form of "Is <object> <property>?" or "Is <object> <type>?". We do this with the additional predicate `askis`. In addition, we provide default responses where query resolves to false. </div> <div class="nb-cell query" name="q5"> askis([sky], [blue]). </div> <div class="nb-cell query" name="q34"> askis([sky], [grey]). </div> <div class="nb-cell query" name="q6"> askis([mayonnaise], [instrument]). </div> <div class="nb-cell markdown" name="md7"> We also create a common entry point for different possible patterns of questions, called `ask`. </div> <div class="nb-cell query" name="q7"> ask([is, mayonnaise, instrument]). </div> <div class="nb-cell query" name="q8"> ask([is, mayonnaise, food]). </div> <div class="nb-cell query" name="q9"> ask([is, sky, blue]). </div> <div class="nb-cell markdown" name="md19"> ## Description of Object in Database </div> <div class="nb-cell markdown" name="md8"> A more general question than asking "Is <object> <property>?" is asking "What is <object>?". </div> <div class="nb-cell query" name="q10"> prop(rose, type, T). </div> <div class="nb-cell markdown" name="md9"> Wouldn't it be nicer if we got an answer waying "Rose is a red flower"? We create a predicate that answers this in a natural way. </div> <div class="nb-cell query" name="q12"> prop(rose, type, T), prop(rose, _, V). </div> <div class="nb-cell query" name="q11"> askwhatis([rose]). </div> <div class="nb-cell markdown" name="md16"> Meanwhile, querying for the specific property of a specific object should be a nice thing to support. </div> <div class="nb-cell query" name="q21"> askwhatis([color, of, rose]). </div> <div class="nb-cell query" name="q13"> ask([what,is,rose]). </div> <div class="nb-cell query" name="q14"> ask([what,is,mayonnaise]). </div> <div class="nb-cell markdown" name="md17"> ## Querying for Specific Property of Object Often, users will ask something like "what is the meaning of life?". Our approach is to parse the sentence structure to identify the object and the property specified. </div> <div class="nb-cell query" name="q22"> askprop([violet], [the,color]). </div> <div class="nb-cell query" name="q24"> askwhatis([the, color, of, violet]). </div> <div class="nb-cell query" name="q25"> ask([what, is, the, color, of, violet]). </div> <div class="nb-cell markdown" name="md13"> Another interesting use case of static knowledge is to ask for reasons, although we might not get a meaningful answer. </div> <div class="nb-cell query" name="q16"> askwhyis([mayonnaise], [bad]). </div> <div class="nb-cell query" name="q17"> askwhyis([sky], [blue]). </div> <div class="nb-cell markdown" name="md14"> And then we generalize this with the entry point `ask` </div> <div class="nb-cell query" name="q18"> ask([why, is, mayonnaise, delicious]). </div> <div class="nb-cell markdown" name="md10"> </div> <div class="nb-cell markdown" name="md12"> # Part 2: Statement Handling </div> <div class="nb-cell markdown" name="md20"> To give an illusion of a human being behind the screen, we may want to give human-sounding responses to input that are not specific queries. The statement handling procedures may use the information in statements to make responses more human. </div> <div class="nb-cell query" name="q15"> stateiam([hungry]) </div> <div class="nb-cell markdown" name="md25"> The predicate `state` further generalizes this interaction, where the user enters a sentence and the noun phrase and adjective phrase are parsed automatically. </div> <div class="nb-cell query" name="q19"> state([i,am,hungry]) </div> <div class="nb-cell query" name="q20"> state([i,am,a,beautiful,person]) </div> <div class="nb-cell markdown" name="md21"> An interesting response might be one that utilizes the knowledge base. </div> <div class="nb-cell query" name="q27"> state([i, am, mayonnaise]). </div> <div class="nb-cell query" name="q33"> state([i, am, blue]). </div> <div class="nb-cell markdown" name="md24"> On the other hand, the user's comment on the chatbot also gets witty responses. </div> <div class="nb-cell query" name="q28"> stateyouare([funny]). </div> <div class="nb-cell query" name="q29"> stateyouare([so, cool]). </div> <div class="nb-cell query" name="q30"> state([you, are, a, rose]). </div> <div class="nb-cell markdown" name="md22"> ## Subjunctive Clauses Subjunctive clauses like "I think you are cool" should be parsed based on the later half rather than the first. </div> <div class="nb-cell query" name="q31"> state([i, think, you, are, very, cold]). </div> <div class="nb-cell markdown" name="md15"> # Part 3: Static Knowledge Query with Clarifying Feedback Sometimes, there are ambigities or missingness in interactions. The chatbot should ask for clarification should ambiguities arise. For example, if the user queries for a specific property of an object, and the object does not have that property, the chatbot might suggest other properties to query. </div> <div class="nb-cell query" name="q26"> ask([what, is, the, meaning, of, life]). </div> <div class="nb-cell markdown" name="md23"> If the user queries for an object that does not exist, then the chatbot should suggest another existing object. </div> <div class="nb-cell query" name="q32"> ask([what, is, the, color, of, apple]). </div> </div>