Class: Llmemory::LongTerm::Episodic::Memory
- Inherits:
-
Object
- Object
- Llmemory::LongTerm::Episodic::Memory
- Includes:
- MemoryModule
- Defined in:
- lib/llmemory/long_term/episodic/memory.rb
Overview
Episodic long-term memory: records agent trajectories and retrieves them by recency, importance and relevance. Designed to coexist with semantic memory (file/graph), not replace it, and to feed reflection (P2), which distills episodes into semantic knowledge.
Recording/retrieval are deterministic and LLM-free by default. Semantic (embedding) retrieval is opt-in via ‘config.episodic_vector_enabled` or by injecting a `vector_store:`; when off, search is keyword-only (unchanged).
Instance Attribute Summary collapse
-
#storage ⇒ Object
readonly
Returns the value of attribute storage.
-
#user_id ⇒ Object
readonly
Returns the value of attribute user_id.
Instance Method Summary collapse
- #count ⇒ Object
- #episodes(limit: nil) ⇒ Object
- #find_episode(id) ⇒ Object
- #forget(ids:, reason: nil) ⇒ Object
-
#initialize(user_id:, storage: nil, vector_store: nil) ⇒ Memory
constructor
A new instance of Memory.
- #list(user_id: nil, limit: nil) ⇒ Object
- #recent_episodes(limit: 10) ⇒ Object
-
#record_episode(steps:, summary: nil, outcome: nil, importance: 0.5) ⇒ Object
Records a trajectory.
-
#search_candidates(query, user_id: nil, top_k: 20) ⇒ Object
Retrieval Engine integration.
- #stats(user_id: nil) ⇒ Object
-
#write(steps:, summary: nil, outcome: nil, importance: 0.5, **_meta) ⇒ Object
— MemoryModule uniform interface —.
Methods included from MemoryModule
Constructor Details
#initialize(user_id:, storage: nil, vector_store: nil) ⇒ Memory
Returns a new instance of Memory.
24 25 26 27 28 29 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 24 def initialize(user_id:, storage: nil, vector_store: nil) @user_id = user_id @storage = storage || Storages.build @vector_store = vector_store @vector_explicit = !vector_store.nil? end |
Instance Attribute Details
#storage ⇒ Object (readonly)
Returns the value of attribute storage.
22 23 24 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 22 def storage @storage end |
#user_id ⇒ Object (readonly)
Returns the value of attribute user_id.
22 23 24 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 22 def user_id @user_id end |
Instance Method Details
#count ⇒ Object
64 65 66 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 64 def count @storage.count_episodes(@user_id) end |
#episodes(limit: nil) ⇒ Object
55 56 57 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 55 def episodes(limit: nil) @storage.list_episodes(@user_id, limit: limit).map { |e| Episode.from_h(e) } end |
#find_episode(id) ⇒ Object
59 60 61 62 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 59 def find_episode(id) raw = @storage.get_episode(@user_id, id) raw && Episode.from_h(raw) end |
#forget(ids:, reason: nil) ⇒ Object
98 99 100 101 102 103 104 105 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 98 def forget(ids:, reason: nil) requested = Array(ids).map(&:to_s) existing = @storage.list_episodes(@user_id).map { |e| (e[:id] || e["id"]).to_s } removed = requested & existing @storage.delete_episodes(@user_id, removed) forget_log.record(@user_id, memory_type: "episodic", ids: removed, reason: reason) removed.size end |
#list(user_id: nil, limit: nil) ⇒ Object
90 91 92 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 90 def list(user_id: nil, limit: nil) episodes(limit: limit) end |
#recent_episodes(limit: 10) ⇒ Object
51 52 53 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 51 def recent_episodes(limit: 10) @storage.list_episodes(@user_id, limit: limit).map { |e| Episode.from_h(e) } end |
#record_episode(steps:, summary: nil, outcome: nil, importance: 0.5) ⇒ Object
Records a trajectory. ‘steps` is an array of hashes with any of :observation, :action, :result, :timestamp. Returns the episode id.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 33 def record_episode(steps:, summary: nil, outcome: nil, importance: 0.5) episode = Episode.new( id: nil, user_id: @user_id, steps: steps, summary: summary || derive_summary(steps), outcome: outcome, importance: importance ) provenance = Llmemory::Provenance.from_text_fingerprint( episode.searchable_text, method: "episode_recording", confidence: episode.importance ) record = episode.to_h.merge(provenance: provenance) id = @storage.save_episode(@user_id, record) index_vector(id, episode.searchable_text) id end |
#search_candidates(query, user_id: nil, top_k: 20) ⇒ Object
Retrieval Engine integration. Returns candidates shaped like the other long-term memories so the Engine can rank episodes by relevance, recency (temporal decay) and importance (P3), with provenance (P10). Hybrid (vector + keyword) when a vector store is active; otherwise keyword-only.
73 74 75 76 77 78 79 80 81 82 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 73 def search_candidates(query, user_id: nil, top_k: 20) uid = user_id || @user_id return [] unless uid == @user_id keyword = @storage.search_episodes(uid, query).first(top_k).map { |e| candidate_for(e, 1.0) } vs = vector_store return keyword unless vs merge_candidates(vector_candidates(query, top_k, vs), keyword, top_k) end |
#stats(user_id: nil) ⇒ Object
94 95 96 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 94 def stats(user_id: nil) { episodes: count } end |
#write(steps:, summary: nil, outcome: nil, importance: 0.5, **_meta) ⇒ Object
— MemoryModule uniform interface —
86 87 88 |
# File 'lib/llmemory/long_term/episodic/memory.rb', line 86 def write(steps:, summary: nil, outcome: nil, importance: 0.5, **) record_episode(steps: steps, summary: summary, outcome: outcome, importance: importance) end |