Lisa - Common lisp expert system

Table of Contents

1. About the project

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 plain defclass
  • multiple rules can have exactly the same LHS, salience, context and its conflict resolution strategy determines which one will fire first.

4. Internals

  • AFAICT, a "query-engine" is an inference engine that only have facts.
  • make-inference-engine just calls make-rete
  • lisa:inference-engine calls lisa:current-engine which 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-set collects the values of *binding-table* into a list

~ defrule parses each pattern into parsed-pattern struct

Created: 2025-12-19 Fri 18:45