92 lines
3.0 KiB
Python
92 lines
3.0 KiB
Python
"""记忆上下文组装与检索结果数据结构(纯领域,无 IO)。
|
|
|
|
``MemoryRetrievalResult`` 持有已加载的 Memory ORM 实例(由 services/memory 检索后传入),
|
|
``build_memory_context`` 把它组装成注入上下文的文本。本模块只接收已加载数据、输出结构化对象。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict
|
|
|
|
from app.core.time import utc_now
|
|
from app.models.memory import Memory
|
|
|
|
|
|
class MemoryRetrievalResult(BaseModel):
|
|
"""记忆检索结果(持有已加载的 Memory ORM 实例列表)。"""
|
|
|
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
|
|
memories: list[Memory]
|
|
|
|
|
|
def _to_aware(dt: datetime | None) -> datetime | None:
|
|
"""确保 datetime 有时区信息,naive 视为 UTC。"""
|
|
if dt is None:
|
|
return None
|
|
if dt.tzinfo is None:
|
|
return dt.replace(tzinfo=UTC)
|
|
return dt
|
|
|
|
|
|
def build_memory_context(result: MemoryRetrievalResult, *, max_length: int = 3000) -> str | None:
|
|
"""把检索到的记忆组装成注入上下文的文本;无记忆时返回 None。
|
|
|
|
按**重要性降序**排列,每条附带重要性标签和相对时间。
|
|
超过 ``max_length`` 字符时截断,避免上下文窗口膨胀。
|
|
"""
|
|
if not result.memories:
|
|
return None
|
|
|
|
now = utc_now()
|
|
|
|
def _mem_sort_key(m: Memory) -> float:
|
|
"""排序键:重要性(越高越优先),同时辅助时间衰减。"""
|
|
ca = _to_aware(m.created_at)
|
|
hours_ago = (now - ca).total_seconds() / 3600 if ca else 0
|
|
decay = max(0.5, 1.0 - hours_ago / 720) # 30 天内缓慢衰减
|
|
return (m.importance or 0.5) * decay
|
|
|
|
sorted_mems = sorted(result.memories, key=_mem_sort_key, reverse=True)
|
|
|
|
lines: list[str] = []
|
|
total_chars = 0
|
|
for idx, item in enumerate(sorted_mems):
|
|
importance_label = _importance_label(item.importance)
|
|
ca = _to_aware(item.created_at)
|
|
hours_ago = (now - ca).total_seconds() / 3600 if ca else 0
|
|
time_label = _time_ago_str(hours_ago)
|
|
line = f"- 「{item.title}」{time_label} [{importance_label}]\n {item.content}"
|
|
line_len = len(line) + 1 # +1 for newline
|
|
if total_chars + line_len > max_length:
|
|
remaining = len(sorted_mems) - idx
|
|
if remaining > 0:
|
|
lines.append(f"…以及 {remaining} 条其他记忆(已截断)")
|
|
break
|
|
lines.append(line)
|
|
total_chars += line_len
|
|
|
|
return "相关记忆:\n" + "\n".join(lines)
|
|
|
|
|
|
def _importance_label(importance: float) -> str:
|
|
if importance >= 0.8:
|
|
return "★重要"
|
|
if importance >= 0.5:
|
|
return "●一般"
|
|
return "○参考"
|
|
|
|
|
|
def _time_ago_str(hours_ago: float) -> str:
|
|
if hours_ago < 1:
|
|
return "刚刚"
|
|
if hours_ago < 24:
|
|
return f"{int(hours_ago)}小时前"
|
|
days = int(hours_ago / 24)
|
|
if days < 30:
|
|
return f"{days}天前"
|
|
months = int(days / 30)
|
|
return f"{months}个月前"
|