A chatbot is a program that is capable of holding a conversation with a user,
which may use text or spoken language. Many Chatbots are very boring because
they are very limited in what they can respond to and what they can say in
response. A chatbot usually does not understand anything that they user types in
or that the chatbot types in response. However, it is possible to have a chatbot
that understands a little about a small subset of the world and can talk about
that.
However, most chatbots depend on the programmer anticipating the sorts of things
that could be typed in and preparing suitable responses. In some cases the
response could be based on some of the words actually typed in, giving the
illusion that the chatbot understood what it read in. An example chatbot that
can use patterns to work out how to respond is the Pop-11 Eliza, which anyone
can interact with on the internet:
http://www.cs.bham.ac.uk/research/projects/cogaff/eliza/
An example of an architecture for a chatbot:
When designing a complex program that puts a number of capabilities together it
is useful to think of the program as having an "architecture". For example, when
a chatbot is running there are typically at least three things interacting,
o a User (the human using the program)
o an Interface (a part of the system to read in what the user types and
print out the machine's answers).
o an Inference Engine (a part of the system that works out what answer
should be given in response to what the user last typed in.)
We can represent the architecture diagrammatically:
*------* --- > --- *---------* --- > --- *----------------*
| USER | |INTERFACE| |INFERENCE ENGINE|
*------* --- < --- *---------* --- < --- *----------------*
The interface is a sub-program which prints out a greeting, then
repeatedly reads in a sentence and prints out a sentence, until the
user types "bye". Then it prints out a farewell message and stops.
In order to decide what to print out it consults the inference
engine.
We shall give a very simple example of how to design an inference
engine -- one that really knows nothing and doesn't really do any
inference, but merely pretends to.
The chatbot inference engine (version 1)
We first define a very stupid and simple inference engine for testing the
interface designed below:
define inference_engine(sentence) -> response;
;;; takes in a list of words and responds by producing a list,
;;; assigned to the output variable 'response'
[Very sorry: I cannot answer this ^sentence] -> response
enddefine;
Notice that this does not directly print out its response. It returns it as a
result, to be communicated to whatever invoked this procedure. We can test it
with some silly examples:
inference_engine([Hi]) =>
** [Very sorry : I cannot answer this [Hi]]
inference_engine([Will you be my friend?]) =>
** [Very sorry : I cannot answer this [Will you be my friend ?]]
Later we can make this more intelligent.
(You could vary this initial response for your first tests.)
The chatbot interface (version 1)
The interface could be defined thus, using the Pop-11 procedure readline, to
read in a list of words typed by the user.
It invokes the procedure inference_engine defined provisionally above, to be
defined better later on.
We use 'lvars' for local variables --
accessible only within the procedure in which they are declared.
define interface();
;;; This procedure does not take any input when it starts.
;;; it repeatedly reads in something typed by the user,
;;; gets a response from inference_engine, then prints out
;;; the response.
;;; It checks whether the user wants to stop, and if so prints
;;; a final message and stops.
;;; we need two variables, to be used repeatedly.
lvars sentence, reply;
repeat forever
[Please type something and end by pressing the RETURN key] =>
readline() -> sentence;
[This is what you typed in:] =>
sentence =>
[This is my reply] =>
;;; get the reply from the inference engine
inference_engine(sentence) -> reply;
;;; and print it out
reply =>
;;; we can test whether to stop now
if sentence = [bye] then
;;; terminate the loop
quitloop();
endif;
endrepeat;
;;; quitloop sends the program here:
[This consultation is free. See you later I hope.] =>
[Bye for now] =>
enddefine;
Testing interface version 1 and inference engine version 1
;;; Start the interface
interface();
Here is what a test interaction might look like. The lines that start
with the prompt simple '?' contain what the user typed in.
Everything else was printed out by the program.
** [Please type something and end by pressing the RETURN key]
? Hello. I need some help.
** [This is what you typed in :]
** [Hello . I need some help .]
** [This is my reply]
** [Very sorry : I cannot answer this [Hello . I need some help .]]
** [Please type something and end by pressing the RETURN key]
? You are very long winded
** [This is what you typed in :]
** [You are very long winded]
** [This is my reply]
** [Very sorry : I cannot answer this [You are very long winded]]
** [Please type something and end by pressing the RETURN key]
? bye
** [This is what you typed in :]
** [bye]
** [This is my reply]
** [Very sorry : I cannot answer this [bye]]
** [This consultation is free . See you later I hope .]
** [Bye for now]
;;; For now, let's ignore the fact that spaces are inserted before the colon and
;;; the period.
We can improve the inference engine by making use of the Pop-11 matcher.
The chatbot inference engine (version 2)
This version of the inference engine is a little more flexible than version 1,
but it is still very stupid and simple.
It makes use of a multi-branch conditional instruction which has a collection of
tests (using if or elseif, and responses based on the tests, indicated by then.
The end of the conditional is indicated by endif.
define inference_engine(input) -> result;
;;; takes in a list of words and responds by producing a list,
;;; assigned to the output variable 'response'
;;; we need some variables for use by the pattern matcher
lvars x, y;
;;; notice that the patterns are now all preceded by "!". This is to
;;; make them work with the local variables defined using "lvars" in this
;;; procedure.
if input matches [i hate == ] then
[perhaps you hate yourself] -> result;
elseif input matches ! [are you ??x ] then
[do i seem ^^x] -> result;
elseif input matches ! [i ??x you] then
[perhaps in your fantasy we ^^x each other] -> result;
elseif input matches ! [??x is ??y] then
[what if ^^x were not ^^y ? ] -> result;
elseif input matches [bye] then
[I have enjoyed our meeting ] -> result;
else
;;; input not recognized -- this is the default response.
[please go on] -> result;
endif;
enddefine;
Since we have used the same name for the inference engine as before, when this
definition is compiled it will override the previous definition. It does not
matter that we have altered the input and output variables to be 'input' and
'result' instead of 'sentence' and 'response' as before.
We can give this some tests
inference_engine([the sky is blue]) =>
** [what if the sky were not blue ?]
inference_engine([i like talking to you]) =>
** [perhaps in your fantasy we like talking to each other]
Ideally, all the options should be tested. We can now do that by running the old
interface program.
interface();
That produces this interaction:
** [Please type something and end by pressing the RETURN key]
? are you my friend
** [This is what you typed in :]
** [are you my friend]
** [This is my reply]
** [do i seem my friend]
** [Please type something and end by pressing the RETURN key]
? i hate obscure answers
** [This is what you typed in :]
** [i hate obscure answers]
** [This is my reply]
** [perhaps you hate yourself]
** [Please type something and end by pressing the RETURN key]
? i really really like talking to you
** [This is what you typed in :]
** [i really really like talking to you]
** [This is my reply]
** [perhaps in your fantasy we really really like talking to each other]
** [Please type something and end by pressing the RETURN key]
? bye
** [This is what you typed in :]
** [bye]
** [This is my reply]
** [I have enjoyed our meeting]
** [This consultation is free . See you later I hope .]
** [Bye for now]
Streamlining and extending the chatbot
It should be clear that this interaction has too much clutter, though the
clutter is useful for making sure that all the mechanisms are working.
You can now produce a new version of the interface procedure that does not
print out so much repetitive stuff, and you can extend the inference_engine procedure
so that it copes in an interesting way with a much wider variety of inputs.
You may get some ideas by playing with the online eliza
http://www.cs.bham.ac.uk/research/projects/cogaff/eliza
or running the version that is included with poplog like this. Type, to pop11
uses eliza
that compiles the eliza program, and then prints out a command to run it:
Please type
eliza();
You can then run it and see what happens.
It will print out some instructions and then you can interact with it. It
prints out all its answers in upper case, e.g.
Good day what is your problem?
? I am talking to the pop11 eliza program I think
STUDYING ARTIFICIAL INTELLIGENCE WOULD OPEN YOUR MIND.
? can i open your mind
WHY DO SOME PEOPLE TALK ONLY ABOUT THEMSELVES?
? your mind is your mind
WHAT IF MY MIND WERE NOT REALLY MY MIND
? bye
BYE FOR NOW.
etc.