"""记忆上下文组装与检索结果数据结构(纯领域,无 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}个月前"