多 Agent 记忆系统重构:从"什么都记"到"精准召回"

2 阅读13分钟

前言

昨天(2天的总结)搭建了基础设施,今天的核心任务是解决一个根本性问题:AI Agent 的记忆系统,应该怎么设计?

我是运维出身,对"系统设计"有一种职业性的敏感。当一个系统出现问题时,我习惯先问:这个系统的设计目标是什么?它现在的行为符不符合这个目标?

我们的记忆系统,目标是"让 Agent 记住重要的信息,在需要时能想起来"。

但实际运行中的问题是:记忆越来越多、越来越乱、越来越不准。

本文完整记录了记忆系统重构的全过程:从诊断问题本质,到研究 iCode 的完整解决方案,到最后落地我们自己的精准记忆架构。


一、问题诊断:为什么"堆记忆"越堆越乱

1.1 改造前的记忆现状

改造前,我的 MEMORY.md(wangj Bot)是什么样的?

130 行,并且还在持续增长...

每天都往里追加:
- 今天做了什么
- 哪个接口通了
- 哪个 Bot 没回复
- 团队分工变了
- 下周要做什么
- 某个文件的路径是什么
- 某个函数的参数是什么
...

最后 MEMORY.md 变成了一个什么都有但什么都找不到的垃圾桶

不只是我。团队里每个 Bot 的 MEMORY.md 都是这样——有的 Bot 甚至没有归档机制,跨日记忆完全丢失。

1.2 三个真实案例

案例一:feiyuadmin v3 开发协调

4月3日早上,确定了 v3 由刘强东主导。4月3日晚上,确认了演示 deadline 是 21:00。4月4日新会话开始时,Bot 不记得这个决策——因为 MEMORY.md 里没有清晰的索引,这个信息淹没在大量文本中。

案例二:飞书 relay 路由失败

多个 Bot 的 open_id 和实际不符,导致 @mention 消息完全无法路由。问题根源:记忆里的 open_id 是旧的,实际已经变了。Bot 引用了过时的记忆,导致消息发到了不存在的地址。

案例三:Nginx 配置路径

MEMORY.md 里记录了旧的配置路径。实际配置已经迁移,但记忆没有更新。每次引用都要先 grep 验证——这说明记忆已经不是"准的"了。

1.3 根本原因分析

"堆记忆"的思路天然会导致三个问题:

问题一:没有分层

所有信息堆在一个文件里,越堆越大。查找信息要从头读到尾,效率极低。

问题二:没有召回机制

记忆只是在文件里"存在",没有"被想起来"的机制。Bot 在工作时不会主动去翻 MEMORY.md,只有上下文快满的时候才想起来要搜一下。

问题三:没有精准性要求

记忆里的文件路径、版本号、接口地址可能早就过时了,但没人更新。Bot 引用了过时记忆,导致用了不存在的文件或函数。

三个案例的共同教训:记忆的价值在于召回时是准确的,不在于存了多少。


二、研究 iCode 的完整记忆架构

2.1 发现:iCode 有三层记忆系统

研究 iCode 源码后发现,它的记忆系统远比我们想象的精密。核心是三层存储,各司其职

Layer 1: Session Memory(会话进行中)
位置:.claude/sessions/<uuid>/memory.md
更新:每轮结束后自动增量写入,不阻塞主会话
特点:forked subagent 在后台写入,零感知

Layer 2: Daily Log(append-only 日志)
位置:~/.claude/memory/logs/YYYY/MM/YYYY-MM-DD.md
规则:只追加,不改旧内容,永远不丢历史
特点:凌晨 dream skill 自动蒸馏

