WebLS User's Guide, v1.0


Contents

Introduction and Purpose
Installation
Tech Support and Enhancements
The Problem
The Solution
System Architecture
A Sample Tech Support Session
Logic-Base Contents Overview
Logic-Base Execution Overview
Building the Logic-Base
Debugging Logic-Bases
Syntax Summary
Upgrading to Amzi! Prolog + Logic Server
Enhancements and Bug Fixes by Release


Introduction and Purpose

The World Wide Web is growing exponentially, and so is the quantity of information on it. Indexes, FAQs, technical notes, product specs and search engines are common, but the future holds the promise of sophisticated and intelligent information delivery. Integrating intelligent components, agents and logic-bases with web pages can fulfill this promise.

WebLS™ (pronounced webbles) is a system that can answer tech support questions, diagnose problems, recommend configurations, advise web surfers and similar tasks. This implementation focuses on answering tech support questions, but you are encouraged to use it for anything you can dream up. (If you need additional features to support your efforts, let us know).

WebLS is implemented using the standard CGI or Win-CGI interface. WebLS interprets your 'logic-base' which consists of facts, rules and fragments of HTML that comprise the questions and the answers. Your logic-base is written in a custom scripting language that is designed specifically for building intelligent, web-based components. When WebLS interprets your logic-bases, its goal is to find an answer. To do this it applies rules to the facts at hand and dynamically constructs HTML documents that ask questions to obtain additional facts. All you need to construct your own logic-bases is any text editor, a web browser and a web server.

If this explanation isn't clear, why not give WebLS a try by running Amzi!'s Problem Resolver. Instructions are here.

WebLS is an application that was built using Amzi!™ Prolog + Logic Server™, with some support functions written in 'C'. Prolog is a language with powerful built-in search and pattern matching capabilities and is the heart of WebLS (although you will not see it). The Amzi! Logic Server can also be used to embed intelligent components and agents in C/C++, Java, Delphi, Visual Basic and many other languages and tools.

The purpose of this manual is to show you how to build your own logic-bases using WebLS. This manual assumes you are familiar with HTML (especially forms) and with the CGI or Win-CGI interface. This manual does not attempt to teach you how to build the WebLS application itself. That documention will be forthcoming in the near future.

WebLS is licensed according to the terms of the file license.htm. In essence, WebLS may be used freely by any individual or organization for adding logic-bases to web pages. It may be redistributed only in its entirety, and at no charge (except a nominal media and shipping fee). The included 'C' and Prolog source code (not the logic-bases) may be used only in conjunction with a properly licensed, registered copy of Amzi! Prolog + Logic Server.

Installation

WebLS runs under Windows 95 or NT using standard CGI or Win-CGI. It also runs under BSDI Unix on an x86. All you need is a web server and a text editor to use WebLS.

The following files are included in the WebLS package:

Extract the files from the archive which will create a top-level directory called WebLS and these subdirectories:

To complete installation, do the following:

  1. Copy the contents of cgi-all to your CGI directory.
  2. Windows users copy contents of the cgi-win or cgi-std directory to your CGI directory. Also copy a_mw3d.dll from win-dll to that same directory or somewhere in your PATH.
  3. BSD users copy the contents of the cgi-bsd directory to your CGI directory.
  4. Your CGI directory must be the current directory when amzicgi(.exe) is executed.
  5. Ensure you have a logic-base file in your CGI directory (a sample one is provided that you may overwrite; it is actually a copy of lilrules.pro). Your logic-base must have the name 'logicbse.pro'. If needed, modify the file and directory names in the system definitions at the top of the file.
  6. Modify the FORM tag in bigrules.htm and lilrules.htm to point to your CGI directory and ensure you have the correct name for the executable. For example, under Windows 95 use 'cgi-win/amzicgi.exe', under BSD Unix use 'cgi-bin/amzicgi'.
  7. Add a link in your index.htm to invoke amzicgi. The link reads something like:
    Press here to <A HREF="/cgi-bin/amzicgi.exe">Try Me</A>

If you are anxious to get started right away skip to the section titled 'Building the Logic-Base'.

Tech Support and Enhancements

WebLS is a work in progress. We invite you to visit Amzi!'s home page, http://www.amzi.com for the latest news and updates. Although we cannot guarantee tech support, we will fix all bugs and we will try to answer all queries. Please, please include a copy of your logicbse.pro that is causing the question/problem! (Licensed users of Amzi! Prolog + Logic Server will receive full tech support for WebLS.)

We also welcome any suggestions you have for enhancement, and we are available on a consulting basis to assist you in building your own logic-bases.

Send you questions, comments and queries to mary@amzi.com. Kindly include the word 'WebLS' in the subject line of your message. Thank you.

The Problem

