主题
Day 7 — 记忆系统 (Memory)
读完这章你能获得什么:理解 AI Agent 的记忆机制——短期记忆(当前对话)、长期记忆(跨对话持久化)、以及如何在有限的 Token 预算内组装最优的上下文。
一、前情提要
经过 Day 1-6,我们的 Agent 已经相当完整了:能收消息、管会话、能思考、有工具、有技能。但有两个明显的短板:
- 对话太长了怎么办?LLM 有上下文长度限制(比如 128K token),不可能把所有聊天记录都塞进去
- 新开一次对话,Agent 就"失忆"了——不记得用户叫什么名字、有什么偏好
短板 1 需要"短期记忆管理"——像人脑的工作记忆,只保留最近的重要信息。 短板 2 需要"长期记忆"——像笔记本,把重要的信息写下来,下次还能查到。
二、生活类比:员工的笔记本
想象一个优秀员工是怎么管理信息的:
便签纸(短期记忆 / ShortTermMemory)
员工桌上贴着便签纸,记录当前正在处理的事情:
- 和客户的最近 10 轮对话
- 便签纸面积有限,旧的写满了就揭掉,贴上新的
- 关机(对话结束)后就丢了
档案柜(长期记忆 / LongTermMemory)
员工有一个档案柜,存放需要长期保存的重要信息:
- "张三喜欢用正式的语气"
- "李四的项目是电商平台,用的 Python"
- 关机后还在,甚至换电脑(重启程序)也还在
每日工作清单(SystemPromptBuilder)
每天上班前,员工会整理一份工作清单:
工作清单 =
基础职责(system_prompt)
+ 当前激活的技能(skills prompts)
+ 可用工具说明(tools description)
+ 今天的日期时间(动态上下文)
+ 相关的长期记忆(从档案柜调出的资料)但清单有字数限制(Token 预算)!不能无限写。所以 SystemPromptBuilder 要聪明地决定哪些信息值得放进去。
三、核心概念详解
3.1 ShortTermMemory——便签纸
ShortTermMemory 管理的是 Session 中的消息历史。核心功能是窗口裁剪:
假设 max_messages = 10(最多保留 10 条)
当前 Session 有 15 条消息:
[msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9, msg10, msg11, msg12, msg13, msg14, msg15]
↑ 最新的
裁剪后只保留最近 10 条:
[msg6, msg7, msg8, msg9, msg10, msg11, msg12, msg13, msg14, msg15]为什么不保留所有消息? 两个原因:
- Token 限制:LLM 有上下文长度上限,塞不下了会报错
- 注意力稀释:消息太多,LLM 的"注意力"被分散,反而回答不好
裁剪策略:目前用最简单的"滑动窗口"——保留最近 N 条。更高级的策略可以基于 Token 数量或消息重要性来裁剪。
3.2 LongTermMemory——档案柜
LongTermMemory 用于跨对话持久化重要信息。
存储方式:每个用户一个 Markdown 文件,每条记忆是一个 section:
markdown
# user_alice 的长期记忆
## 2025-01-15 14:30:00 | 用户偏好
用户喜欢简洁的回答风格,不喜欢啰嗦。
## 2025-01-15 15:00:00 | 项目信息
用户正在做一个 Python 电商项目,使用 FastAPI + PostgreSQL。为什么用 Markdown 而不是数据库?
| 对比 | Markdown 文件 | 数据库 |
|---|---|---|
| 复杂度 | 零依赖,一个文件 | 需要安装配置 |
| 可读性 | 直接打开就能看 | 需要客户端工具 |
| 搜索能力 | 简单关键词匹配 | 全文索引、向量搜索 |
| 适合场景 | 教学、原型 | 生产环境 |
教学阶段用 Markdown 足够了——重点是理解记忆的概念和流程,存储介质可以随时替换。
搜索(recall):用关键词匹配每条记忆的文本。生产环境会用向量数据库做语义搜索,但原理是一样的——给一个查询,返回最相关的记忆条目。
3.3 SystemPromptBuilder——工作清单
这是记忆系统最精妙的部分。它负责在有限的 Token 预算内,组装最优的系统提示词。
组装顺序(优先级从高到低):
┌───────────────────────────┐ ← 最高优先级,一定保留
│ 1. 基础 system_prompt │ "你是 miniOpenClaw..."
├───────────────────────────┤
│ 2. 激活的 Skill prompts │ 翻译/编程等技能提示词
├───────────────────────────┤
│ 3. 工具描述 │ 可用工具列表
├───────────────────────────┤
│ 4. 动态上下文 │ 当前时间、用户信息等
├───────────────────────────┤
│ 5. 长期记忆回忆 │ 从档案柜调出的相关记忆
└───────────────────────────┘ ← 最低优先级,预算不够就裁Token 预算管理:
总预算 = 4096 tokens
基础 prompt 用了 500 → 剩 3596
技能 prompt 用了 300 → 剩 3296
工具描述 用了 200 → 剩 3096
动态上下文 用了 100 → 剩 2996
长期记忆 ← 最多只能用 2996 tokensToken 计算:优先使用 tiktoken(精确),如果没安装则按"1 字符 ≈ 1 token"估算。
四、记忆系统全景流程
五、动手实验指南
5.1 运行示例
bash
python -m miniclaw.memory.short_term # 短期记忆演示
python -m miniclaw.memory.long_term # 长期记忆演示
python -m miniclaw.memory.context # SystemPromptBuilder 演示5.2 改一改,看看会怎样
实验 1:把 ShortTermMemory 的 max_messages 设成 3,然后添加 10 条消息,观察裁剪效果。
实验 2:向 LongTermMemory 存入几条记忆,然后用不同的关键词搜索,看看哪些能被召回。
实验 3:把 SystemPromptBuilder 的 Token 预算设成一个很小的值(如 100),观察哪些层级的信息被裁掉了。
5.3 运行测试
bash
pytest miniclaw/memory/ -v六、常见问题 FAQ
Q1:短期记忆和 Session 的 messages 有什么区别?
A:Session.messages 是完整的对话历史。ShortTermMemory 在此基础上做窗口裁剪——只取最近 N 条发给 LLM。原始历史不会被删,只是"喂给 LLM 的部分"被截断了。
Q2:长期记忆什么时候写入?谁来决定"什么值得记住"?
A:当前实现需要显式调用来存入记忆。更高级的实现可以让 Agent 自己判断"这条信息值得记住"——比如用户说"以后回答请用简洁风格",Agent 可以自动存入长期记忆。
Q3:Token 预算是怎么确定的?
A:通常设为 LLM 上下文窗口大小的一部分(比如 128K 上下文窗口中给 system prompt 预留 4K),剩下的留给对话历史和工具结果。具体数值需要根据实际使用场景调优。
下一章
到这里,我们的 Agent 已经相当完整了——能收消息、能思考、有工具、有技能、有记忆。最后一个问题:怎么和外部生态对接?
下一章 Day 8: MCP 协议 将让 Agent 学会"社交"。