Eliza 15 - Almost there!

by Ravi Bhavnani


Now that we have a testable version of Eliza and one that supports synonyms, it's time to add all the remaining patterns, synonyms and decomposition/reassembly rules. As we do this, we'll also add or update the corresponding pattern tests so that our test app is up to date.

It's important that your tests mirror your current code. If you add new functionality but don't write corresponding tests, be prepared for sleepless nights.

The generic response pattern

The first pattern in Eliza's script is the one that causes Eliza to respond with a generic response when it can't map the user's input to a pattern. The current version of Eliza just displays this string when it encounters user input it can't "understand".

Eliza's script requires us to cycle through four generic responses.

We'll implement this by creating a generic response format-free pattern.

And a corresponding test (so we can continue to sleep at night).

We'll need to modify Eliza so it uses the generic response pattern if it can't find another.

A manual test confirms our generic response pattern works as expected. But you should also run ElizaTest to confirm the pattern's test succeeds.

Adding all synonyms

Now it's time to add the remaining synonyms. We'll do this in a separate method called InitializeSynonyms() and call that from Eliza's Initialize() method.

Adding all patterns

After adding all the synonyms, we can go ahead an add the remaining patterns and update existing patterns that referred to synonyms.

And because we like to sleep well at night, we'll add and update the corresponding pattern tests to ensure we have 100% test coverage for every response for every pattern.

The test run confirms all patterns behave correctly.

Tweaking the pattern selection logic

If you run Eliza and tell give it the input "I like chocolate.", it responds with a null response.

Why is this happening? Without adding some logging statements that tell us what Eliza is doing, it's impossible to say. So let's uncomment the Console.WriteLine() lines in the method Eliza.FindMatchingPatternForInput() and rerun Eliza with this input.

We can now see what's going on.

Both IPattern and LikePattern match the input, but LikePattern is selected to generate a response because its rank (10) is higher than that of IPattern (zero). Unfortunately, LikePattern doesn't have a decomposition/reassembly rule that can handle the input and therefore returns a null response.

There are 2 things we need to change:

  1. Modify Eliza's GetResponse() method so that it uses GenericResponsePattern to generate a response if the response of the selected pattern was null.

  2. Increase the rank of IPattern to something greater than LikePattern (indicating the presence of "I" is more significant than the presence of "LIKE", which seems plausible).

After doing (1), Eliza's response is:

After doing (2), Eliza's response is:

Where we've come

Congratulations on following Eliza's journey so far!

Eliza is a far cry from its initial version in which it simply echoed the user's input like a parrot.

It's now able to engage in what appears to be a somewhat "intelligent" (albeit stilted) conversation.

Next steps

Eliza is looking very close to Joseph Weizenbaum's creation. Next, we'll improve upon Eliza's generic response generator and add a few other goodies!

 

Most of the content at this site is copyright © Ravi Bhavnani.
Questions or comments?  Send mail to ravib@ravib.com