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.

Retrieval is keyword-based for now (vector search is a follow-up). The success rate of each skill is surfaced as ‘importance`, so the retrieval Engine ranks battle-tested skills higher (P3 importance weighting).

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MemoryModule

#forget_log, #read

Constructor Details

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

Returns a new instance of Memory.



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

def initialize(user_id:, storage: nil)
  @user_id = user_id
  @storage = storage || Storages.build
end

Instance Attribute Details

#storageObject (readonly)

Returns the value of attribute storage.



21
22
23
# File 'lib/llmemory/long_term/procedural/memory.rb', line 21

def storage
  @storage
end

#user_idObject (readonly)

Returns the value of attribute user_id.



21
22
23
# File 'lib/llmemory/long_term/procedural/memory.rb', line 21

def user_id
  @user_id
end

Instance Method Details

#countObject



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

def count
  @storage.count_skills(@user_id)
end

#find_skill(query) ⇒ Object



39
40
41
42
# File 'lib/llmemory/long_term/procedural/memory.rb', line 39

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

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



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

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



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

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

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



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

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).



30
31
32
33
34
35
36
37
# File 'lib/llmemory/long_term/procedural/memory.rb', line 30

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
  )
  @storage.save_skill(@user_id, skill.to_h)
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.



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

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).



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/llmemory/long_term/procedural/memory.rb', line 66

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

  @storage.search_skills(uid, query).first(top_k).map do |raw|
    skill = Skill.from_h(raw)
    {
      id: skill.id,
      text: skill.searchable_text,
      timestamp: skill.created_at,
      score: 1.0,
      importance: skill.success_rate,
      evergreen: false
    }
  end
end

#skills(limit: nil) ⇒ Object



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

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

#stats(user_id: nil) ⇒ Object



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

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

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

— MemoryModule uniform interface —



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

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