#+TITLE: Stage 2: Reason (reason.lisp) #+AUTHOR: Amr #+FILETAGS: :harness:reason: #+STARTUP: content * Stage 2: Reason (reason.lisp) ** Architectural Intent: Unified Cognition The Reason stage is the cognitive engine of the OpenCortex. It unifies two distinct reasoning modes: 1. **Probabilistic Reasoning:** Consulting neural models (LLMs) to generate action proposals based on current context. 2. **Deterministic Reasoning:** Running those proposals through a series of deterministic safety gates (Policy, Invariants, and Skill-specific validation) to ensure alignment and security. ** Package and Registry We initialize the probabilistic backends and the provider cascade which determines the order in which models are consulted. #+begin_src lisp :tangle ../src/reason.lisp (in-package :opencortex) (defvar *probabilistic-backends* (make-hash-table :test 'equal)) (defvar *provider-cascade* nil) (defvar *model-selector-fn* nil) (defvar *consensus-enabled-p* nil) (defun register-probabilistic-backend (name fn) "Registers a neural provider (e.g., :gemini, :anthropic) with its calling function." (setf (gethash name *probabilistic-backends*) fn)) #+end_src ** Neural Dispatch (Probabilistic) The `probabilistic-call` function manages the cascade of neural providers. If the primary provider fails, it automatically falls back to the next one in the list. #+begin_src lisp :tangle ../src/reason.lisp (defun probabilistic-call (prompt &key (system-prompt "You are the Probabilistic engine.") (cascade nil) (context nil)) "Dispatches a neural request through the provider cascade. Returns a Lisp plist or a failure log." (let ((backends (or cascade *provider-cascade*))) (or (dolist (backend backends) (let ((backend-fn (gethash backend *probabilistic-backends*))) (when backend-fn (harness-log "PROBABILISTIC: Attempting backend ~a..." backend) (let* ((model (when *model-selector-fn* (funcall *model-selector-fn* backend context))) (result (if model (funcall backend-fn prompt system-prompt :model model) (funcall backend-fn prompt system-prompt)))) ;; If the result is valid and doesn't contain a failure log, return it. (unless (or (null result) (and (stringp result) (search ":LOG" result))) (return result)))))) ;; Final fallback if all backends in the cascade fail. (list :type :LOG :payload (list :text "Neural Cascade Failure: All providers exhausted."))))) #+end_src ** Cognitive Proposal (Think) The `think` function represents the "intuitive" side of the agent. It identifies the active skill, assembles the global context, and asks the probabilistic engine for a structured action proposal. #+begin_src lisp :tangle ../src/reason.lisp (defun think (context) "Generates a Lisp action proposal based on current context." (let* ((active-skill (find-triggered-skill context)) (tool-belt (generate-tool-belt-prompt)) (global-context (context-assemble-global-awareness)) (system-logs (context-get-system-logs)) (assistant-name (or (uiop:getenv "MEMEX_ASSISTANT") "Agent"))) (if active-skill (let* ((prompt-generator (skill-probabilistic-prompt active-skill)) (raw-prompt (when prompt-generator (funcall prompt-generator context))) (system-prompt (format nil "IDENTITY: Actuator for ~a. MANDATE: ONE Lisp plist. ~a ~a RECENT_LOGS: ~a" assistant-name global-context tool-belt system-logs))) (if (and raw-prompt (> (length raw-prompt) 1)) (let* ((thought (probabilistic-call raw-prompt :system-prompt system-prompt :context context)) ;; Ensure we are working with a string for read-from-string (cleaned (if (stringp thought) (string-trim '(#\Space #\Newline #\Tab) thought) thought))) (if (stringp cleaned) (handler-case (read-from-string cleaned) (error (c) (list :type :EVENT :payload (list :sensor :syntax-error :code cleaned :error (format nil "~a" c))))) cleaned)) (list :type :LOG :payload (list :text (format nil "Skill '~a' triggered (Deterministic only)" (skill-name active-skill)))))) nil))) #+end_src ** Deterministic Verification Once a proposal is generated, it MUST pass through the deterministic gates. Every skill can register a gate that inspects and potentially modifies or blocks the proposed action. #+begin_src lisp :tangle ../src/reason.lisp (defun deterministic-verify (proposed-action context) "Iterates through all skill deterministic-gates sorted by priority." (let ((current-action proposed-action) (skills nil)) ;; 1. Collect and sort active gates (maphash (lambda (name skill) (declare (ignore name)) (when (skill-deterministic-fn skill) (push skill skills))) *skills-registry*) (setf skills (sort skills #'> :key #'skill-priority)) ;; 2. Execute gates sequentially if their trigger allows (dolist (skill skills) (let ((trigger (skill-trigger-fn skill)) (gate (skill-deterministic-fn skill))) (when (or (null trigger) (ignore-errors (funcall trigger context))) (setf current-action (funcall gate current-action context)) ;; If any gate returns a LOG or EVENT, it has intercepted the action. (when (and (listp current-action) (member (getf current-action :type) '(:LOG :EVENT :log :event))) (harness-log "DETERMINISTIC: Intercepted by skill '~a'" (skill-name skill)) (return-from deterministic-verify current-action))))) current-action)) #+end_src ** The Reason Gate The entry point for the Reason stage. It orchestrates the transition from probabilistic "thought" to deterministic "verification." #+begin_src lisp :tangle ../src/reason.lisp (defun reason-gate (signal) "Unified Stage: Combines Probabilistic proposals and Deterministic verification." ;; Only process events that haven't been reasoned yet. (unless (eq (getf signal :type) :EVENT) (return-from reason-gate signal)) (let ((candidate (think signal))) (if candidate (setf (getf signal :approved-action) (deterministic-verify candidate signal)) (setf (getf signal :approved-action) nil)) (setf (getf signal :status) :reasoned) signal)) #+end_src