Class: Llmemory::LongTerm::Procedural::Memory

Inherits:
Object
  • Object
show all
Includes:
MemoryModule
Defined in:
lib/llmemory/long_term/procedural/memory.rb

Overview

Procedural long-term memory: a Voyager-style skill library. Agents register reusable skills (prompts, templates, code), retrieve them by relevance to the current task, and report outcomes so proven skills are preferred over unproven ones.

The success rate of each skill is surfaced as importance, so the retrieval Engine ranks battle-tested skills higher (P3). Semantic (embedding) retrieval is opt-in via config.procedural_vector_enabled or by injecting a vector_store:; when off, search is keyword-only.

Constant Summary collapse

KEYWORD_MATCH_SCORE =

Retrieval Engine integration: skills ranked by relevance, recency and proven utility (success rate exposed as importance). Hybrid (vector + keyword) when a vector store is active; otherwise keyword-only.

0.6

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MemoryModule

#forget_log, #forget_log_store, #read

Constructor Details

#initialize(user_id:, storage: nil, vector_store: nil, cipher: nil, forget_log_store: nil) ⇒ Memory

Returns a new instance of Memory.



25
26
27
28
29
30
31
32
# File 'lib/llmemory/long_term/procedural/memory.rb', line 25

def initialize(user_id:, storage: nil, vector_store: nil, cipher: nil, forget_log_store: nil)
  @user_id = user_id
  @cipher = cipher || Llmemory.build_cipher
  @forget_log_store = forget_log_store
  @storage = storage || Storages.build(cipher: @cipher)
  @vector_store = vector_store
  @vector_explicit = !vector_store.nil?
end

Instance Attribute Details

#storageObject (readonly)

Returns the value of attribute storage.



23
24
25
# File 'lib/llmemory/long_term/procedural/memory.rb', line 23

def storage
  @storage
end

#user_idObject (readonly)

Returns the value of attribute user_id.



23
24
25
# File 'lib/llmemory/long_term/procedural/memory.rb', line 23

def user_id
  @user_id
end

Instance Method Details

#countObject



61
62
63
# File 'lib/llmemory/long_term/procedural/memory.rb', line 61

def count
  @storage.count_skills(@user_id)
end

#expired_ids(cutoff:) ⇒ Object

Storage accessor for the TTL maintenance job.



120
121
122
# File 'lib/llmemory/long_term/procedural/memory.rb', line 120

def expired_ids(cutoff:)
  @storage.expired_skill_ids(@user_id, cutoff: cutoff)
end

#find_skill(query) ⇒ Object



47
48
49
50
# File 'lib/llmemory/long_term/procedural/memory.rb', line 47

def find_skill(query)
  raw = @storage.search_skills(@user_id, query).first
  raw && Skill.from_h(raw)
end

#forget(ids:, reason: nil, mode: :soft) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/llmemory/long_term/procedural/memory.rb', line 106

def forget(ids:, reason: nil, mode: :soft)
  requested = Array(ids).map(&:to_s)
  existing = @storage.list_skills(@user_id).map { |s| (s[:id] || s["id"]).to_s }
  targeted = requested & existing
  count = case mode
  when :hard then @storage.delete_skills(@user_id, targeted).to_i
  else @storage.archive_skills(@user_id, targeted).to_i
  end
  forget_log.record(@user_id, memory_type: "procedural", ids: targeted, reason: reason)
  Llmemory::Instrumentation.instrument(:memory_forget, memory_type: "procedural", user_id: @user_id, count: count, mode: mode)
  count
end

#get_skill(id) ⇒ Object



52
53
54
55
# File 'lib/llmemory/long_term/procedural/memory.rb', line 52

def get_skill(id)
  raw = @storage.get_skill(@user_id, id)
  raw && Skill.from_h(raw)
end

#list(user_id: nil, limit: nil, offset: nil) ⇒ Object



98
99
100
# File 'lib/llmemory/long_term/procedural/memory.rb', line 98

def list(user_id: nil, limit: nil, offset: nil)
  skills(limit: limit, offset: offset)
end

#register_skill(name:, body:, description: nil, kind: Skill::DEFAULT_KIND, version: nil, provenance: nil) ⇒ Object

Registers a skill. If version is omitted and a skill with the same name exists, the version auto-increments (skill evolution).



36
37
38
39
40
41
42
43
44
45
# File 'lib/llmemory/long_term/procedural/memory.rb', line 36

def register_skill(name:, body:, description: nil, kind: Skill::DEFAULT_KIND, version: nil, provenance: nil)
  version ||= next_version_for(name)
  skill = Skill.new(
    id: nil, user_id: @user_id, name: name, body: body,
    description: description, kind: kind, version: version, provenance: provenance
  )
  id = @storage.save_skill(@user_id, skill.to_h)
  index_vector(id, skill.searchable_text)
  id
end

#report_outcome(skill_id, success:) ⇒ Object

Records that applying a skill succeeded or failed. Feeds retrieval ranking and adaptive retrieval (P8). Returns the updated Skill.



67
68
69
70
# File 'lib/llmemory/long_term/procedural/memory.rb', line 67

def report_outcome(skill_id, success:)
  raw = @storage.record_outcome(@user_id, skill_id, success: success)
  raw && Skill.from_h(raw)
end

#search_candidates(query, user_id: nil, top_k: 20) ⇒ Object



77
78
79
80
81
82
83
84
85
86
# File 'lib/llmemory/long_term/procedural/memory.rb', line 77

def search_candidates(query, user_id: nil, top_k: 20)
  uid = user_id || @user_id
  return [] unless uid == @user_id

  keyword = @storage.search_skills(uid, query).first(top_k).map { |raw| candidate_for(raw, KEYWORD_MATCH_SCORE) }
  vs = vector_store
  return keyword unless vs

  merge_candidates(vector_candidates(query, top_k, vs), keyword, top_k)
end

#skills(limit: nil, offset: nil) ⇒ Object



57
58
59
# File 'lib/llmemory/long_term/procedural/memory.rb', line 57

def skills(limit: nil, offset: nil)
  @storage.list_skills(@user_id, limit: limit, offset: offset).map { |s| Skill.from_h(s) }
end

#stats(user_id: nil) ⇒ Object



102
103
104
# File 'lib/llmemory/long_term/procedural/memory.rb', line 102

def stats(user_id: nil)
  { skills: count }
end

#write(name:, body:, description: nil, kind: Skill::DEFAULT_KIND, version: nil, provenance: nil, **_meta) ⇒ Object

--- MemoryModule uniform interface ---



90
91
92
93
94
95
96
# File 'lib/llmemory/long_term/procedural/memory.rb', line 90

def write(name:, body:, description: nil, kind: Skill::DEFAULT_KIND, version: nil, provenance: nil, **_meta)
  result = nil
  Llmemory::Instrumentation.instrument(:memory_write, memory_type: "procedural", user_id: @user_id) do
    result = register_skill(name: name, body: body, description: description, kind: kind, version: version, provenance: provenance)
  end
  result
end