Klauseln erzeugen / Expertensystem

Prolog bietet die Möglichkeit, während des Programmablaufs neue Klauseln zu produzieren und das Programm um diese zu erweitern. Damit wird Prolog lernfähig, also ein Werkzeug der KI. Zunächst werden die passenden Prädikate eingeführt, um sie danach für ein selbst lernendes Expertensystem zu nutzen.

Ein Expertensystem ist im Prinzip eine Datenbank, die Fragen stellt und nach passenden Antworten weitere Fragen. Zum Schluss wird ein Datensatz ausgegeben, der zu der Antwortkette gehört. Wird auf eine Frage mit einer unbekannten Antwort reagiert, erfragt das System eine Anschlussfrage, alternativ den auszugebenden Datensatz. Die neue Frage bzw. der Datensatz werden gespeichert und in die nächste Fragenrunde integriert. Frage, Antwort und Nachfolgefrage werden wir zu einem Prädikat zusammen fassen.

Expertensysteme werden in der Medizin eingesetzt, um aus Symptomen eine Diagnose abzuleiten, oder auch in Werkstätten, um Fehler zu finden und die Möglichkeit, sie zu beheben. Die Anzahl der Antworten pro Frage ist nicht begrenzt!

Die Idee

Alternativ muss nach der Beantwortung einer Frage eine Nachfolgefrage oder ein (Diagnose-)Text ausgegeben werden. Im Programm lassen sich Frage, Antwort und Nachfolgefrage/Diagnose durch eine Klausel dat mit drei Parametern darstellen. Soll statt einer neuen Nachfolgefrage eine Diagnose erstellt werden, so antworte man anschließend nach Ausgabe der Diagnose als Frage mit #. Der Programmablauf ist dann:

  1. Es wird getestet, ob ein vorgegebener Text eine Diagnose ist. Falls ja, wird er ausgegeben und das Programm beendet.
  2. Der vorgegebene Text wird als Frage ausgegeben, zu dieser eine Antwort erwartet. Es wird eine dat-Klausel mit zu dieser Frage und Anwort gesucht. Falls vorhanden, liefert dat einen dritten Text, mit dem das Verfahren ab Punkt 1 nochmals durchlaufen wird.
  3. Es wird keine passende dat-Klausel gefunden. Dann muss eine neue angelegt werden. Die Frage ist bekannt, die Antwort in Punkt 2 mit dem Verlassen aber nicht mehr. Die Antwort muss nochmals eingegeben werden, danach die neue Nachfolgefrage bzw. Diagnose. Man kann daraus eine neue dat-Klausel produzieren und dann das Verfahren mit Punkt 1 fortsetzen.

Das Programm soll entsprechend aus drei Klauseln exp(F) bestehen, die als Parameter die Frage bzw. Diagnose übergeben. Nützlich ist eine vierte Klausel, über die das Programm gestartet wird und die die erste Frage stellt. Die Fragen müssen sich unterscheiden, damit das Programm sauber arbeitet.

Die Werkzeuge

Zum Einlesen von Texten verwenden wir das Prädikat readln(T). readln ewartet eine Texteingabe und teilt dann den Text in einzelne Atome auf, die die Variable T in Form einer Liste ersetzen.

Es muss geklärt werden, ob eine Klausel mit einer vorgegebenen Frage und Antwort existiert. Dazu kann man das Prädikat clause(A,B) verwenden.
In clause(K,R) ist K der Kopf eines Prädikats und R der Rumpf. clause(K,R) ist dann wahr, wenn es diese Kombination von Kopf und Rumpf gibt. Alternativ kann K ein Fakt und R der Wert true sein. In diesem Fall testet das Programm, ob es einen Fakt der vorgegebenen Form gibt. Sind in dem Fakt Atome durch Variablen ersetzt, so sucht clause passende Atome zu den Variablen, so dass ein vorhandener Fakt entsteht:

clause(paar(berta,peter),true)

add(X,X+1). clause(add(2,A),true)

testet, ob es den Fakt paar(berta,peter) gibt

X wird ersetzt durch X+1
testet, ob es einen Fakt der Form add(X,X+1) gibt, ersetzt dabei X durch 2 und gibt A = 2+1 aus


Um neue Klauseln in das Programm einzufügen, kann man die Prädikate assert bzw. assertz oder asserta verwenden:
asserta fügt eine Klausel zu Beginn einer Klauselliste zu einem Prädikat ein, assert bzw. assertz am Ende. Als Parameter P in asserta(P), assert(P), assertz(P) kann eine Klausel mit oder ohne Rumpf stehen:

     assert(kind(berta,paul,frieda)) bzw. assert(vater(K,V):-kind(K,V,_))

Hinweis: Benutzt man assert usw. zur Definition einer Klausel, so müssen alle Klauseln des zugehörigen Prädikats über assert usw. definiert werden!

Das Programm

exp(F):-clause(dat(F,[#],_),true),
   write("Diagnose:"),write(F),nl.
exp(F):-write(F),readln(A),clause(dat(F,A,Fn),true),exp(Fn).
exp(F):-write("Antwort wiederholen oder #:"),readln(A),
   write("Neue Frage:"),readln(Fn),asserta(dat(F,A,Fn)),exp(Fn).
anf:-exp([krank]).
dat(F,[#]) suchen mit der Frage F und der Antwort #, bei Erfolg F als Diagnose

sonst Frage F, nach Antwort A dat(F,A,Fn) suchen, exp(Fn) aufrufen
wenn dat(F,A,Fn) nicht vorhanden, dann Antwort nochmals und neue
Nachfolgefrage/Diagnose Fn erfragen, dat(F,A,Fn) anlegen, exp(Fn) aufrufen
Startfrage: [krank]


Es ist schon beeindruckend, dass man mit vier Klauseln ein Expertensystem programmieren kann. Allerdings ist die Ausgabe nicht sehr schön. Um das zu verbessern, kann man ein neues Prädikat ausl definieren, das die Liste als Text ausgibt, wobei jeweils das Komma zwischen zwei Listenelementen durch ein Leerzeichen ersetzt wird:

ausl([]).
ausl([A|R]):-write(A),write(' '),ausl(R).
keine Aktion bei leerer Liste
Liste in erstes Element A und Restliste R aufteilen, A ausgeben, Restliste mit ausl(R) ausgeben

Die write-Prädikate, die Text-Listen ausgeben, sind dann durch ausl zu ersetzen. Andere Möglichkeiten werden später vorgestellt, wenn Schnittstellen zu Windows behandelt werden. Das vollständige Programm findet man unter experte.pl.

Ein Expertensystem führt nicht nur über Abfragen zur Diagnose, sondern kann bei vorgegebener Diagnose passende Fragen und Antworten suchen, also in der Medizin zu einer Diagnose zugehörige Symptome, die erfragt werden. Dies soll in einer Aufgabe geklärt werden.

Aufgabe: Zu einer Diagnose passende Fragen und Antworten finden und ausgeben.