Expert Systems and Rule-Based AI

Expert systems were one of the earliest commercial successes of AI, and Prolog is an ideal language for building them. In this chapter we build a complete expert system shell and demonstrate it with practical examples.

Architecture diagram for the Expert Shell example
Figure 10. Architecture diagram for the Expert Shell example

What Is an Expert System?

An expert system is a computer program that emulates the decision-making ability of a human expert. Developed in the 1970s and 1980s during the “Rule-Based AI” era (producing famous systems like MYCIN and DENDRAL), they represent one of the first successful applications of AI to real-world problems.

The standard architecture of an expert system consists of four key components:

  1. Knowledge Base (KB): A database of domain-specific facts and rules (heuristic knowledge) usually structured as “IF-THEN” statements.
  2. Inference Engine: The brain of the system, which applies logical rules to the knowledge base to deduce new information or prove a hypothesis. It can operate via forward chaining (data-driven) or backward chaining (goal-driven).
  3. Explanation Facility: A module that explains the system’s reasoning path to the user, answering “How” a conclusion was reached or “Why” a particular question is being asked.
  4. User Interface: The interactive portal through which the system prompts the user for missing information and displays conclusions.

Prolog is uniquely suited for building expert systems because its core runtime environment already includes an inference engine (SLD resolution) and a backtracking search mechanism.

Building an Expert System Shell in Prolog

Instead of hard-coding an expert system for a single domain, we can build a domain-independent shell. The shell defines the interactive loop, maintains the database of user-supplied facts, and provides explanation utilities, while the specific domain knowledge is loaded from a separate rules file.

To implement the shell, we use Prolog’s dynamic database to store facts provided by the user during a session using known/2 terms. Prolog’s built-in backward-chaining engine automatically executes the rules. When a rule needs an attribute that is not yet known, the shell prompts the user, records the answer, and continues evaluation.

The expert_shell project provides a domain-independent shell. Here is the file expert_shell/prolog/shell.pl:

 1  %% shell.pl - Expert system shell with backward chaining and explanations
 2 :- module(shell, [
 3     consult_expert/1,
 4     explain/1,
 5     ask_question/1
 6 ]).
 7 
 8 :- dynamic known/2.  % known(Attribute, Value) - user-provided facts
 9 
10 %% consult_expert(-Conclusion) - Main entry point
11 consult_expert(Conclusion) :-
12     retractall(known(_, _)),
13     hypothesis(Conclusion),
14     !.
15 
16 %% explain(+Conclusion) - Show reasoning chain
17 explain(Conclusion) :-
18     hypothesis_explanation(Conclusion, Explanation),
19     format("Conclusion: ~w~n", [Conclusion]),
20     format("Reasoning: ~w~n", [Explanation]).
21 
22 %% ask_question(+Attribute) - Ask user for information
23 ask_question(Attribute) :-
24     format("~nWhat is the value of ~w? ", [Attribute]),
25     read(Value),
26     assert(known(Attribute, Value)).
27 
28 %% Hypothesis rules (to be extended in domain-specific knowledge bases)
29 hypothesis(unknown) :-
30     format("Could not determine a conclusion from the given facts.~n").
31 
32 hypothesis_explanation(
33     unknown,
34     'Insufficient data to reach a conclusion.').

Knowledge Acquisition and Rule Representation

Knowledge acquisition is the process of extracting domain knowledge from human experts and structuring it into rules. In a Prolog-based expert system, we represent this knowledge using clauses.

Structuring Rules for the Shell

To plug into our shell, a domain knowledge base must define rules for the hypothesis/1 predicate and explanations for hypothesis_explanation/2. To prompt the user interactively, we define an ask_if/2 helper:

1 ask_if(Attribute, Value) :-
2     known(Attribute, Value), !.
3 ask_if(Attribute, Value) :-
4     \+ known(Attribute, _),
5     ask_question(Attribute),
6     known(Attribute, Value).

A rule in the knowledge base then looks like this:

1 hypothesis(diagnose_internet_issue) :-
2     ask_if(router_lights, off),
3     ask_if(cables_plugged_in, yes).

Improving Readability with Custom Operators

To make rules more readable for non-programmers, Prolog allows you to define custom operators using op/3. For example, we can define operators like if, then, and, and is to write rules in a natural-language-like syntax:

1 :- op(900, xfx, then).
2 :- op(800, xfy, and).
3 :- op(700, xfx, is).
4 
5 % Now we can write rules like:
6 % rule 1: if router_lights is off and cables are connected then problem is router_power.

We can then write a simple parser/meta-interpreter to evaluate these custom-cased rules.

Explanation Facilities

One of the defining features of an expert system is its ability to explain its reasoning.

  • “How” Explanations: Explain how the system reached a specific conclusion. This is done by traversing the proof tree or rule firing history and listing the rules and facts that succeeded.
  • “Why” Explanations: Explain why the system is asking a particular question. When the system prompts the user with a question, the user can type why. The system responds by showing the current rule it is trying to satisfy and the subgoal chain.

In our simplified shell.pl implementation, we provide a basic “How” explanation via explain/1, which fetches the pre-written hypothesis_explanation/2 text associated with the successful hypothesis. In a more advanced system, we can integrate the proof-tree meta-interpreter (from the previous chapter) to dynamically construct and display step-by-step explanations.

Case Study: A Wine Selection Advisor

To demonstrate rule-based reasoning in a practical domain, we look at the wine_advisor project. This advisor acts as a digital sommelier, recommending wines by matching food pairings and flavor profiles.

