feat(memory): complete M.2-M.5 memory upgrade phases with tests
- M.2: ForgettingCurve, MemoryDecay, MemoryReinforcement (selective forgetting) - M.3: DailyDigestGenerator, ReminderScheduler, ProactiveInformer (proactive reminders) - M.4: MemoryExtractor with LLM-based memory extraction from conversations - M.5: MemoryRecallInjector with token budget control for prompt injection - All phases include comprehensive unit tests (109 tests passing) - Updated checklist.md to mark all tasks complete
This commit is contained in:
72
backend/app/services/memory/reinforcement.py
Normal file
72
backend/app/services/memory/reinforcement.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
MemoryReinforcement
|
||||
|
||||
Triggers memory reinforcement on recall and handles auto-reinforcement.
|
||||
"""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.memory import UserMemory
|
||||
|
||||
|
||||
class MemoryReinforcement:
|
||||
"""Reinforce memories on recall to prevent forgetting."""
|
||||
|
||||
MAX_FREQUENCY = 10
|
||||
AUTO_REINFORCE_BOOST = 1.1 # 10% boost per week
|
||||
|
||||
def trigger(self, memory: "UserMemory") -> "UserMemory":
|
||||
"""Called when memory is recalled: reset decay_score, increment frequency.
|
||||
|
||||
This is the core reinforcement mechanism - each recall makes the memory
|
||||
stickier by resetting its decay curve and incrementing frequency.
|
||||
"""
|
||||
from app.services.memory.forgetting_curve import ForgettingCurve
|
||||
|
||||
# Increment frequency count (capped at MAX_FREQUENCY)
|
||||
current_freq = getattr(memory, "frequency_count", 0) or 0
|
||||
memory.frequency_count = min(current_freq + 1, self.MAX_FREQUENCY)
|
||||
|
||||
# Update last accessed time
|
||||
now = datetime.now(UTC)
|
||||
memory.last_accessed_at = now
|
||||
memory.last_recalled_at = now
|
||||
|
||||
# Reset decay score to near 1.0 (fully retained)
|
||||
curve = ForgettingCurve()
|
||||
memory.decay_score = min(0.95, curve.calculate_decay(memory) + 0.1)
|
||||
|
||||
return memory
|
||||
|
||||
def auto_reinforce(self, memories: list["UserMemory"]) -> list["UserMemory"]:
|
||||
"""Weekly auto-reinforce for high-importance memories.
|
||||
|
||||
Applies a 10% boost to frequency_count for high-importance memories
|
||||
that haven't been accessed recently, keeping them fresh.
|
||||
"""
|
||||
reinforced = []
|
||||
now = datetime.now(UTC)
|
||||
|
||||
for memory in memories:
|
||||
importance_level = getattr(memory, "importance_level", "medium") or "medium"
|
||||
if importance_level != "high":
|
||||
continue
|
||||
|
||||
current_freq = getattr(memory, "frequency_count", 0) or 0
|
||||
if current_freq >= self.MAX_FREQUENCY:
|
||||
continue
|
||||
|
||||
# Apply 10% boost, capped at MAX_FREQUENCY
|
||||
new_freq = min(int(current_freq * self.AUTO_REINFORCE_BOOST + 1), self.MAX_FREQUENCY)
|
||||
memory.frequency_count = new_freq
|
||||
|
||||
# Slightly improve decay score
|
||||
current_decay = getattr(memory, "decay_score", 0.5) or 0.5
|
||||
memory.decay_score = min(0.95, current_decay * 1.05)
|
||||
|
||||
memory.last_accessed_at = now
|
||||
reinforced.append(memory)
|
||||
|
||||
return reinforced
|
||||
Reference in New Issue
Block a user