Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

Automating Tech Support


Web Development: Automating Support...

Automating Tech Support

It's logical to use the Web and an inference engine

Mary Kroening

Mary, a founder of Amzi! Inc., can be contacted at [email protected].


For software users, the Web is useful for technical support. As a vendor of software, I use the Web to provide my users with the answers they need in the most timely and efficient manner possible.

While search engines, technical notes, and FAQs are fine, they really just scratch the surface of what is possible on the Web. HTML has all the necessary tools to gather information from users and present solutions. The missing piece is a logic base to tie it all together intelligently and efficiently.

In this article, I'll present a system called "WebLS" that's designed to dynamically answer technical-support questions. WebLS is implemented using CGI. The logic base is implemented with the Amzi! Logic Server, which is based on the Prolog language. However, the WebLS design isn't specific to Amzi! or Prolog. With a little work, you can implement the concepts in any language (although Prolog is ideally suited to building logic bases). WebLS is available electronically and at http://www.amzi.com. As of this writing, WebLS runs under Windows 95 and NT. UNIX versions will also become available. WebLS includes full source code, documentation, and sample logic bases.

The Problem

My goal with this project was to build and maintain an online tech-support system that answers questions from new users, clears up common misunderstandings for existing users, clarifies documentation deficiencies, and reports known bugs and their workarounds.

Users need to be able to access online technical support using any web browser. Questions need to be kept specific and easy to answermultiple choice, yes/no, and fill-in-the-blanks. Answers need to be complete and point to any additional resources required to resolve the problem (location of a patch file, sample code, and so on).

For developers, the system needs to be available on many different web servers. Although Netscape and Microsoft are introducing APIs for communicating directly with web servers, CGI is currently the only common standard. However, because the CGI interface fires off a program each time a form is submitted, you need to minimize the number of times this is necessary.

