Lisa - Common lisp expert system
Table of Contents
1. About the project
- github repo
- reference guide (in the wiki)
- MIT License
- By David E. Young
- stands for "Lisp-based Intelligent Software Agents"
2. cheatsheet
(cl:defpackage #:foo (:use #:lisa-lisp)) (in-package #:foo) ;; slot must be lisa:slot (deftemplate <class-name> (#|no &key supported yet|#) (slot <slot-name>) (slot <slot-name> <default-value>)) (deffacts <fact-group-name> (#|no &key supported yet|#) (<class-name> (<slot-name> <slot-value>)*)) (defrule <rule-name> (&key (salience 0) (context nil) (belief nil) (auto-focus nil)) [patterns] lisa:=> [actions]) ;; patterns: (<slot-name> "value") (<slot-name> (not "value")) (<slot-name> ?value) (<slot-name> (not ?value)) (<slot-name> ?value <constraint>) (logical <patterns>+)
How to neatly use the whole engine as a function:
(in-package #:foo) (defun bar (value &key (runp t)) (with-inference-engine ((inference-engine)) (reset) (let ((?var (make-instace 'foobar :value 42))) (assert (baz (value ?var)))) (when runp (run))))
How to encode triple without being too generic:
(in-package #:foo) (defclass attribute () ((entity :initform nil :initarg :entity :reader entity :documentation "The entity to which this attributes apply to.") (value :initform nil :initarg :value :reader value :documentation "The value of the attribute.")) (:documentation "A mix-in class to easily define attributes on an entity.")) (defclass height (attribute) ()) (defclass age (attribute) ()) (deftemplate person () (slot name)) (let ((?person (assert (person (name "you"))))) (assert (height (entity ?person) (value "180au"))) (assert (age (entity ?person) (value "4by"))))
3. Misc
- you don't have to use
deftemplate, you can use plaindefclass - multiple rules can have exactly the same LHS,
salience,contextand itsconflict resolution strategydetermines which one will fire first.
4. Internals
- AFAICT, a "query-engine" is an inference engine that only have facts.
make-inference-enginejust callsmake-retelisa:inference-enginecallslisa:current-enginewhich returns*lisa::active-engine*- by default, duplicate facts are allowed, use
(setf (allow-duplicate-facts) nil)to disallow it. *binding-table*is used to collect the variables that appears in a rule (both the "pattern" and "action" sides)make-binding-setcollects the values of*binding-table*into a list
~ defrule parses each pattern into parsed-pattern struct