Skip to content

Day 7 — 记忆系统 (Memory)

读完这章你能获得什么:理解 AI Agent 的记忆机制——短期记忆(当前对话)、长期记忆(跨对话持久化)、以及如何在有限的 Token 预算内组装最优的上下文。


一、前情提要

经过 Day 1-6,我们的 Agent 已经相当完整了:能收消息、管会话、能思考、有工具、有技能。但有两个明显的短板:

  1. 对话太长了怎么办?LLM 有上下文长度限制(比如 128K token),不可能把所有聊天记录都塞进去
  2. 新开一次对话,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]

为什么不保留所有消息? 两个原因:

  1. Token 限制:LLM 有上下文长度上限,塞不下了会报错
  2. 注意力稀释:消息太多,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 tokens

Token 计算:优先使用 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 学会"社交"。