Developers also require that the state of the dialog be maintained throughout the information-gathering process with the user. As multiple users will be using online tech support simultaneously, the state for each user must be kept separately. (Netscape's "cookies" could be used for this purpose, but cookies are not yet widely supported.)

Finally (and most importantly) the tech-support database must be kept up-to-dateand the only way this is possible is if the rules in the logic base are easy to maintain. Additionally, the logic base needs to take advantage of existing resources, such as other online documents, pictures, databases, and search engines.

The Solution

WebLS meets these requirements by integrating a logic base with the CGI interface and HTML; see Figure 1. HTML forms are ideal for gathering information from users. Multiple pieces of information can be gathered at the same time. HTML documents can be constructed a piece at a time. And, most importantly, they can point to other resources, such as other documents, a particular place in another document, other files, directories of files, and more.

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. The logic base knows about related facts so it can batch questions together (to reduce the number of CGI invocations).

In WebLS, I use Prolog's ability to define new operators to implement the rule language, and its powerful, built-in search and pattern-matching capabilities to determine which rules to use in a given situation. These features make Prolog ideal for implementing custom logic bases.

A Sample Tech-Support Session

Before delving into how WebLS works, let's take a look at a problem-resolution session for a user seeking help.

First, WebLS collects general information from the user about the user's environment and the problem area via HTML forms; see Figure 2. In this case, the problem obviously involves the IDE (Integrated Development Environment). Next, a dialog (see Figure 3) occurs between the user and the system to narrow the problem and gather details. In this case, the problem is that the printer font size is too small. Once the problem has been determined, WebLS presents an answer, as in Figure 4. The answer can be a few sentences, or it can be another web document. In this case, a couple of sentences describe how to change the font size.

The Logic Base

A WebLS logic base consists of

  • Facts.
  • Methods for getting the values of facts.
  • Lists of related facts.
  • Rules.
  • Methods for outputting answers.

Facts are either gathered from users or distilled from other facts. Examples of facts include error messages, program version, and operating system. Rules of the If/Then form check the values of facts to determine the problem. Methods define how to perform input and output using HTML.

Facts are stored in the logic base as attribute-value pairs. For example, environment-Windows 95, and programName-Amzi! IDE.

For each fact that can be obtained from a user, a method needs to be defined for requesting it. WebLS implements three methods using HTML form components:

  • Fill-in-the-blank.
  • Select from a drop-down list.
  • Answer yes/no or not applicable.

Example 1(a) shows how to ask for the errorCode attribute. The "5'' is the HTML form-field length, while dollar signs are used to delimit strings. Example 1(b) shows how to ask for a value from a list (the square brackets are used to enclose the list of choices), while Example 1(c) shows how to ask a yes/no question.

There are some facts that WebLS does not ask users for; instead, these facts are distilled from other facts. This is usually done to group a set of related facts together. A typical distilled fact is language, which can have the value C/C++ if the fact languageTool has any of the values Visual C++, Borland C++, or Watcom C++; see Example 2(b).

To reduce the number of interactions with users, the logic base optionally supports the concept of related facts. These facts let you group facts together, so all the facts for a particular hypothesis can be gathered in a single form.

Related facts are specified by adding another entry in the attribute definition of the fact. For example: attr(ideCommand, [... related=[ideProblemType, amziVersion] ...), which says that ideProblemType and amziVersion are related to ideCommand. When the rules are executed, if WebLS is going to ask users for ideProblemType, it will also ask for the other two facts as well.

Rules are at the heart of the logic base. Rules can either directly determine the problem, or they can determine other facts (distilled facts). The simple rule in Example 2(a) says that if the attribute errorMessage has the value specified, then the problem is srcbufTooSmall. There is nothing particularly difficult or remarkable about this rule, nor is there about most of the WebLS rules. What is notable is that the rules are easy to read and can appear in the logic base in any order. This makes it easy to maintain logic bases with hundreds of rules. A Prolog inference engine scans the rules looking for ones that succeed.

Before examining the inference engine, let's look at some more rules and how answers are output. Example 2(b) distills the fact language from the user-provided fact languageTool. This allows you to simplify rules such as those in Example 2(c). The power of the rule language becomes evident here, as you can use Boolean logic to check facts. Rules that have determined an answer end with problem=.

Answers

For each fact in the rule base, you must define how to obtain its value. Similarly for every problem, you have to define how to output the answer. WebLS offers three options, the first two of which can be used in combination:

  • Output HTML text, optionally accompanied by a standard header and footer.
  • Output an existing (local) HTML file.
  • Redirect the user to another document or place on the web (by specifying a URL).
In Example 3(a), which shows how to output HTML text, answer() is the name of a Prolog predicate (procedure). This one is executed when the problem is cLargeModelRequired. The dollar signs are used around strings in the logic base, and the square brackets are used around lists. This answer is a list of two strings. If you would rather keep the answers in separate HTML documents, you can send them out as shown in Example 3(b).

Another approach is to redirect the user to another document. This lets you keep your answers in searchable, browsable documents, and use tags to direct the user to the right place; see Example 3(c).

Calling the Logic Base from CGI

An inference engine is used to execute the rules. The inference engine in WebLS is written in Prolog. In fact, many of the previously described structures (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.

The first thing the code does is display an initial form to gather information from the user and start a problem-resolution session. The initial form is used to gather as much information as possible (without overwhelming the user) in order to determine a general direction to search for the answer. With WebLS, I ask for the error message because it is often possible to determine the problem directly from the message alone. When a user presses the submit button (on the form), WebLS is started and the form values are sent to it via the CGI interface.

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 attribute 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 attribute errorCode. WebLS writes the initial form to the CGI output file and exits.

When WebLS starts again, it reads the form values entered by the user and asserts the facts to the logic base. Next, the inference engine looks for a rule with facts that are all known and with values matching the criteria specified in the rule. If the problem is identified, the answer() is displayed for that problem, and WebLS proceeds to the clean-up phase. If the problem is not found, WebLS enters another dialog with the user to gather more information.

To build the next form, WebLS searches the logic base again, this time looking for the first rule whose known facts all match and makes a list of the unknown facts. For each unknown fact, the attr() definition is used to output the question to the HTML form and look for all related facts. The resulting form will be returned to the web server to be displayed to the user.

Before WebLS exits, the facts 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.

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 known facts and checks for an answer again. This process repeats until the problem is discovered or all hypotheses are exhausted.

Finally, the fact file of known facts is deleted, along with any fact files that are more than an hour old. This is necessary because they remain when users abort a question/answer session.

Debugging Logic-Base Execution

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 straightforward.

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, a URL pointing to the output is included on each page generated by WebLS. This lets you 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.

Listing One is a logic base and the trace of a particular run. You can see in the listing the three main parts of the logic base: the rules, the attr() definitions, and the answer() definitions.

The trace in Listing Two illuminates the backtracking search inherent in Prolog 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 rule matches on both and succeeds; that is, it identifies the problem as sampleXPL.

Logic-Base Maintenance and Design Considerations

Logic base maintenance is straightforward. The Amzi! tech-support logic base is organized by problem area. There is a section based on error messages alone, then a section on the Amzi! samples, a section on the IDE, another on Prolog predicates, one on Logic Server API calls, and sections for each of the tools and languages into which Amzi! Logic Servers can be embedded.

Every time tech support writes up an answer for a customer, they forward 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 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 user and the load on the web server.

When debugging new rules, the trace facility is useful for figuring out which rule succeeds and why. In addition to the trace facility, WebLS reports syntax errors in the logic base; 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 this purpose.

Future Directions

WebLS has many possibilities. The most obvious is to e-mail unresolved problems to tech support. This e-mail would include the known facts, plus a free-form description of the problem. It would also be useful to support large fields and fields with special characters that are saved in external files. Large-field support would be enhanced by adding a contains operator that checks if the field contains a particular word or phrase. A larger goal would be adding some natural-language parsing using knowledge specific to the problem domain. (Prolog is good for this.)

Another useful feature would be to save each transaction in an external database for later analysis. This would allow tech-support managers to keep statistics regarding the use of the online system versus other support methods. It could help development managers to identify the areas where users have the most problems. And it would help the logic-base developer to craft better questions.

Likewise, the online tech-support system could use ODBC (or another means) to connect to, search, and retrieve information from an existing tech-support system or related database. And the tech-support system could save the problems in the customer database so that when a human answers a tech-support call, a log of the customer's use of the online system is made available.

Finally, the logic base and inference engine could be shipped with the product to help new users through the early learning period as they may encounter the same problems and have the same types of questions. Of course, the part shipped with the product would interface with the full web-server-based system.

Figure 1: WebLS architechture.

Figure 3: Tech-support dialog.

Figure 2: An initial HTML form for tech support.

Figure 4: Problem resolution.

Example 1: (a) Asking for the errorCode attribute; (b) asking for a value from a list; (c) asking a yes/no question.

(a)

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


(b)

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


(c)

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

Example 2: (a) A simple rule; (b) typical distilled fact; (c) a simplified rule.

(a)

if errorMessage = 'Code too long to load'
then problem = srcbufTooSmall.


(b)

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


(c)

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

Example 3: (a) A Prolog predicate; (b) keeping answers in separate HTML documents; (c) keeping answers in searchable, browsable documents.

(a)

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


(b)

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


(c)

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

Listing One

% ------------------------------------------------------------------- %
%                           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:\\AmziCGI\\logs\\lilrun.htm').
system('Results Files', 'C:\\AmziCGI\\logs\\results.pro').
system('Title', $Amzi! Problem Resolver$).
system('Initial Form', $C:\\AmziCGI\\html\\testprob.htm$).
system('AmziCGI Directory', $C:\\AmziCGI$).
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.$]
       ]).

Listing Two

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(amziVersion,'3.3 Jan96').
fact(environmentNameVer,'Windows 3.x').
fact(apiFunction,none).
fact(predicate,none).
fact(languageTool,other).
fact(programType,'Amzi! Sample Program').
fact(errorCode,'600').
fact(errorMessage,none).

Processing POST method (subsequent times through)
Opening fact file: submitProblem

---> 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


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.