Skip to content

feat(memory): implement tier decay, importance evaluation, and reinforced ranking - #25

Merged
liut merged 2 commits into
mainfrom
feat/memory-tier-decay
Jul 30, 2026
Merged

feat(memory): implement tier decay, importance evaluation, and reinforced ranking#25
liut merged 2 commits into
mainfrom
feat/memory-tier-decay

Conversation

@liut

@liut liut commented Jul 30, 2026

Copy link
Copy Markdown
Owner

What

Three-tier memory lifecycle (working / short-term / long-term) with Ebbinghaus decay re-ranking, rule-based importance evaluation on write, and access-count-driven tier promotion on
recall.

Why

Vector similarity alone can't distinguish important preferences from stale chatter — memory_recall quality degrades as memories accumulate.

Changes

  • Data model: added tier, importance_score, last_accessed_at, access_count, decay_rate to convo_memory
  • Write: EvaluateImportance scores content by rule (length + punctuation + keywords), RouteTier assigns tier + decay rate. Upserts preserve tier
  • Retrieval: MatchMemories computes FinalScore = Similarity × R × ForgottenMultiplier (Ebbinghaus R = e^(-t/(24×S))), re-ranks by composite score
  • Reinforcement: recall hits reset last_accessed_at (clock-reset → next R ≈ 1.0), increment access_count, promote tier at configurable thresholds (default: 3 → short-term, 10 →
    long-term). Uses MemorySet + SetWith for correct ORM change tracking
  • Tools: memory_recall returns tier, memory_list supports tier filter, memory_forget cleans up vector row
  • Config: MEMORY_{LONG_TERM,SHORT_TERM,FORGET}_THRESHOLD, MEMORY_PROMOTE_{W2S,S2L}

Key decisions

  • Clock-reset over CalcReinforce: retention is derived from timestamps, resetting last_accessed_at achieves full reinforcement without needing a persistence target for R_new
  • Rule-based importance (no LLM), tier orthogonal to category, decay rate stored per row

liut added 2 commits July 30, 2026 13:01
…rced ranking

- Add tier/importance_score/last_accessed_at/access_count/decay_rate columns
- EvaluateImportance: rule-based scoring (length + punctuation + keywords)
- CalcRetention: Ebbinghaus decay R = e^(-t / 24S) in MatchMemories re-ranking
- RouteTier: auto-assign working/short-term/long-term on write
- PromoteTier: access-count-based tier promotion on recall
- CalcReinforce: R_new = min(1, R + F*(1-R)) on recall hit
- ForgottenMultiplier: x0.1 penalty for memories below threshold
- Fix: delete corpus_vector_400 row on memory_forget
- Add tier filter to memory_list, tier field to memory_recall output
- Configurable thresholds via env vars (MEMORY_LONG_TERM_THRESHOLD etc.)
- Replace direct field assignment with MemorySet + SetWith so dbUpdate
  detects changed columns (last_accessed_at, access_count, tier, decay_rate)
- Remove dead CalcReinforce call: retention is derived from timestamp,
  resetting last_accessed_at to now() already achieves full reinforcement
- Remove redundant CalcRetention pre-computation in reinforce path
- Update plan doc to document the clock-reset reinforcement strategy
@liut
liut merged commit aaa0330 into main Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant