OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / opencog-kb / language-learning / chatbot / test-selflearn.scm

221 lines · scm ref:
;
; test-selflearn.scm - Test vagueness-driven self-learning loop
;
; Tests the 7 layers of emergent capability:
;   Layer 1: Counting - words and pairs exist
;   Layer 2: Association - PMI computed between words
;   Layer 3: Structure - "X contains Y" discovered from PMI
;   Layer 4: Response - answers questions from associations
;   Layer 5: Self-awareness - knows its own capabilities
;   Layer 6: Tool reasoning - PMI-reasons about search plans
;   Layer 7: Autonomous action - executes tools, acquires knowledge
;
; Key metric: bootstrap cost = manual observations needed per layer
;

(use-modules (opencog))
(use-modules (ice-9 rdelim))
(use-modules (ice-9 format))

(primitive-load "atomspace-learner.scm")

;; Load knowledge files (tracks as manual observations)
(define (load-knowledge filename)
  (display (format #f "Loading ~a...\n" filename))
  (let ((port (open-input-file filename))
        (count 0))
    (let loop ((line (read-line port)))
      (if (eof-object? line)
          (begin (close-port port)
                 (display (format #f "  Processed ~a lines\n" count)))
          (begin
            (let ((trimmed (string-trim-both line)))
              (unless (or (string-null? trimmed)
                          (string-prefix? "#" trimmed))
                (process-text-manual! trimmed)
                (set! count (+ count 1))))
            (loop (read-line port)))))))

;; ═══════════════════════════════════════════════════════════════════
;; LAYER TESTS - Each layer builds on the previous
;; ═══════════════════════════════════════════════════════════════════

(define (test-layer-1)
  "Layer 1: Counting - do we have units and pairs?"
  (let ((words (length (get-all-words)))
        (pairs (length (cog-get-atoms 'EdgeLink #t))))
    (display (format #f "  Words: ~a  Pairs: ~a\n" words pairs))
    (display (format #f "  Bootstrap cost: ~a manual observations\n" *manual-observations*))
    (and (> words 0) (> pairs 0))))

(define (test-layer-2)
  "Layer 2: Association - can we compute meaningful PMI?"
  (let* ((test-words '("keypair" "anproto" "grep" "atomspace"))
         (results (filter-map
                    (lambda (w)
                      (let ((assocs (get-associations w 3)))
                        (and (not (null? assocs))
                             (cons w (map cdr assocs)))))
                    test-words)))
    (for-each
      (lambda (r)
        (display (format #f "  ~a → ~a\n" (car r) (string-join (cdr r) ", "))))
      results)
    (display (format #f "  Bootstrap cost: ~a manual observations\n" *manual-observations*))
    (not (null? results))))

(define (test-layer-3)
  "Layer 3: Structure - did we discover 'X contains Y' relations?"
  (let ((evals (length (cog-get-atoms 'EvaluationLink #t))))
    (display (format #f "  EvaluationLinks: ~a\n" evals))
    ;; Show some discovered structures
    (for-each
      (lambda (w)
        (let ((contains (safe-string-list (query-contains w))))
          (when (not (null? contains))
            (display (format #f "  ~a contains: ~a\n" w (string-join contains ", "))))))
      '("atomspace" "keypair" "anproto" "message" "signature"))
    (display (format #f "  Bootstrap cost: ~a manual observations\n" *manual-observations*))
    (> evals 0)))

(define (test-layer-4)
  "Layer 4: Response - can we answer questions from knowledge?"
  (let ((questions '("what is keypair" "what is anproto" "what is atomspace"))
        (pass #t))
    (for-each
      (lambda (q)
        (let ((answer (respond q)))
          (display (format #f "  Q: ~a\n  A: ~a\n" q answer))
          (when (string-prefix? "I don't know" answer)
            (set! pass #f))))
      questions)
    (display (format #f "  Bootstrap cost: ~a manual observations\n" *manual-observations*))
    pass))

(define (test-layer-5)
  "Layer 5: Self-awareness - does it know about its own capabilities?"
  (let ((self-questions '("what is grep" "what is atomspace" "what is wirecore"))
        (pass #t))
    (for-each
      (lambda (q)
        (let ((answer (respond q)))
          (display (format #f "  Q: ~a\n  A: ~a\n" q answer))
          (when (string-prefix? "I don't know" answer)
            (set! pass #f))))
      self-questions)
    (display (format #f "  Bootstrap cost: ~a manual observations\n" *manual-observations*))
    pass))

(define (test-layer-6)
  "Layer 6: Tool reasoning - can PMI form search plans?"
  (let ((test-words '("keypair" "signature" "signing" "verify"))
        (plans-formed 0))
    (for-each
      (lambda (w)
        (display (format #f "  Reasoning about '~a':\n" w))
        (let ((plan (reason-search-plan w)))
          (if plan
              (begin
                (display (format #f "    Plan: ~a\n" (assoc-ref plan 'reasoning)))
                (set! plans-formed (+ plans-formed 1)))
              (display "    BLOCKED - cannot form plan\n"))))
      test-words)
    (display (format #f "  Plans formed: ~a/~a\n" plans-formed (length test-words)))
    (display (format #f "  Bootstrap cost: ~a manual observations\n" *manual-observations*))
    (> plans-formed 0)))

(define (test-layer-7 max-iters)
  "Layer 7: Autonomous action - self-learning loop with PMI-directed search"
  (let ((before-auto *auto-observations*))
    (learning-loop! max-iters 0.005)
    (let ((acquired (- *auto-observations* before-auto)))
      (display (format #f "\n  Self-acquired: ~a observations\n" acquired))
      (display (format #f "  Bootstrap cost: ~a manual, ~a auto\n"
                       *manual-observations* *auto-observations*))
      (display (format #f "  Ratio: 1:~,1f (manual:auto)\n"
                       (if (> *manual-observations* 0)
                           (/ (* 1.0 acquired) *manual-observations*)
                           0)))
      (> acquired 0))))

;; ═══════════════════════════════════════════════════════════════════
;; MAIN TEST
;; ═══════════════════════════════════════════════════════════════════

(define (run-selflearn-test)
  (display "\n")
  (display "════════════════════════════════════════════════════════════\n")
  (display "  SELF-LEARNING TEST: 7 Layers of Emergent Capability\n")
  (display "════════════════════════════════════════════════════════════\n\n")

  ;; Bootstrap: Feed manual data
  (display "BOOTSTRAP: Loading manual knowledge\n")
  (load-knowledge "data/self-knowledge.txt")
  (load-knowledge "data/anproto-knowledge.txt")
  (load-knowledge "data/unix-tools.txt")
  (discover-all-structures!)
  (display (format #f "\n~a\n" (show-stats)))

  ;; Test each layer in sequence
  (let ((results '()))
    (define (run-layer n name test-fn . args)
      (display (format #f "\n── Layer ~a: ~a ──\n" n name))
      (let ((pass (apply test-fn args)))
        (set! results (cons (list n name pass) results))
        (display (format #f "  Result: ~a\n" (if pass "PASS" "FAIL")))))

    (run-layer 1 "Counting" test-layer-1)
    (run-layer 2 "Association" test-layer-2)
    (run-layer 3 "Structure" test-layer-3)
    (run-layer 4 "Response" test-layer-4)
    (run-layer 5 "Self-awareness" test-layer-5)
    (run-layer 6 "Tool reasoning" test-layer-6)
    (run-layer 7 "Autonomous action" test-layer-7 5)

    ;; Post-learning questions
    (display "\n── Post-learning call/response ──\n\n")
    (for-each
      (lambda (q)
        (display (format #f "Q: ~a\n" q))
        (display (format #f "A: ~a\n\n" (respond q))))
      '("what is keypair"
        "what is signature"
        "what is signing"
        "how do you create keypair"
        "what is verification"))

    ;; Summary
    (display "\n════════════════════════════════════════════════════════════\n")
    (display "  LAYER TEST SUMMARY\n")
    (display "════════════════════════════════════════════════════════════\n\n")

    (for-each
      (lambda (r)
        (display (format #f "  Layer ~a (~a): ~a\n"
                         (car r) (cadr r)
                         (if (caddr r) "PASS" "FAIL"))))
      (reverse results))

    (display (format #f "\n  Total bootstrap cost: ~a manual observations\n" *manual-observations*))
    (display (format #f "  Total self-acquired:  ~a auto observations\n" *auto-observations*))
    (display (format #f "  Efficiency ratio:     1:~,1f (manual:auto)\n"
                     (if (> *manual-observations* 0)
                         (/ (* 1.0 *auto-observations*) *manual-observations*)
                         0)))

    ;; AtomSpace state
    (display "\n  AtomSpace:\n")
    (display (format #f "    ConceptNodes:    ~a\n" (length (cog-get-atoms 'ConceptNode #t))))
    (display (format #f "    EdgeLinks:       ~a\n" (length (cog-get-atoms 'EdgeLink #t))))
    (display (format #f "    EvaluationLinks: ~a\n" (length (cog-get-atoms 'EvaluationLink #t))))
    (display (format #f "    Total words:     ~a\n" (length (get-all-words))))

    (display "\n════════════════════════════════════════════════════════════\n")
    (display "  END\n")
    (display "════════════════════════════════════════════════════════════\n")))

(when (and (defined? 'command-line)
           (> (length (command-line)) 1)
           (string=? (cadr (command-line)) "test"))
  (run-selflearn-test))