The system utilizes two distinct categories of rules:

  1. Meal Pairing Rules: Determining which color of wine (red, white, rose) matches the food type (fish, red meat, dessert).
  2. Flavor Preference Rules: Matching the user’s body preference (bold, light, moderate) with the wine’s characteristics (full body, light body, medium body).
Architecture diagram for the Wine Advisor example
Figure 11. Architecture diagram for the Wine Advisor example

The wine_advisor project implements a rule-based wine recommender. Here is the file wine_advisor/prolog/wine_rules.pl:

 1  %% wine_rules.pl - Wine selection expert system
 2 :- module(wine_rules, [
 3     recommend_wine/3
 4 ]).
 5 
 6 %% recommend_wine(+MealType, +Preference, -Wine)
 7 recommend_wine(MealType, Preference, Wine) :-
 8     wine(Wine, Color, Body, _Sweetness),
 9     meal_pairs_with(MealType, Color),
10     preference_matches(Preference, Body).
11 
12 %% Wine database: wine(Name, Color, Body, Sweetness)
13 wine(cabernet_sauvignon, red, full, dry).
14 wine(merlot, red, medium, dry).
15 wine(pinot_noir, red, light, dry).
16 wine(chardonnay, white, full, dry).
17 wine(sauvignon_blanc, white, light, dry).
18 wine(riesling, white, light, sweet).
19 wine(champagne, white, light, dry).
20 wine(rose, rose, light, dry).
21 wine(port, red, full, sweet).
22 
23 %% Meal pairing rules
24 meal_pairs_with(red_meat, red).
25 meal_pairs_with(poultry, red).
26 meal_pairs_with(poultry, white).
27 meal_pairs_with(fish, white).
28 meal_pairs_with(seafood, white).
29 meal_pairs_with(pasta, red).
30 meal_pairs_with(dessert, white).
31 meal_pairs_with(cheese, red).
32 
33 %% Preference matching
34 preference_matches(bold, full).
35 preference_matches(moderate, medium).
36 preference_matches(light, light).
37 preference_matches(any, _).

Case Study: A Fault Diagnosis System

As a final case study, we can implement a Fault Diagnosis System that diagnoses computer network issues. By combining our domain-independent shell (shell.pl) with network diagnostic rules, we can build an interactive system that helps users troubleshoot connectivity problems.

Here is the complete fault diagnosis knowledge base, expert_shell/prolog/fault_diagnosis.pl:

 1  %% fault_diagnosis.pl - Network fault diagnosis rules
 2 :- module(fault_diagnosis, [
 3     diagnose/1
 4 ]).
 5 
 6 :- use_module(shell).
 7 
 8 %% Import ask_question and known from shell
 9 :- reexport(shell).
10 
11 %% ask_if(+Attribute, +Value) - Check fact database or ask user
12 ask_if(Attribute, Value) :-
13     known(Attribute, Value), !.
14 ask_if(Attribute, Value) :-
15     \+ known(Attribute, _),
16     ask_question(Attribute),
17     known(Attribute, Value).
18 
19 %% Knowledge base rules defining hypotheses
20 hypothesis(cable_unplugged) :-
21     ask_if(ethernet_status, disconnected).
22 
23 hypothesis(router_failure) :-
24     ask_if(ethernet_status, connected),
25     ask_if(router_lights, off).
26 
27 hypothesis(dns_configuration_issue) :-
28     ask_if(ethernet_status, connected),
29     ask_if(router_lights, on),
30     ask_if(ping_ip_address, success),
31     ask_if(ping_domain_name, failure).
32 
33 hypothesis(isp_outage) :-
34     ask_if(ethernet_status, connected),
35     ask_if(router_lights, on),
36     ask_if(ping_ip_address, failure).
37 
38 hypothesis(local_software_firewall) :-
39     ask_if(ethernet_status, connected),
40     ask_if(router_lights, on),
41     ask_if(ping_domain_name, success),
42     ask_if(browser_connect, failure).
43 
44 %% Explanations for each conclusion
45 hypothesis_explanation(cable_unplugged,
46     'Your Ethernet cable is disconnected. Please plug it in securely and retry.').
47 hypothesis_explanation(router_failure,
48     'Your router has no power or is failing. Check power cables and cycle the router power.').
49 hypothesis_explanation(dns_configuration_issue,
50     'You can connect to raw IP addresses but not domain names. Your DNS server configuration is likely broken.').
51 hypothesis_explanation(isp_outage,
52     'You cannot ping external IP addresses. This indicates a physical line issue or ISP outage.').
53 hypothesis_explanation(local_software_firewall,
54     'Pings are successful, but browser traffic is blocked. A local firewall or proxy is likely blocking HTTP ports.').

Running the Diagnosis System

You can run the network troubleshooter in the SWI-Prolog REPL:

 1 ?- consult_expert(Conclusion).
 2 
 3 What is the value of ethernet_status? connected.
 4 
 5 What is the value of router_lights? on.
 6 
 7 What is the value of ping_ip_address? success.
 8 
 9 What is the value of ping_domain_name? failure.
10 
11 Conclusion = dns_configuration_issue.
12 
13 ?- explain(dns_configuration_issue).
14 Conclusion: dns_configuration_issue
15 Reasoning: You can connect to raw IP addresses but not domain names. Your DNS server configuration is likely broken.

This case study demonstrates the power of separating the inference logic (defined in the shell) from the domain rules (defined in the fault diagnosis module), allowing you to build new expert systems simply by swapping in different rule bases.