feat(memory): Day M.1 complete - importance scoring system
- Add FrequencyTracker: increment(), get_frequency_score(), get_recency_score(), get_time_decay() - Add EmotionAnalyzer: EMOTION_KEYWORDS dict, extract(), calculate_score(), get_emotion_profile() - Add ImpactEvaluator: evaluate(), get_topic_overlap(), rank_by_impact() - Add ImportanceScorer: composite scoring (freq 35% + recency 20% + emotion 25% + impact 20%) - Update UserMemory model: frequency_count, emotion_tags, importance_score, importance_level, associated_topics - Integrate ImportanceScorer into memory_service.py (recall + importance update) - Add 37 tests for all memory scoring components - Fix urgency patterns: remove overly broad '今天' that matched neutral text - Update memory-update checklist: mark all M.1 tasks complete
This commit is contained in:
52
backend/app/services/memory/impact_evaluator.py
Normal file
52
backend/app/services/memory/impact_evaluator.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
ImpactEvaluator
|
||||
|
||||
Evaluates the breadth of impact a memory has based on associated topics.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.memory import UserMemory
|
||||
|
||||
|
||||
class ImpactEvaluator:
|
||||
"""Evaluate the impact breadth of a memory"""
|
||||
|
||||
# Threshold for maximum impact score
|
||||
IMPACT_THRESHOLD = 5 # Number of associated topics for max impact
|
||||
|
||||
def evaluate(self, memory: "UserMemory") -> float:
|
||||
"""Calculate impact score (0.0 - 1.0)
|
||||
|
||||
The more associated topics a memory has, the higher its impact.
|
||||
Topics represent "what this memory is about" — if it touches
|
||||
many aspects of the user's life, it has high impact.
|
||||
"""
|
||||
associated_topics = memory.associated_topics or []
|
||||
if not associated_topics:
|
||||
return 0.0
|
||||
|
||||
# Normalize: IMPACT_THRESHOLD topics = full impact (1.0)
|
||||
raw_score = len(associated_topics) / self.IMPACT_THRESHOLD
|
||||
return min(1.0, raw_score)
|
||||
|
||||
def get_topic_overlap(self, memory_a: "UserMemory", memory_b: "UserMemory") -> float:
|
||||
"""Calculate topic overlap between two memories (0.0 - 1.0)
|
||||
|
||||
Used for finding related memories.
|
||||
"""
|
||||
topics_a = set(memory_a.associated_topics or [])
|
||||
topics_b = set(memory_b.associated_topics or [])
|
||||
|
||||
if not topics_a or not topics_b:
|
||||
return 0.0
|
||||
|
||||
intersection = topics_a & topics_b
|
||||
union = topics_a | topics_b
|
||||
|
||||
return len(intersection) / len(union) if union else 0.0
|
||||
|
||||
def rank_by_impact(self, memories: list["UserMemory"]) -> list["UserMemory"]:
|
||||
"""Rank memories by impact score (descending)"""
|
||||
return sorted(memories, key=lambda m: self.evaluate(m), reverse=True)
|
||||
Reference in New Issue
Block a user