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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MemoryModule

#forget_log, #read

Constructor Details

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

Returns a new instance of Memory.



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

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

#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



59
60
61
# File 'lib/llmemory/long_term/procedural/memory.rb', line 59

def count
  @storage.count_skills(@user_id)
end

#find_skill(query) ⇒ Object



45
46
47
48
# File 'lib/llmemory/long_term/procedural/memory.rb', line 45

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

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



98
99
100
101
102
103
104
105
# File 'lib/llmemory/long_term/procedural/memory.rb', line 98

def forget(ids:, reason: nil)
  requested = Array(ids).map(&:to_s)
  existing = @storage.list_skills(@user_id).map { |s| (s[:id] || s["id"]).to_s }
  removed = requested & existing
  @storage.delete_skills(@user_id, removed)
  forget_log.record(@user_id, memory_type: "procedural", ids: removed, reason: reason)
  removed.size
end

#get_skill(id) ⇒ Object



50
51
52
53
# File 'lib/llmemory/long_term/procedural/memory.rb', line 50

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

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



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

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

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

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



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

def register_skill(name:, body:, description: nil, kind: Skill::DEFAULT_KIND, version: 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
  )
  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.



65
66
67
68
# File 'lib/llmemory/long_term/procedural/memory.rb', line 65

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

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.



73
74
75
76
77
78
79
80
81
82
# File 'lib/llmemory/long_term/procedural/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_skills(uid, query).first(top_k).map { |raw| candidate_for(raw, 1.0) }
  vs = vector_store
  return keyword unless vs

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

#skills(limit: nil) ⇒ Object



55
56
57
# File 'lib/llmemory/long_term/procedural/memory.rb', line 55

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

#stats(user_id: nil) ⇒ Object



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

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

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

— MemoryModule uniform interface —



86
87
88
# File 'lib/llmemory/long_term/procedural/memory.rb', line 86

def write(name:, body:, description: nil, kind: Skill::DEFAULT_KIND, version: nil, **_meta)
  register_skill(name: name, body: body, description: description, kind: kind, version: version)
end