- Build a system for automobile diagnosis.
- The best way to do this is to try to get one rule
firing. This requires slots, rules, and a user interface.
Once this is done, you can go back and add the other
components. There are bugs in the data below and copying the
verbatim will not teach you much. Try to develop the system in
a logical manner and try to understand what you are doing. The
goal of this lab is not to copy this work, it is to understand
a forward-chaining rule-based system.
Workshop 2 Building a simple forward-chaining system
The purpose of this workshop is to :
· Show you how the objects to be found in a Kappa knowledgebase can be combined to produce a simple, working expert system containing some real knowledge.
· Introduce you to forward-chaining rule-based reasoning.
Load Kappa in the usual way.
1. Classes & slots. If we want to build a simple system, based on
shallow knowledge, we only need to deal with a few objects. In this
case, three class objects will do (together with some rules and a
function), and they can all be subclasses of the 'Root' object. The
idea is simply that we have somewhere to store the pieces of
information that we are using to reach our diagnosis.
· We need somewhere to store evidence gathered from the user. In the
object browser window, click on the class 'Root' and then select 'Add
Subclass' from the Edit menu. Call this new class user_data. Make sure
you use lower case: Kappa is case-sensitive. Double-click on this new
class to edit it: this will put the class editor on screen.. Select
'New' from the 'Slots' menu. When you are asked for a name, call this
new slot tank_empty. Similarly, add slots to this class called
petrol_in_carburettor, engine_turns_over, and lights_turn_on. Then
choose Update.Save to save the class with its slots, and Update.Close
to get the editor off screen.
· We also need to store an intermediate conclusion about whether
petrol is reaching the engine. Click on the class 'root' again, select
'Add Subclass' from the Edit menu, and call this new class
engine. Double-click on this new class to edit it. Select 'New' from
the 'Slots' menu. When you are asked for a name, call this new slot
condition. Save the class as before, and close the editor.
· Add another subclass to Root, called car_problem. Double-click on
this new class to edit it. Select 'New' from the 'Slots' menu. When
you are asked for a name, call this new slot diagnosis. Save the class
but don't close the editor.
· We must also add a monitor to the slot 'diagnosis' that we have just
created, so that it provides some output when it has reached a
conclusion. Choose the 'Methods' menu on the class editor, and 'New'
from this menu, and make a method called report. In the 'Body' section
of the method editor, type this:
PostMessage("The problem with the car is: ", car_problem:diagnosis);
[You should be quite clear what this means. It means that if the class
object called 'car_problem' is asked to do something called 'report',
it will put a message box on screen containing the words "The problem
with the car is: ", followed by whatever it can find in the slot
called 'diagnosis.'] Click on the 'Close' option on the 'Update'
menu. The 'car_problem' class is now capable of reporting what it
knows about the diagnosis, under the right conditions. We want it to
do so whenever something new turns up in the 'diagnosis' slot. Go to
the 'Slots' window in the class editor and double click on
'diagnosis'. Find the 'Monitors' section in the middle of the slot
editor, and find the 'After change' subsection at the end of it. Use
the 6button to get 'Report' on screen and click on it. Then click on
'OK' . Then choose the 'Save' option, and the 'Close' option, from the
'Update' menu of the class editor.
2. Rules. The and/or chart that we saw in Workshop 1 shows us that we will need six rules [refer back to it now]. Each will look for a pattern of data in the slots of the classes/instances we have just created and, if it finds it, will fire and put a piece of data in another slot somewhere. In the 'Edit Tools' window, click on the 'Rule' button and select 'New' to create a new rule. Call this rule empty_tank. In the 'If:' part of the rule editor, type:
user_data:tank_empty #= TRUE;
And in the 'Then:' part type:
{ (car_problem:diagnosis = "run out of petrol");
(engine:condition = no_petrol_reaching_engine) };
You should be very clear what this means. The first part means "Have a
look at the class called 'user_data'. Have a look at the slot in it
called 'tank_empty'. Make sure that what you find there is the word
'TRUE'." The second part, which will only happen if the first part is
successful, says "Have a look at the class called 'car_problem'. Have
a look at the slot in it called 'diagnosis'. Change whatever you find
there to the words "run out of petrol". Next, have a look at the class
called 'engine'. Have a look at the slot in it called
'condition'. Change whatever you find there to the word
no_petrol_reaching_engine".
Notice that "#=" does not mean the same as "=". The first, "#=", means
"check that the slot contains the following value", and it is used in
the conditions of a rule. The second, "=", means "make the value in
the slot the following", and it is used in the conclusions of rules.
Having written the rule, choose the 'Save' option, and the 'Close'
option, from the 'Update' menu of the rule editor.
The next five rules are made in the same way:
· Make a rule called petrol_blocked whose 'If:' part looks like this:
(user_data:tank_empty #= FALSE) And
(user_data:petrol_in_carburettor #= FALSE);
and whose 'Then:' part looks like this:
{ (car_problem:diagnosis = "blocked feed pipe leading to carburettor");
(engine:condition = no_petrol_reaching_engine) };
· Make a rule called petrol_reaching_engine whose 'If:' part looks like
this:
(user_data:petrol_in_carburettor #= TRUE;
and whose 'Then:' part looks like this:
engine:condition = petrol_reaching_engine;
· Make a rule called bad_battery whose 'If:' part looks like this:
(user_data:engine_turns_over #= FALSE) And
(user_data:lights_turn_on #= FALSE) And
(engine:condition #= petrol_reaching_engine);
and whose 'Then:' part looks like this:
car_problem:diagnosis = "battery is defective, or battery cables are
defective.";
· Make a rule called bad_spark_plugs whose 'If:' part looks like this:
(user_data:engine_turns_over #= TRUE) And
(engine:condition #= petrol_reaching_engine);
and whose 'Then:' part looks like this:
(car_problem:diagnosis = "bad spark plugs";
· Make a rule called bad_starter_motor whose 'If:' part looks like this:
(user_data:engine_turns_over #= FALSE) And
(user_data:lights_turn_on #= TRUE) And
(engine:condition #= petrol_reaching_engine);
and whose 'Then:' part looks like this:
car_problem:diagnosis = "starter motor is defective";
Notice the syntax that you have to use when writing a rule. Think of
something like car_problem:diagnosis = "broken down" - that is, a slot
name + some sort of testing of value or allocation of value + the
value in question - as an "expression". If there is just one
expression to be put into the 'if-part' of a rule, you just write it,
with a ";" at the end. Similarly if there's just one expression to be
put into the 'then-part': you just write it, with a ";" at the end. If
there is more than one expression to be put into the 'if-part', you
put round brackets around each expression, and "And" or "Or" between
the expressions (according to what's appropriate), and a ";" after the
last one. If there is more than one expression to be put into the
'then-part', you put round brackets around each expression, and ";"
between the expressions, and you also put "{" at the beginning of the
whole group of expressions and "};" at the end.
3. Functions. In due course, we will want the system to use the rules to come to a conclusion. So we will make a function called 'proceed' that will start rule-based reasoning.
In the 'Edit Tools' window, click on the 'Functions' button and select 'New' to create a new function. Call this function proceed. In the body of the function, type
ForwardChain([NOASSERT]);
4. Now we need to construct the user interface.
a) Click on the 'Session' button in the main KAPPA window. This will
bring up a session window, and it is here that we will build the user
interface to the program. Click on the window then type L to go
into layout mode (alternatively, choose Options.Layout Mode). When you
are more experienced, you will want to use the yellow palette of user
interface images that has just appeared but, for the time being, find
'Image' on the 'Select' menu, and choose 'Check Box' from the
sub-menu. A cross appears in the session window. Move this to a
suitable place - I suggest about a centimetre below the 'Control' menu
- and click once. Now make sure the cursor is inside the
purple-cornered box that has appeared and double-click. In the
CheckBox Options editor, make the following entries: set its title to
No petrol in tank?, its Owner to user_data and its OwnerSlot to
tank_empty. Press the OK button to save it.
b) Now make another check box in exactly the same way: position it
about a centimetre below the first. Give it the following attributes:
set its title to Petrol reaching the carburettor?, its Owner to
user_data and its OwnerSlot to petrol_in_carburettor. Press the OK
button to save it.
c) Now make another, about a centimetre below that one. Set its title
to Engine turns over?, its Owner to user_data and its OwnerSlot to
engine_turns_over. Press the OK button to save it.
d) Now make another, about a centimetre below that one. Set its title
to Lights turn on?, its Owner to user_data and its OwnerSlot to
lights_turn_on. Press the OK button to save it.
e) Now we need a button that the user can click on, in order to get a
diagnosis, once they have used the check boxes to tell the system
everything they know about the four symptoms. As before, find 'Image'
on the 'Select' menu, but this time choose 'Button' from the
sub-menu. Position it about a centimetre below the check boxes, and
give it the following attributes: set its title to Proceed with
diagnosis, and, in the 'Action' box, use the 6button to get 'Proceed'
on screen and click on it. Click on the OK button to save it.
f) Move your cursor so that it is on a part of the session window that
isn't a check box or button or the yellow palette, and
double-click. In the Window Attributes editor, make the title Car
trouble shooter. Press the OK button to save it.
g) Then press L to leave layout mode. The session window should
now look something like this:
o No petrol in the tank?
o Petrol reaching the carburettor?
o Engine turns over?
o Lights turn on?
h) The user interface is now complete and, if you have followed all
the instructions correctly, the expert system should be in good
working order.
5. Testing the system. Click on the check
boxes to switch on or off the various symptoms. Then click on the
'Proceed with diagnosis' button. This should produce a correct
diagnosis: for instance, if only the o No petrol in the tank? box is
ticked, a window should appear on screen, saying
Try out all the combinations of symptoms you can think of, and decide
whether the diagnoses they produce are sensible. If anything doesn't
work, see if you can find out what's gone wrong.
6. Saving. Save the program by going to the file menu in the topmost window on screen and choosing "Save As…". Call it CAR_DIAG.KAL. Save it in your own account, in a suitable folder - 'H:\work', for instance.