It’s useful to be able to track how a complex program like Eliza works. It’s
particularly important to know how Eliza decides to display a specific response.
Having the ability to easily follow Eliza’s inner working in real time will enable
us to improve Eliza if we discover a flaw in its logic.
Complex programs trace their execution by using a logger, which is a component
that allows a program to log (i.e. record) interesting events such as important
decisions or errors. In this step, we’ll implement a logger and provide the ability
for the user to turn on and turn off logging, so that they can view Eliza’s logic
while having a conversation.
Defining the logger
A logger essentially provides the ability to:
- Log an information message (e.g. “App is starting...”).
- Log a warning message (e.g. “Unrecognized response encountered”).
- Log an error message (e.g. “Initialization failure occured”).
- Turn on and turn off logging.
Let’s start by defining an interface that presents this behavior. We’ll call it
(what else) ILogger.
At this point, we may be tempted to write a Logger class that implements
ILogger. Let’s hold off doing that for now. After all, the
interface already tells Eliza everything it needs to know for it to be
able to use a logger. As long as the driver program gives Eliza a concrete
implementation of an ILogger when it runs, Eliza can use that to log messages.
Using the logger
If you search the code of the Eliza project for the string:
Console.WriteLine
you’ll find a few (5, to be precise) commented lines of code that trace Eliza’s
reasoning process. If you uncomment these lines and run Eliza, you’ll see the
messages that Eliza logs as it runs. Here’s an example:
In our version of Eliza, messages are logged by writing them to the console
using Console.WriteLine(), because Eliza is a console (command line) application. But
wait a minute! The Eliza class isn’t an application - it’s simply an object
that happens to be used by the console application, ElizaMain.
Nothing prevents Eliza from being used by a Windows application or even an Android
app. Well, nothing except this horrible reliance on Console.WriteLine().
Remember, Windows and Android have no idea what a Console is. So if we want
Eliza to (eventually) be usable on other platforms, we’ll need to replace the calls to
Console.WriteLine() with something that’s supported on those platforms.
Thankfully, we already have a logging mechanism that runs in console,
Windows and Android apps: it’s called ILogger! What this means is:
- We’ll give Eliza an instance of an ILogger (suitable for the platform on which it’s running).
- We’ll replace the calls to Console.WriteLine() with ILogger methods.
Giving Eliza an ILogger
In order for Eliza to use an ILogger, we need to give it one. The best way
to do this is pass it an ILogger in its constructor.
Eliza will store this logger in a private variable and use it whenever it needs.
Let’s also provide Eliza the ability to turn on and turn off logging at any time
during a conversation. We do this by adding this method to the IEliza interface:
and a corresponding implementation to the Eliza class. Notice how Eliza simply
delegates this task to its logger.
Now that Eliza has a logger, let’s use make use of it. We’ll replace all the
calls to Console.WriteLine() (that only work in the world of console applications)
with calls to ILogger methods, that are guaranteed to work on all platforms.
One such call is:
which becomes:
There’s another class that does logging, and that’s DecompReassemblyRule,
which logs its actions in its CanDecompose() method.
In order for this method to also use an ILogger, it needs to receive one.
So let’s pass the method an ILogger.
This will require us to also tweak the Pattern, FormatFreePattern and
ComplexPattern classes. When this is done, Eliza will be ready to use its
ILogger instead of calling Console.WriteLine().
What remains is for the driver program ElizaMain (a console app) to
pass an ILogger to Eliza’s constructor. So let’s do this.
An ILogger for ElizaMain
ElizaMain is a console app. In order to view Eliza’s log messages,
it needs to give Eliza an ILogger that works in the world of console apps.
So let’s create one. We’ll call it ConsoleLogger and add it to the
ElizaMain project.
How does ConsoleLogger work?
STOP. TRY AND WRITE ConsoleLogger YOURSELF. WHEN DONE, OR IF YOU'RE
STUCK, TAKE A LOOK AT THE VERSION IN THE SOLUTION.
We’ll also need to create a similar ConsoleLogger for the ElizaTest project.
Enabling logging during a conversation
Now that we’ve implemented logging, the only thing that remains is giving the
user the means to turn logging on and off during a conversation, so they
can view Eliza’s logic in real time. This means we need to expose the
ILogger.EnableLogging() method to the user.
We’ll do this by recognizing a couple of commands issued by the user that
aren’t part of the conversation. We’ll differentiate commands from normal
user input by requiring them to be prefixed with the $ symbol. For now,
we’ll recognize these two commands:
$LON
$LOFF
that represent “turn on logging” and “turn off logging”. This is done by slightly
modifying the the ElizaMain’s main input/output loop.
Entering $LON or $LOFF in the midst of a conversation will now cause
Eliza to display (or hide) log messages, as seen here.
Where we’ve reached
In this step, we accomplished two important goals:
- We made it possible for the user to view Eliza’s thinking process as it conducts a conversation.
- In doing so, we made the Eliza engine no longer limited to running only within a console app.
Next steps
In the next step, we’ll give Eliza the ability to respond to the powerful emotions
of love and hate. Or at least the illusion of being able to respond to these
emotions.
|