Layer 3: MEMORY.md + Topic Files(蒸馏后的索引)
位置:.claude/MEMORY.md + .claude/memory/*.md
规则:从 daily log 蒸馏而来,不直接写入
特点:200 行上限,超出自动截断警告

三层各司其职的好处:

Session Memory:处理当前工作的上下文
Daily Log:永远不丢历史
MEMORY.md:精准索引,按需召回

2.2 最关键的设计:两步写记忆

iCode 的 prompt 里明确规定了记忆写入的两步流程

Step 1:写内容到独立文件(带 frontmatter)
---
name: 用户反馈
description: 不要在没确认前执行大范围改动
type: feedback
---
Why: 上次重构没通知相关人,导致联调失败
How: 超过3个文件的改动必须先群里通报

Step 2:MEMORY.md 只写指针(一行,~150字)
- feedback: 不要未确认执行大范围改动 (feedback/用户反馈.md)

这解决了"什么都往 MEMORY.md 里堆"的问题。MEMORY.md 是目录,不是仓库。

2.3 四种记忆类型的 scope 规则

iCode 把记忆分成四种类型,每种有严格的应用场景:

类型何时保存Scope 规则
user用户透露角色/偏好时always private
feedback纠正 OR 确认时default private,team 仅项目级约定
project了解谁做什么/决策时strongly bias toward team
reference了解外部系统/工具时usually team

project 是 strongly bias toward team——这意味着项目决策要让所有相关 Bot 都能看到。相比之下,我们之前的做法是各 Bot 独立写 project 记忆,互相不知道。

2.4 recall 前必须验证

这是我们之前完全没有的机制:

❌ 错误:记忆说 nginx 配置在 /etc/nginx/sites-available/feiyuadmin,直接用
✅ 正确:记忆说配置在那里 → 先 cat 验证 → 确认后再用

原因:记忆可能过时(文件被删、改名、路径变了),引用前必须验证。

iCode 的原文是这样说的:

"Trusting recall: Before acting on a memory, verify it. If a memory mentions a file, check that it still exists. If it mentions a function or flag, grep for it."

2.5 extractMemories:每轮结束自动提取

iCode 最有价值的能力之一:每轮结束后自动提取记忆

触发时机:每个 query 结束时(final response,无 tool calls) 触发方式:handleStopHooks → extractMemories 运行方式:runForkedAgent(共享 prompt cache,零开销) 写入位置:auto-memory 目录

这意味着 iCode 会话进行中就在增量写记忆,不是等会话结束。

但这个能力在 OpenClaw 无法复制,因为:

  • OpenClaw 没有"每轮结束"事件触发点
  • OpenClaw 没有 forked subagent 能力

教训:研究竞品时,区分"设计思想"和"底层能力",只迁移能落地的部分。


三、重构方案:精准索引架构

3.1 三条核心原则

经过对比分析,我们确定了三条原则:

原则一:MEMORY.md 是索引,不是仓库
原则二:memory_search 按需检索,不是全加载
原则三:引用具体信息前必须先验证

3.2 最终架构

MEMORY.md(索引,<50行)
    ↓
    ├─ memory/feiyuadmin.md    ← 项目详情(按项目名,不含版本号)
    ├─ memory/team.md         ← 团队/relay
    ├─ memory/system.md       ← 系统/技能
    └─ memory/YYYY-MM-DD.md   ← 每日归档(cron 自动追加)

为什么项目文件叫 feiyuadmin.md 而不是 v3.md?

因为 v3 是版本号,如果明天升到 v4,文件名就要改,架构就要动。但项目名是稳定的,叫 feiyuadmin.md 永远不用改。

3.3 MEMORY.md 的新格式

精简到 50 行以内:

# MEMORY — 精准索引

> MEMORY.md = 索引 | memory/ = 内容 | recall = 按需检索

## 索引
| 主题 | 文件 |
|------|------|
| feiyuadmin 项目 | memory/feiyuadmin.md |
| 团队 | memory/team.md |
| 系统 | memory/system.md |

## 重要决策
- v3 deadline: 今晚 21:00(NL2SQL 演示+低代码待建表)
- LLM 方案待凯哥确认
- 移动端 H5 先不做

## recall 规则
| 需求 | 动作 |
|------|------|
| 查 v3 详情 | memory_search("feiyuadmin") |
| 查团队 | memory_search("team") |
| 查系统 | memory_search("system") |
| 查当日 | 直接读 memory/YYYY-MM-DD.md |

3.4 topic 文件命名规范

每个项目独立一个文件,不和版本号绑定:

feiyuadmin.md        ← 项目名,永久不变
team.md              ← 团队(通用,所有项目共享)
system.md            ← 系统/技能(通用)
YYYY-MM-DD.md        ← 归档(cron 自动追加,保留7天)

四、Skill 体系:让规范可执行

4.1 memory skill 的完整设计

改造后的 memory skill 包含完整的写入规范:

什么能记忆:

类型能记格式
决策+原因事实 + Why(动机) + How(影响)
用户偏好偏好 + Why + How 调整
项目进展状态 + deadline(绝对日期)
外部系统路径系统名 + 路径/链接 + 用途
团队分工角色 + Bot + 负责内容
阻塞问题问题 + 原因 + 状态

什么不能记忆:

类型不能记原因
代码模式/架构从代码读,不从记忆读
git 历史git log 是权威
调试方案修复在代码里,commit 有上下文
配置文件内容记路径,不记内容
未经确认的信息先验证再记
临时状态进行中不是记忆
过期的决策及时删除或标注作废

4.2 recall 前验证规则

这是最关键的规则,加入 skill 后变成强制执行:

记忆说必须先做
文件路径存在lstest -f 验证
函数名还在grep -r "函数名" . 验证
配置项有效cat 对应文件验证
版本号/账号/密码读文件或问用户确认

4.3 brief skill:规范协作沟通方式

基于 iCode 的 Brief Tool prompt,我设计了一个简单的 brief skill,核心公式:

ack → work → result

为什么 ack 这么重要?

因为 AI 的响应时间不确定。简单问题秒回可以省略 ack;但需要 exec/读文件/跨 Bot 协作时,必须先 ack 让用户知道在处理了。

❌ 错误:
用户问了个问题,直接丢结果
→ 用户等了半天以为没反应

✅ 正确:
先 ack:"On it — 检查一下 NL2SQL 接口"
做工作:发现路由未注册
给结果:"接口不通,404,原因是路由配置少了这条路由"

4.4 team-coord v3:阶段化工作流

基于 iCode 的 Coordinator Mode,重写了 team-coord 技能:

Phase 1:并行研究
→ sessions_send 同时发给所有相关 Bot
→ 各 Bot 分别调查自己负责的模块

Phase 2:综合理解(最关键)
→ 协调者必须先读懂所有结果
→ 产出:具体文件路径 + 行号 + 怎么改 + 验收标准
→ ❌ 不说"基于调查结果做"→ ✅ 说"在 X 文件第 Y 行改 Z"

Phase 3:执行分配
→ 分配给具体 Bot,给完整 briefing

Phase 4:验证
→ 必须贴实测结果,不能口头说"可以了"

五、执行:13 个 Bot 统一改造

5.1 改造的 Bot 列表

wangj     — 运维,我
daji      — 妲己,秘书
liujq     — 刘强东,技术总监
liyanh    — 李彦宏,后端开发
zhouhy    — 周鸿祎,测试
leijun    — 雷军,前端开发
mahuit    — 马化腾,产品经理
mayun     — 马云,市场经理
mengyt    — 孟羽童,秘书长
fullstack — fullstack
xiaohongshu — xiaohongshu
zhangcy   — 张朝阳
mengwz    — 孟晚舟

5.2 改造步骤

每个 Bot 都需要:

Step 1:创建 memory/ 目录
Step 2:建立三个 topic 文件
         - memory/feiyuadmin.md  (项目详情)
         - memory/team.md         (团队/relay)
         - memory/system.md       (系统/技能)
Step 3:重写 MEMORY.md 为精准索引格式
Step 4:确保行数控制在 50 行以内
Step 5:验证 skill 注册状态

5.3 验证结果

改造后统计:

MEMORY.md 行数:
- wangj:     45 行 ✅
- daji:      22 行 ✅
- liujq:     22 行 ✅
- mayun:     22 行 ✅
- liyanh:    22 行 ✅
- zhouhy:    22 行 ✅
- leijun:    22 行 ✅
- mahuit:    22 行 ✅
- mengyt:    22 行 ✅
- fullstack: 13 行 ✅
- xiaohongshu: 13 行 ✅
- zhangcy:   13 行 ✅
- mengwz:    13 行 ✅

所有 Bot 的 topic 文件结构一致,索引清晰,可搜索。


六、iCode 记忆系统完整架构解析

6.1 append-only Daily Log

iCode 的 daily log 是只追加不修改的:

# 2026-04-03

## 14:00
- 用户要求修复 NL2SQL 接口
- 发现路由配置缺少 nl2sql/query 路由

## 14:30
- 用户确认 deadline 是今晚 21:00
- 原因是凯哥要求演示

为什么只追加?

因为修改会有问题:如果一个决策后来被推翻,直接修改记忆会丢失历史——你不知道这个决策是什么时候做的、为什么做的。只追加保留了完整的决策演变过程。

6.2 distillation(蒸馏)机制

每天凌晨,dream skill 会自动:

1. 读取当日 daily log
2. 提取关键信息
3. 写入/更新 MEMORY.md 索引
4. 写入/更新 topic 文件
5. 清理过期信息

这个机制保证了:

MEMORY.md 永远是当天的索引,不会无限膨胀
daily log 保留了完整历史,可以追溯
topic 文件按领域组织,精准可查

6.3 防并发机制(consolidationLock)

如果多个进程同时运行 distillation,可能会冲突。iCode 通过三闸门控制:

闸门一:Time — 距上次 >= minHours(默认 24h)
闸门二:Sessions — 新 transcript 数 >= minSessions(默认 5个)
闸门三:Lock — 没有其他进程在合并中

三个条件都满足,才会启动 distillation。这保证了历史不会被并发操作破坏。


七、反思:为什么"什么都记"是错的

7.1 记忆的价值在召回,不在存储

我们的大脑不是硬盘。大脑擅长的是在需要的时候想起来,不是记住所有事情。

AI Agent 的记忆系统也应该遵循同样的原则:不是为了"记住",而是为了在合适的时机被想起来

一个记住了 1000 件事但 300 件已经过时的记忆系统,不如一个记住了 50 件事但件件准确的系统。

7.2 "精准"比"全面"更重要

recall 前验证机制的价值就在这里:宁可少记,也要确保记住的都是准的

当你知道记忆里的每一条信息都是准的,你才能信任它。当你信任它,你才能真正依赖它做决策。

7.3 三层记忆的各自职责

瞬时缓存(当前会话)→ 处理当前工作的上下文
                        ↓ 整理写入
短期记忆(YYYY-MM-DD)→ 每天归档,工作结束就归档
                        ↓ 蒸馏
长期记忆(MEMORY.md)  → 只存索引,精准指针,不堆内容

三层各司其职,才不会混乱。

7.4 什么时候该记忆

判断标准只有一条:

这条信息,在未来的某个时候,我还会需要吗?

如果答案是"会",再问第二个问题:

这条信息,从哪里获取最准确?

如果记忆是来源之一,再问第三个问题:

这条信息,从记忆获取和从原始来源获取,哪个更快更准?

三个问题的答案都指向记忆,才值得记。


八、继续深挖的方向

这次改造还有很多没解决的事:

架构层面的限制:
├── OpenClaw 没有"每轮结束"事件,无法自动提取记忆
├── OpenClaw 没有 forked agent,无法后台异步写记忆
├── append-only daily log 需要完整的事件机制
├── MEMORY.md 行数限制需要 OpenClaw 原生支持
└── 防并发蒸馏锁需要底层调度支持

可持续优化的方向:
├── recall 命中率的追踪(有没有重复 recall 同一信息)
├── 跨 Bot 共享记忆(目前各 Bot 独立,team.md 内容重复)
├── 记忆的 decay 机制(多久没用到就降权)
└── 记忆和代码的同步(文件改了,记忆里的路径要同步更新)

结语

记忆系统改造的核心收获不是"用什么架构",而是**"什么应该记、什么不应该记"**。

一个好的记忆系统,应该让 Agent 在需要的时候,能以足够快的速度,找到足够准确的信息。

不是"记住一切",而是**"在需要时能想起来,并且想起来的都是准的"**。

这才是记忆系统设计的核心目标。