feat: add long-term memory systems with fused retrieval and memory explorer

Phase 5 (ADR-0003). pkg/concepts fixes the epistemic vocabulary
(observation, fact, memory, claim, hypothesis, belief, prediction,
assumption, inference, opinion), memory kinds and relation names.

internal/memory stores episodic, semantic, procedural and
autobiographical memories in one mind-scoped table with trust-derived
credibility, importance, salience and decaying activation. Retrieval
fuses full-text rank (any-term by default), optional pgvector similarity,
recency, activation, importance and credibility, and returns the
component scores so every recall can be explained. Decay lowers
activation and never deletes; retraction and supersession keep rows for
provenance. Relationships are constrained to one mind at insert time.
Embeddings live in a conditional pgvector table keyed by model so vector
spaces are never mixed; without an embedder the system degrades to text
and graph retrieval.

Attention gates encoding: the attend handler turns admitted observations
into episodic memories (operator changes become autobiographical ones)
with memory_created events; the retrieve process proposes recalled
memories as candidates and admitted recalls are reinforced with
memory_retrieved events carrying their scoring components.

Memory explorer UI (search with component breakdown, timeline, filters,
operator knowledge entry), memory detail with provenance, relationships,
citing events and an inaccurate-memory correction; API; noemactl memory
commands; decay and embed-missing background jobs.
This commit is contained in:
Andy Dixon
2026-06-20 23:47:50 +01:00
parent e9c1b25c80
commit 03631e5685
30 changed files with 1957 additions and 25 deletions
+89
View File
@@ -0,0 +1,89 @@
// Package concepts defines the epistemic vocabulary Noema refuses to collapse:
// the difference between an observation, a claim, a hypothesis and a belief is
// carried on every memory, belief and evidence link (spec §0.1).
package concepts
// Epistemic classifies how something is known.
type Epistemic string
// Epistemic kinds.
const (
Observation Epistemic = "observation" // directly perceived through a channel
Fact Epistemic = "fact" // verified against an authoritative source
Memory Epistemic = "memory" // recalled episode; subject to distortion
Claim Epistemic = "claim" // asserted by someone else, unverified
Hypothesis Epistemic = "hypothesis" // proposed explanation under evaluation
Belief Epistemic = "belief" // held with confidence derived from evidence
Prediction Epistemic = "prediction" // expectation about the future
Assumption Epistemic = "assumption" // taken as given without evidence
Inference Epistemic = "inference" // derived by reasoning from other items
Opinion Epistemic = "opinion" // preference or judgement, not truth-apt
)
// AllEpistemic lists kinds in a stable order.
var AllEpistemic = []Epistemic{Observation, Fact, Memory, Claim, Hypothesis, Belief, Prediction, Assumption, Inference, Opinion}
// Valid reports whether e is known.
func (e Epistemic) Valid() bool {
for _, k := range AllEpistemic {
if k == e {
return true
}
}
return false
}
// MemoryKind is the storage system a memory belongs to.
type MemoryKind string
// Memory kinds (spec §8).
const (
Episodic MemoryKind = "episodic"
Semantic MemoryKind = "semantic"
Procedural MemoryKind = "procedural"
Autobiographical MemoryKind = "autobiographical"
)
// AllMemoryKinds lists kinds in a stable order.
var AllMemoryKinds = []MemoryKind{Episodic, Semantic, Procedural, Autobiographical}
// Valid reports whether k is known.
func (k MemoryKind) Valid() bool {
for _, x := range AllMemoryKinds {
if x == k {
return true
}
}
return false
}
// Relation labels an edge between memories or entities.
type Relation string
// Relations (spec §24).
const (
RelatedTo Relation = "related_to"
CausedBy Relation = "caused_by"
Contradicts Relation = "contradicts"
Supports Relation = "supports"
DependsOn Relation = "depends_on"
BelongsTo Relation = "belongs_to"
DiscussedWith Relation = "discussed_with"
ObservedDuring Relation = "observed_during"
DerivedFrom Relation = "derived_from"
SimilarTo Relation = "similar_to"
Follows Relation = "follows"
)
// AllRelations lists relations in a stable order.
var AllRelations = []Relation{RelatedTo, CausedBy, Contradicts, Supports, DependsOn, BelongsTo, DiscussedWith, ObservedDuring, DerivedFrom, SimilarTo, Follows}
// Valid reports whether r is known.
func (r Relation) Valid() bool {
for _, x := range AllRelations {
if x == r {
return true
}
}
return false
}