主题
Day 7 — Memory 设计文档
设计目标:构建分层记忆系统——短期记忆管理对话窗口、长期记忆持久化跨对话知识、SystemPromptBuilder 在 Token 预算内组装最优上下文。
一、核心接口设计
1.1 ShortTermMemory
| 方法 | 签名 | 说明 |
|---|---|---|
| get_messages | get_messages(session) -> List[ChatMessage] | 获取裁剪后的消息列表 |
| trim | trim(messages, max_messages) -> List[ChatMessage] | 按窗口大小裁剪 |
配置参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| max_messages | 20 | 最多保留的消息条数 |
1.2 LongTermMemory
| 方法 | 签名 | 说明 |
|---|---|---|
| store | async store(user_id, content, category) | 存入一条记忆 |
| recall | async recall(user_id, query, top_k) -> List[str] | 按关键词召回记忆 |
| clear | async clear(user_id) | 清除用户所有记忆 |
存储格式(Markdown 文件):
markdown
## 2025-01-15 14:30:00 | 用户偏好
用户喜欢简洁的回答风格
## 2025-01-15 15:00:00 | 项目信息
用户在做 Python 电商项目1.3 SystemPromptBuilder
| 方法 | 签名 | 说明 |
|---|---|---|
| set_base_prompt | set_base_prompt(prompt: str) | 设置基础提示词 |
| add_skills_layer | add_skills_layer(prompts: List[str]) | 添加技能层 |
| add_tools_layer | add_tools_layer(tools_desc: str) | 添加工具描述层 |
| add_dynamic_context | add_dynamic_context(context: str) | 添加动态上下文 |
| add_memory_layer | add_memory_layer(memories: List[str]) | 添加长期记忆层 |
| build | build(token_budget: int) -> str | 在预算内构建最终 prompt |
二、关键流程图
SystemPromptBuilder 组装流程
短期记忆裁剪流程
三、设计决策与权衡
决策 1:短期记忆用消息条数 vs Token 数量裁剪
| 方案 | 优点 | 缺点 |
|---|---|---|
| 消息条数(选择) | 简单直观,O(1) 操作 | 不同消息长度差异大,实际 Token 用量不稳定 |
| Token 数量 | 精确控制 | 需要逐条计算 Token,复杂 |
| 消息摘要 | 保留更多上下文 | 需要额外 LLM 调用,成本高 |
选择理由:教学阶段用条数裁剪最直观。理解了概念后,改为 Token 裁剪只需修改 trim() 方法。
决策 2:长期记忆用 Markdown 文件
| 方案 | 优点 | 缺点 |
|---|---|---|
| Markdown 文件(选择) | 零依赖、可读性好、Git 友好 | 搜索效率低(O(n) 遍历) |
| SQLite | 本地零配置、结构化查询 | 需要 SQL 知识 |
| 向量数据库 | 语义搜索、最准确 | 需要安装配置、向量化模型 |
选择理由:教学目标是理解"长期记忆的概念和流程",而非实现高效的存储引擎。Markdown 存储让学习者可以直接打开文件查看记忆内容,降低学习门槛。
决策 3:Token 计算的降级策略
python
try:
import tiktoken
encoding = tiktoken.encoding_for_model(model)
token_count = len(encoding.encode(text))
except ImportError:
token_count = len(text) # 1 字符 ≈ 1 token 的粗略估算| 方案 | 优点 | 缺点 |
|---|---|---|
| tiktoken 优先 + 字符数降级(选择) | 有就精确、没有也不报错 | 降级后不够准确 |
| 强制依赖 tiktoken | 始终精确 | 增加安装门槛 |
决策 4:Builder 分层优先级
高优先级的层保证保留,低优先级的层预算不够时裁减:
- 基础 prompt(永远保留)
- 技能 prompt(通常很短,保留)
- 工具描述(影响工具使用能力,优先保留)
- 动态上下文(有用但非必须)
- 长期记忆(最低优先级,可以不带)
理由:没有基础 prompt,Agent 不知道自己是谁;没有工具描述,Agent 无法调用工具。长期记忆是"锦上添花",缺了不影响基本功能。
四、与前序章节的集成点
- 依赖 Day 3:ShortTermMemory 操作 Session.messages
- 依赖 Day 5:Builder 的工具描述层来自 ToolRegistry
- 依赖 Day 6:Builder 的技能层来自 SkillManager.get_active_prompts()
- 被 Day 4 依赖:Builder.build() 替代简单的 system_prompt 字符串传给 AgentRuntime
五、与真实生产系统的对比
| 维度 | miniOpenClaw | 生产级实现 |
|---|---|---|
| 短期记忆 | 滑动窗口(条数) | Token 精确裁剪 + 消息摘要 |
| 长期记忆 | Markdown 关键词搜索 | 向量数据库 + 语义检索 |
| 记忆写入 | 显式调用 | Agent 自动判断 + 结构化提取 |
| Token 计算 | tiktoken / 字符数 | tiktoken 精确计算 |
| 上下文窗口 | 固定预算 | 动态分配(根据任务复杂度调整) |
| 多用户隔离 | 文件名区分 | 数据库行级隔离 + 加密 |