The initial goal of WebLS is to provide a system that can be used by technical support personnel to build and maintain an on-line tech support system that can answer most new user's questions, many existing user's common misunderstandings, and clarify documentation deficiencies and report known bugs (or 'limitations' as some large vendors like to call them). The next phase of development for WebLS will focus on providing web site guides.

Design Requirements

The system needs to be available on many different web servers and usable by many different web browsers. Although Netscape and Microsoft are introducing APIs for communicating directly with web servers, CGI (Common Gateway Interface) is currently the only common standard across servers. However, because the CGI interface fires off a program each time an HTML form is submitted, we need to minimize the number of times this happens. (In the future WebLS will support the new APIs.)

Another requirement is that the state of the dialog needs to be maintained throughout the information gathering process with the user. As multiple users will be using the logic-base simultaneously, the state for each of them must be kept separately. (Netscape's 'cookies' could be used for this purpose but as cookies are not yet widely supported, a different approach is used.)

Finally, and most importantly, an intelligent web page will only be useful if it can be kept up-to-date. And it can only be kept up-to-date if the rules in the logic-base are easy to maintain. In addition, the logic-base needs to take advantage of existing resources, such as other on-line documents, pictures, databases, search engines, etc.

The Solution

WebLS fills the above requirements by integrating a custom logic-base with the CGI interface and HTML. The architecture is shown in the figure below:

Figure: WebLS Architecture

HTML forms are ideal for gathering information from the user. Multiple pieces of information can be gathered at the same time by having multiple fields on a form. HTML documents can be constructed a piece at a time. And, most importantly, they can readily point to other resources, such as other documents, a particular place in another document, other files, directories of files, etc.

A logic-base ties everything together. It contains facts gathered from the user, and rules to determine the problem from the facts. The rules are easy-to-read and declarative so they are easy to maintain.

System Architecture

WebLS has three major subsystems:
  1. The Logic-Base containing the facts, rules and HTML fragments
  2. The Inference Engine that executes the rules
  3. The CGI Interface to the web server

The logic-base is completely independent of the other two subsystems. In fact, it is stored as a separate source code file which is loaded by the inference engine. This is the piece you will be developing for your own intelligent components and agents.

The inference engine is designed specifically for gathering facts and reaching a conclusion based on those facts, and for efficient integration with the web server using the CGI interface. The CGI interface itself is a set of Prolog predicates (functions) for reading the CGI input from the web server and returning the HTML output. The Inference Engine and CGI Interface are provided in an .EXE file, an .XPL file (Amzi! Logic Server load module) and a DLL (for Windows). If you are a licensed owner of Amzi! Prolog + Logic Server, you can modify the EXE and XPL as well. Documentation on this will be forthcoming separately. For the time being, see the comments in the code.

A Sample Tech Support Session

Before we get into how WebLS works, lets take a look at a problem resolution session for an Amzi! user. Start up the Amzi! Tech Support system.

First, you will see an HTML form asking for general information about the your environment and the problem area. In answer to "What program are you running?" answer the "Amzi! IDE". Leave everything else as the default answer. Press submit.

Next, a short dialog takes place between the user and the system in order to narrow down the problem and gather specific details. In this case, for the problem select "Screen/printer font size is too small/large." Press submit.

Once the problem has been determined an answer is presented. The answer can be a couple of paragraphs, or it can be another web document. In this case, a couple of sentences describes how to change the font size.

Logic-Base Contents Overview

A logic-base in WebLS consists of:
  1. facts,
  2. fact attributes,
  3. if-then rules,
  4. answer definitions, and
  5. common notes.

Facts are gathered from the user, or distilled from other facts. Examples of facts are: the error message, the program version, and the operating system. Rules are of the form if-then and check the values of facts to determine the problem. Methods define how to perform input and output using HTML.

Facts

Facts are stored in the logic-base as name-value pairs. For example: the 'environment' fact has the value 'Windows 95', the 'programName' fact has the value 'Amzi! IDE'. Facts can also have multiple values (a list). This is useful for collecting a set of symptoms, for example.

Fact Attributes

For each fact, we define its attributes. The most important attributes are the type of the fact and the method used to ask for its value. WebLS implements three methods using HTML form components. They are:
  1. Fill-in-the-Blank
  2. Select from a Drop-Down List
  3. Answer Yes/No or Not Applicable

Below is an example of each. This is how we ask for the 'errorCode' fact. The 5 is the HTML form field length.

attr(errorCode, 
   [
   prompt = $What error code was displayed or 'none'?$, 
   ask = field,
   length = 5
   ]).

This is how we ask for a value from a list. The square brackets are used to bracket the list of choices.

attr(environmentNameVer, 
   [
   prompt = $What environment are you running under?$,
   ask = menu(['Windows 3.x', 'Windows 95', 'Windows NT', 'DOS', 'Linux'])
   ]).
If you wanted to ask for all the environments you create a multivalued fact as shown below. This allows the user to select multiple values from the list using the [ctrl] key and the mouse. You use the 'include' comparator to check multivalued facts (see If-Then Rules below).
attr(environmentNameVer, 
   [
   multivalued,
   prompt = $What environment are you running under?$,
   ask = menu(['Windows 3.x', 'Windows 95', 'Windows NT', 'DOS', 'Linux'])
   ]).

This is how we ask a yes/no question:

attr(usingDCG, 
   [
   prompt = $Are you using DCG in your Prolog module?$,
   ask = yesno
   ]).

Related Facts

In order to reduce the number of interactions with the user, the logic-base supports the concept of related facts. Entirely optional, they allow you to specify what facts are related to each other. It lets you 'group' facts together, so all the facts for a particular hypothesis can be gathered in a single form.

Here is how related facts are added to the fact attributes:

attr(environmentNameVer, 
   [
   prompt = $What environment are you running under?$,
   ask = menu(['Windows 3.x', 'Windows 95', 'Windows NT', 'DOS', 'Linux']),
   related = [memSize, processorType]
   ]).

This says that 'memSize' and 'processorType' are related to 'environmentNameVer'. When the rules are executed, if WebLS is going to ask the user for environmentNameVer, it will also ask for the other two facts as well.

Distilled Facts

Some facts WebLS does not ask the user for values for. Instead they are distilled from rules. Usually this is done to group a set of related facts together. An example of a distilled fact is 'language', which has the value 'C/C++' if the fact 'languageTool' has any one of these values: 'Visual C++', 'Borland C++', 'Watcom C++', etc.

If-Then Rules

The heart of the logic-base is the rules. Rules can either directly determine the problem, or they can determine other facts (distilled facts). Here is a simple rule:
if errorMessage = 'Code too long to load'
then problem = srcbufTooSmall.

It says if the fact 'errorMessage' has the value specified then the problem is 'srcbufTooSmall'. Now there is nothing particularly difficult or remarkable about this rule, nor is there about most of the rules in WebLS. What is notable is that the rules are easy to read and can appear in the logic-base in any order. The WebLS 'inference engine' (that's a fancy way to say search and pattern matching engine) scans the rules looking for ones that 'succeed.' A rule is said to succeed when all of the facts on the 'if' side match correctly.

Before we examine the inference engine, lets look at some more rules and how answers are outputted.

if languageTool = 'Visual C++' or
   languageTool = 'Borland C++' or
   languageTool = 'Watcom C++'
then language = 'C/C++'.

This rule distills the fact 'language' from the user-provided fact 'languageTool'. This allows us to simplify rules such as the following:

if language = 'C/C++' and
   apiFunction = 'lsInit' and
   ((errorMessage = 'GPF (General Protection Fault)' and 
   environmentNameVer = 'Windows 3.x') or
   environmentNameVer = 'DOS')
then problem = cLargeModelRequired.

Note here that more of the power of the rule language becomes evident. We can use 'and', 'or' and parenthesis to check facts. Also, not shown here, is the ability to check if an fact is 'not equal' to a particular value. You can use these comparators: =, \= (not equal), >, <, >= and <=. For checking multivalued facts use 'include' as follows:

if (languageTool = 'Borland C++' or
   languageTool = 'Borland C') and
   symptoms include 'Errors linking with the Logic Server libraries' and
   applicationMode = '16-bit' and
   releaseDate <= '19960302'
then problem = borlandStatic16.

Notice that rules that have determined an answer end with 'problem='. The logic-base has one final piece, that is outputting the answer.

Answer Definitions

For each fact in the rule-base we had to define its attributes. Similarily for every problem, we have to define the nature of the answer and how it is outputted. WebLS offers three methods:
  1. Output HTML text optionally accompanied by a standard header and footer
  2. Output an existing (local) HTML file
  3. Redirect the user to another document or place on the web (by specifying a URL (Uniform Resource Locator)

Lets look at some examples. First outputting HTML text:

answer(cLargeModelRequired,
   [
   text = [$16-bit C/C++ applications require the large memory model. $,
           $Failure to use it leads to immediate GPFs.$]
   ]).

When the problem is 'cLargeModelRequired', the specified text is outputted. The dollar signs are used around strings in the logic-base, and the square brackets are used around lists. So this answer is a list of two strings. The answer text is bracketed by the CGI interface with the following header:

Content-type: text/html

<HTML><HEAD><TITLE> Title </TITLE></HEAD>
<BODY><H2> Title </H2>
and footer:
<P><HR>Footer<FONT SIZE=-1><CENTER><I>WebLS™ Freeware by <A HREF="http://www.amzi.com/">Amzi!</A></I>
</CENTER></BODY></HTML>
The system definition for Title is inserted where the word Title appears, and the systems definition for Footer is inserted where the word Footer appears.

If you would rather keep the answers in separate HTML documents, you can send them out as shown in this example:

answer(lsxNotLoaded,
   [
   htmlFile = 'lsxintro.htm'
   ]).

The third approach is to redirect the user to another document. This lets you keep your answers in searchable, browseable documents and use tags to direct the user to the right place. For example:

answer(componentNotInstalled,
   [
   url = 'http://www.amzi.com/install#delphi_component'
   ]).

Common Notes

Another feature of answers is that you can also define a list of notes to accompany the answer. Notes are outputted after the answer separated by a horizontal rule. Notes are defined like text answers as follows:
note(debugEmbed,
   [
   text = [$For more information on debugging embedded Prolog modules $,
           $we suggest you see <A HREF="ftp://ftp.amzi.com/pub/users/amzi/articles/APIDEBUG.TXT">Debugging Hints</A>.$]
   ]).
Notes are used by adding a notes list to the answer. Remember lists are enclosed by square brackets. For example:
answer(cLargeModelRequired,
   [
   text = [$16-bit C/C++ applications require the large memory model. $,
           $Failure to use it leads to immediate GPFs.$],
   note = [debugEmbed, cLibraries]								
   ]).

Logic-Base Execution Overview

Now that we've described the contents of a logic-base, we can examine how it is executed. We mentioned before that an 'inference engine' is used to execute the rules. The inference engine in WebLS is written in Prolog. In fact many of the structures described above that form the logic-base use Prolog components such as strings and lists. The core of the search and pattern matching engine is accomplished in about 35 lines of Prolog code. Here is what it does:
  1. An initial form is displayed to gather information from the user and start a problem resolution session
  2. If the problem can be determined from gathered information, the answer is returned to the user and WebLS exits
  3. If not, a plausible hypothesis is determined
  4. All the facts needed to prove the hypothesis are asked, plus any related facts
  5. The existing fact values are saved in a file on the web server and WebLS exits
  6. The process of finding a hypothesis and asking the user for facts repeats until an answer is found (or until no answer can be found)
  7. Finally a clean-up of the fact files is performed

Lets examine these steps in some detail.

The Initial Form

The initial form is used to gather as much information as possible (without overwhelming the user) to determine a general direction to search for the answer. In Amzi!'s system, we ask for the error message because often it is possible to determine the problem directly from the message alone.

The Prolog-CGI interface reads the form values from the CGI input file and asserts the facts in the logic-base. The initial form is designed such that the fact names are simply taken from the name of the form field. For example:

<INPUT NAME="errorCode" TYPE="TEXT" VALUE="none" ROWS=1 SIZE="10" ALIGN=left>

This gets the value for the fact 'errorCode'.

The first time WebLS is invoked, the web server uses the GET method. Subsequent invocations use the POST method. This makes it easier to distinguish a new problem solving session from an existing one, and fits within the spirit of GET and POST as subsequent forms may cause data to be written to various external files (see 'Future Directions'). The initial form's and all subsequent questions form's headers looks like this:

<FORM METHOD=POST ACTION="\cgi-bin\amzicgi.exe">

WebLS writes the initial form to the CGI output file and exits. When the submit button (on the form) is pressed by the user, WebLS is started again and the form values are sent to it via the CGI interface.

Check for an Answer

When WebLS starts again it reads the form values entered by the user and asserts the facts to WebLS's dynamic database. Next, we run the logic-base to see if a rule can be found, whose facts are all known, and whose values match the criteria specified in the rule. To do this, the inference engine starts with the first rule in the logic-base and continues to examine the rules until one is found or there are no more rules. If the problem was identified, the answer is loaded for that problem and we proceed to the clean-up phase.

If the problem is not found, then we enter a dialog with the user to gather more information.

Find a Hypothesis

To build the next form, we search the logic-base again. This time we look for the first rule whose known facts all match, and make a list of the unknown facts. For each unknown fact, the ask is loaded to output the question to the HTML form. Also for each unknown fact, the ask is used for any and all related facts. The resulting form will be returned to the web server to be displayed to the user.

Save the Known Facts

Before WebLS exits, the facts we already know are saved to a file. The name of the file is generated using a random number and is hidden in the submit button of the form we built above.

Check the Hypothesis

When WebLS is started again, it picks up the name of the fact file from the submit button. It combines the new facts from the user with the facts we already knew and checks for an answer again. This process repeats until we find the problem or run out of hypotheses.

Clean-Up

Finally we delete the fact file that we were saving the known facts in. We also delete any fact files that are more than an hour old. This is necessary because they will be left around when a user aborts out of a question-answer session.

Building the Logic-Base

There are two primary files that you need to create to build your logic-base. The first is your initial form. This gathers general information about the problem in the hopes of determining a direction for further inquiry. The second is the logic-base itself. Both are plain text files that can be created in any editor, although you may want to use an HTML editor to create the form.

Creating the Initial Form

Below is a skeleton for the initial form. You might want to look at lilrules.htm as an example. We suggest you use the same title in the Title and Heading1 (WebLS will do so for any additional questions). You must use the ''POST' form method, and specify the 'Submit' button exactly as shown.
<HTML><HEAD><TITLE> Your Title </TITLE></HEAD>
<BODY>
<H2> Your Title </H2>
   Your Introductory Text
<FORM METHOD=POST ACTION="/cgi-bin/amzicgi.exe">
   Your Questions
<INPUT NAME="submitProblem" TYPE="Submit">
</FORM>
</BODY></HTML>

When naming your HTML form fields, we recommend you start with a lowercase letter and not use any special characters. This allows you to use the fact names in your rules without surrounding them with single quotes. (See syntax below). Also to avoid naming conflicts we recommend you NOT use field names of the form _ (otherwise multivalued fields will not work properly).

Try to keep to a single page of questions and think carefully about the default answers if the user doesn't select one. The default answers will become facts in your logic-base and you don't want unexpected results because the user skipped over a couple of questions.

Also if you can get some information (like an error number or message) that leads directly to an answer, try to fit that on as well.

You will find that the initial form changes a lot as you develop your rules.

Logic-Base Header Information

Your logic-base is kept in a file called 'logicbse.pro'. You might want to look at 'lilrules.pro' as a skeleton for a logic-base.

Before you start writing rules, there is some required header information. It reads as shown below. The first four lines define new operators in WebLS and should not be changed or deleted and they must remain at the start of the file.

:- op(790, fx, if).     	% prefix operator
:- op(780, xfx, then).  	% infix operator
:- op(775, xfy, or).   	 	% infix that can be linked
:- op(770, xfy, and).   	% infix that can be linked
:- op(700, xfx, <=).		% infix operator
:- op(700, xfx, include).	% infix operator

The next lines define a number of system variables that are used by the inference engine. These let you set the title and the location of various files and directories. Under Windows they might look like this:

%system('Log File', 'C:\\WebLS\\logs\\trace.htm').
%system('Log File URL', 'file:///C:/WebLS/logs/trace.htm').
system('Results File', 'C:\\WebLS\\logs\\results.pro').
system('Initial Form', $C:\\WebLS\\html\\rptprob.htm$).
system('Temp Directory', $C:\\WebLS\\temp\\$).
system('Body Args', $bgcolor=#CEFAFF text=#000000$).
system('Title', $Amzi! Problem Resolver$).
system('Footer', $<FONT SIZE=-1>Copyright ©1996 Amzi! inc.  All Rights Reserved.$).
system('Form Action', 'Executable Path').
system('Goal', 'problem').
Note double backslashes are needed under Windows because backslash \ is the 'escape' character in strings (exactly like 'C') so \n means a newline character.

Under BSD the file and directory names might look like this:

system('Log File', '/usr/web/amzi/WWW/trace.htm'),
system('Log File URL', 'http://amzi.harvard.net/trace.htm'),
system('Results File', $/usr/web/amzi/logs/results.pro$)),
system('Initial Form', $/usr/web/amzi/WWW/rptprob.htm$),
system('Temp Directory', $/usr/web/amzi/temp/$),

The system variables are as follows:

Writing the First Rule

Now with the preliminaries out of the way you can write your first rule. Start by using one of the facts on your initial form. Here's the basic structure:
if fact = value
then problem = problemID.

answer(problemID,
   [
   text = [$ resolution for problemID $,
           $ more resolution for problemID $]
   ]).

Fact and value are 'atoms.' As such if they begin with an uppercase letter, have spaces in them or contain any characters other than letters, numbers and underscores, then they must be enclosed in single quotes ' '.

The answer in this simple case is a list of strings. Strings in WebLS are enclosed in dollar signs $ $. A list of strings is separated by commas and enclosed in square brackets [ ].

Save your rules as 'logicbse.pro' and put that file in your Windows CGI directory or whatever the current directory is when amzicgi is executed.

Test your logic-base by adding a link to a web page that launches amzicgi. This link can look like this:

Press here to <A HREF="\cgi-win\amzicgi.exe">Try Me</A>
Or under BSD Unix
Press here to <A HREF="/cgi-bin/amzicgi ">Try Me</A>

If your logic-base is not working, see the section on debugging logic-bases.

Asking for Additional Facts

Next write a rule that needs to obtain additional information from the user. In addition to the if-then rule and answer defined above, you also need to add an attr definition to generate the HTML form field that asks the question.

Now you should have a logic-base that looks like this:

attr(fact2,
   [
   prompt = $ fact2 question text $,
   ask = menu([choice1, choice2, choice3])
   ]).

if fact = value and
   fact2 = value2
then problem = problemID.

answer(problemID,
   [
   text = [$ answer text for problemID $,
           $ more answer text for problemID $]
   ]).

Menu choices are also atoms and must be quoted if they do not follow the rules outlined above.

Related Facts

The most important performance enhancement you can do with your logic-base is to group related facts together. This is done as follows:

attr(fact2,
   [
   prompt = $ fact2 question text $,
   ask = menu([choice1, choice2, choice3]),
   related = [fact2, fact3, fact4]
   ]).

This says the list containing fact2,3 and 4 is related to fact1. Whenever WebLS asks for the value of fact1, it will ask for the other three values as well. This should optimize the number of times WebLS is invoked and greatly reduce the amount of time your users spend answering questions in order to obtain a resolution to their problem.

Overall Logic-Base Organization

You must keep all the like items together in your logic-base. That is all the if-then rules must be contiguous, as must all the attr, answer and note declarations. So your logic-base looks generally like this:
% Operator definitions
:-op( . . .
% System variables
system( . . .
% Fact Attributes
attr( . . .
attr( . . .
% Rules
if . . . then . . .
if . . . then . . .
% Answers
answer( . . .
answer( . . .
% Notes
note( . . .
note( . . .

No Answer and Special Notes

Before you add more rules, there are some optional items you might want to define in your logic-base. There is one special answer: There are two special notes:

Examples of each of these follows below. Make sure you put these with the other answers or notes in your logic-base.

answer(noAnswer,
   [
   text = [$We are unable to diagnose your problem at this time. $,
           $Please e-mail a description of your problem to $,
           $<A HREF="mailto:support@amzi.com">support@amzi.com</A>. $,
           $You should receive a reply in 48 hours or less.$]
   ]).

%
% systemError is a special note that is appended to an error message
%
note(systemError,
   [
   text = [$<HR>Please write down the above message and what you were $,
           $doing when this problem occurred, including the values you $,
           $typed into the forms.  E-mail this information $,
           $to <A HREF="mailto:info@amzi.com">us</A>.  Thank you.$]
   ]).


%
% answerHeader and answerFooter are special notes that are used to
% bracket the output from sendAnswer
%
note(answerHeader, []).            % this means there is no header

note(answerFooter,
   [
   text = [$<P><HR><P>This is an experimental version of our new automated $,
           $technical support system.  We are very interested in hearing $,
           $your feedback at <A HREF="mailto:info@amzi.com">mary@amzi.com</A>. $,
           $We would be especially grateful if this system does not provide you an answer, if you $,
           $can tell us what your form inputs were and describe your problem. $,
           $We'll e-mail you an answer and try to add it to this system.$,
           $<P>Thank you! $,
           $<P><FONT SIZE=-1>Copyright ©1996 Amzi! inc.  All Rights Reserved.$]
   ]).

More Details on Rules

There are a number of other things you can do with your rules. You check if a value is not equal. For example:

if fact \= value
then problem = problemID.
You can also compare values using >, < >=, <=. This sorts by the ASCII character codes so 'A' < 'B', 'c' > 'D', '2' < 'E'. For example:
if fact > value
then problem = problemID.

if fact <= value
then problem = problemID.

You can use and, or and parentheses to group facts and values. For example:

if fact1 = value1 and (fact2 = value2 or fact3 /= value3)
then problem = problemID.
If you use the HTML tag 'MULTIPLE' on a list in your initial form or add the declaration 'multivalued' to your attr definition, then your users can select multiple values from that list. You can check if a value has been selected with the 'include' operator. For example:
if fact include value
then problem = problemID.

Line breaks are not important. The critical part of the syntax is the word if followed by fact value checks followed by the word then and an fact set equal to a value, followed by a period. Don't forget the period.

Design Considerations

One of the nice features of a logic-base is that the rules can be kept in any order. However you do have to take care that you don't have rules like:
if a1 = v1 then problem = p1.
if a1 = v1 and a2 = v2 then problem = p2.

The second rule will never succeed because if a1 = v1 the first rule will be checked first and will always succeed. In practice, rules of this form are probably poorly written.

Another consideration is that performance will be slightly enhanced if you check facts from the initial form first in the 'if' part of the rule. You can see this for yourself if you look carefully at a trace of your logic-base (see the section on debugging below).

Logic-Base Maintenance

On-going maintenance of the logic-base is very straight-forward. The Amzi! tech support logic-base is organized by problem area. There is a section based on error message alone, then a section on the Amzi! samples, a section on the Amzi! IDE, a section on Prolog predicates, a section on Logic Server API calls and then sections for each of the tools and languages that Amzi! Logic Servers can be embedded into. The rules, attrs and answers follow the same organization.

Everytime tech support writes up an answer for a customer, it forwards it to the logic-base developer who incorporates it into the rules in the appropriate section, and then adds the answer definition for the problem. If the rule requires some new facts, attr definitions (or rules) are created for each one.

The most important consideration when adding new rules, is to use related facts lists wherever possible. This reduces the number of interactions with the users, resulting in less frustration for them, and less load on the web server.

Debugging Logic-Bases

In general you will find that logic-bases for technical support are generally 'flat'. This means that most rules won't depend on multiple layers of distilled facts. So debugging the rules is generally straight-forward.

Syntax Errors

The most common problem you will encounter is typing errors in your logic-base. The two most likely will be missing the period at the end of a rule or output function, and forgetting to quote facts and values that do not start with a lowercase letter, or contain characters other than letters, numbers and underscores.

Syntax errors are reported by WebLS in your browser. The indicator <- NEAR HERE -> will give you an idea of where the error is in the text of your logic-base.

Trace Facility

WebLS includes a trace facility to assist in testing and debugging logic-bases. The trace lists all the known facts, and then shows each rule being tried, indicating which facts match, and which rule or hypothesis matches. When tracing is enabled (by defining a log file at the top of your logic-base), a URL pointing to the output is included on the bottom of each page generated by WebLS. This allows you to examine the trace directly in your browser.

A typical debugging session has you fill-in the answers on the form (in the same manner your users would), then see the trace to determine how the answer was reached, or why another set of facts is being asked for.

Below is a small logic-base and the trace of a particular run of lilrules.pro.

% ------------------------------------------------------------------- %
%                           The Rule-Base                             %
% ------------------------------------------------------------------- %

%
% Required definitions for Amzi!  Do not change or remove.
%
:- op(790, fx, if).     	% prefix operator
:- op(780, xfx, then).  	% infix operator
:- op(775, xfy, or).    	% infix that can be linked
:- op(770, xfy, and).   	% infix that can be linked
:- op(700, xfx, <=).		% infix operator
:- op(700, xfx, include).	% infix operator

system('Log File', 'C:\\WebLS\\logs\\liltrace.htm').
system('Log File URL', 'file:///C:\\WebLS\\logs\\liltrace.htm').
system('Results File', $C:\\WebLS\\logs\\lilrslts.pro$).
system('Initial Form', $C:\\WebLS\\html\\lilrules.htm$).
system('Temp Directory', $C:\\WebLS\\temp\\$).
system('Body Args', $bgcolor=#CEFAFF text=#000000$).
system('Title', $Amzi! Little Problem Resolver$).
system('Footer', $<FONT SIZE=-1>Copyright ©1996 Amzi! inc.  All Rights Reserved.$).
system('Form Action', 'Executable Path').
system('Goal', 'problem').

attr(errorCode, 
     [
     prompt = $What error code was displayed or 'none'?$,
     ask = field,
     length = 5
     ]).

attr(programType, 
     [
     prompt = $What program are you running?$,
     ask = menu(['other', 'Amzi! Hello Program', 'Amzi! Sample Program',
                 'Amzi! IDE', 'My Program', 'Windows Application'])
     ]).

if errorCode = '600' and
   programType = 'Amzi! Hello Program' 
then problem = helloXPL.

if errorCode = '600' and
   programType = 'Amzi! Sample Program' 
then problem = sampleXPL.

if errorCode = '600' and
   programType = 'My Program' 
then problem = missingXPL.

answer(helloXPL, 
       [
       answer = [$The program is unable to locate the Amzi! Prolog object module, an XPL file. $,
                 $Make sure the AMZI installation directory is in your path, and that HELLO.XPL file $,
                 $exists in the path or the current directory.$]
       ]).

answer(sampleXPL, 
       [
       answer = [$The program is unable to locate the Amzi! Prolog object module, an XPL file. $,
                 $Some of the Amzi! samples include source only, $,
                 $and the PRO file needs to be compiled and linked $,
                 $into an XPL file.$,
                 $<P>If the XPL file exists, then it must be in your $,
                 $path or the current directory.$]
       ]).

answer(missingXPL, 
       [
       answer = [$The program is unable to locate the Amzi! Prolog object module, an XPL file. $,
                 $Make sure the the XPL file exists in your path $,
                 $or the current directory.$,
                 $<P>Note: Some development environments such as Visual Basic and $,
                 $Delphi set the current directory to a directory you might not $,
                 $have expected.$]
       ]).

Figure: A Sample Logic-Base

You can see in the listing above the three main parts of the logic-base. There are the rules. These are followed by the attr declarations and the answer declarations.

Logic-Base Debugging Trace
system($Input File$,$C:\WEBSITE\CGI-TEMP\287WS.INI$).
system('Content File','C:\WEBSITE\CGI-TEMP\287WS.INP').
system('Output File','C:\WebSite\cgi-temp\287ws.out').
. . .
system('Log File','C:\AmziCGI\logs\lilrun.htm').
system('Title',$Amzi! Problem Resolver$).
system('Initial Form',$C:\AmziCGI\html\rptprob.htm$).
system('AmziCGI Directory',$C:\AmziCGI$).
system('Form Action','Executable Path').
system('Goal',problem).

cgi('Content Length','191').
cgi('Content Type','application/x-www-form-urlencoded').
. . .
cgi('Executable Path','/cgi-win/cgirun.exe').
cgi('Request Method','POST').
cgi('Request Protocol','HTTP/1.0').

fact(submitProblem,'Submit').
fact(programType,'Amzi! Sample Program').
fact(errorCode,'600').

Processing input file C:\WEBSITE\CGI-TEMP\544WS.INI
Opened fact file: submitProblem
Processing POST method (subsequent times through

---> Calling logic-base for the first time

Trying problem = helloXPL
   Trying errorCode = 600
   Matching errorCode is 600
   Trying programType = Amzi! Hello Program
Failing problem = helloXPL
Trying problem = sampleXPL
   Trying errorCode = 600
   Matching errorCode is 600
   Trying programType = Amzi! Sample Program
   Matching programType is Amzi! Sample Program
Matching problem = sampleXPL

---> Logic-base succeeded in finding an answer: sampleXPL

Figure: Trace of the Sample Logic-Base

This trace illuminates the 'backtracking' search that is inherent in the Prolog language and implemented in the WebLS inference engine. You can see the first rule matches on the errorCode ('600') but fails on the programType. The second rules matches on both and succeeds; that is, it identifies the problem as 'sampleXPL.'

Error Messages

In addition to the trace facility, WebLS reports when attr is not defined for a needed fact and when answer is not defined for a reached conclusion. These errors are returned on an HTML error page designed for the purpose.

Syntax Summary

Below is a summary of all the major parts of the logic-base. Items in italics are provided by you. Items separated by vertical bars | indicate any one of the choices may be selected. Curly brackets {} are used around optional items. Note the placement of square brackets [] to indicate lists and the closing period.

Fact Attributes

attr(fact-name,
   [
   prompt = $prompt-text$,
   ask = field | yesno | menu(['choice1', 'choice2', ... 'choiceN']),
   {related = [fact1, fact2, ... factN]}
   {multivalued}
   ]).

Note each item must be followed by a comma, except the last one. Also note the careful placement of []'s, ()'s and the ending . period.

Rules

if expression then goal = answer.

Expression consists of:

fact operator value {and | or fact operator value}

where operator is either = (equal), \= (not equal), < (less than), > (greater than), <= (less than or equal), >= (greater than or equal) or include. The 'include' operator can only be used with 'multivalued' facts. Expressions can be grouped with parentheses (), and linked together with 'and' and 'or'.

Answer Definitions

answer(answer-name,
   [
   {text = $short-answer-text$,}
   {text = [$long-answer-text-string1$,
            $long-answer-text-string2$,
            . . .
            $long-answer-text-stringN$],}
   {url = 'uniform-resource-locator',}
   {htmlFile = 'html-filename',}
   {note = [note1, note2, ... noteN]}
   ]).
Answers follow the same syntax as fact attributes. Note each item must be followed by a comma, except the last one. Also note the careful placement of []'s, ()'s and the ending . period.

Notes

note(note-name,
   [
   {text = $short-answer-text$,}
   {text = [$long-answer-text-string1$,
            $long-answer-text-string2$,
            . . .
            $long-answer-text-stringN$],}
   ]).

Upgrading to Amzi! Prolog + Logic Server

WebLS is a complete system for implementing small to medium sized tech support systems. As your logic-base grows you might want to increase its speed by compiling the rules, and you may want to add additional features not supported by WebLS. These features might include:

If you would like to do any of these things (or any of the other ideas you dream up that we don't implement), you can purchase the Personal or Professional Edition of Amzi! Prolog + Logic Server. This product includes an Interactive Developer's Environment for developing your logic-base and Prolog inference engine. It also has a compiler for you to compile your logic-base (which usually results in a 10x improvement in execution speed). And it includes the Logic Server libraries that let you embed Prolog components in C/C++, Java, Delphi, Visual Basic, Access, SmallTalk and many other tools and environments. It also has extensions for the Sockets API and ODBC. See our web site, http://www.amzi.com, for product details and pricing information.

Enhancements and Bug Fixes by Release

Enhancements

Release 1.0 Enhancements

Release 0.3 Enhancements

Release 0.2 Enhancements

Bug Fixes

Release 0.4 Bug Fixes

Release 0.2 Bug Fixes

Copyright ©1996 Amzi! inc. All Rights Reserved.