OpenClaw Agent Team 记忆系统实战:踩了19天坑后的架构复盘
📖 本文首发于微信公众号「Wesley AI 日记」,更多 AI Agent 实战系列请微信搜索关注。
问题起源:为什么 AI Agent 需要记忆?
运营 AI Agent 团队一个月后,遇到了一个让人抓狂的问题:Agent 每次醒来都是"失忆"状态。
具体场景:
- PM Agent 昨天刚规划了本周内容方向,今天接到任务时完全不记得
- 内容编辑 Agent 重复问"我们公众号叫什么名字?"
- CEO Agent 每次开会都要重新介绍团队分工
这就像雇了一群每天失忆的员工——能力强,但记不住任何上下文。
第一版方案:MEMORY.md(失败)
最初的想法很简单:给每个 Agent workspace 放个 MEMORY.md,让它自己记录重要信息。
workspace-pm/
└── MEMORY.md # PM 自己维护的记忆
workspace-editor/
└── MEMORY.md # 编辑自己维护的记忆
失败原因:
- Agent 不会主动写入:除非明确指令"更新记忆",否则它不会主动记录
- 读写冲突:Agent 高频读写同一文件,经常出现"文件正在被修改"错误
- 无结构化:MEMORY.md 变成了流水账,Agent 读取时根本找不到关键信息
踩坑时长:7天
第二版方案:memory/ 目录结构(部分成功)
参考 OpenClaw 官方文档,改用目录化结构:
workspace-ceo/
└── memory/
├── bank/ # 长期记忆库
│ ├── strategy/ # 战略方向
│ ├── sop/ # 标准流程
│ └── reference/ # 参考资料
└── wal/ # Write-Ahead Log(高频写入缓冲区)
└── events/ # 临时事件记录
核心改进:
- 读写分离:高频写入走
wal/,长期存储走bank/ - 结构化分类:不同类型信息放不同目录
- 支持 memory_search:OpenClaw 内置的语义搜索工具
新问题:
- Agent 不知道该写哪里:CEO 把会议记录写进了
strategy/,导致文件混乱 - 读取路径硬编码:每次改目录结构,Agent 就找不到文件了
踩坑时长:5天
第三版方案:WAL Protocol + 定时 Compaction(当前方案)
参考数据库 WAL 机制,设计了这套架构:
核心设计
1. 写入流程(WAL)
graph LR
A[Agent 产生事件] --> B[写入 wal/events/YYYY-MM-DD.md]
B --> C[追加写入,无需读取]
C --> D[当日所有事件累积]
关键点:
- 只追加,不读取:避免读写冲突
- 按日期分文件:
2026-03-23.md只写今天的事件 - 低成本:Agent 执行
echo "事件" >> wal/events/2026-03-23.md即可
2. 读取流程(memory_search)
graph LR
A[Agent 需要查询信息] --> B[调用 memory_search]
B --> C[搜索 bank/ 下所有文件]
C --> D[返回 Top-K 相关片段]
关键点:
- 禁止直接读 wal/:wal 是临时缓冲区,不保证数据结构
- 只搜索 bank/:长期记忆库经过整理,结构稳定
3. 定时整理(Compaction)
# 每日凌晨 2:00 执行(OpenClaw Cron)
ceo-compaction:
schedule: "0 2 * * *"
task: |
1. 读取昨天的 wal/events/YYYY-MM-DD.md
2. 提取关键信息(去重、分类)
3. 写入对应的 bank/ 子目录
4. 归档 WAL 到 wal/archive/
关键点:
- 异步执行:不阻塞 Agent 日常工作
- 人工可介入:Compaction 结果可人工校验调整
- 安全删除:归档后保留原始 WAL 7天,防止误删
实际效果
写入性能:
- 之前:Agent 每次写入耗时 2-5 秒(读取 → 修改 → 写回)
- 现在:Agent 每次写入耗时 < 0.5 秒(纯追加)
读取准确率:
- 之前:Agent 经常读到过时信息或读不到关键信息
- 现在:memory_search 能精准返回相关上下文(语义搜索)
维护成本:
- 之前:每周需手动整理 MEMORY.md,防止膨胀到几万字
- 现在:每天自动 Compaction,bank/ 保持结构清晰
代码实现(关键部分)
Agent 写入示例(CEO Agent)
// CEO 在会议后记录决策
async function recordDecision(decision) {
const today = new Date().toISOString().split('T')[0]; // 2026-03-23
const walPath = `memory/wal/events/${today}.md`;
// 追加写入(不读取文件)
await exec(`echo "\n## ${new Date().toISOString()}\n${decision}" >> ${walPath}`);
}
Compaction 逻辑(每日凌晨执行)
// 伪代码
async function compactWAL() {
const yesterday = getYesterdayDate(); // 2026-03-22
const walFile = `memory/wal/events/${yesterday}.md`;
const events = await readFile(walFile);
// 调用 LLM 提取关键信息
const summary = await llm.summarize(events, {
categories: ['strategy', 'sop', 'reference']
});
// 写入 bank/
await writeFile(`memory/bank/strategy/decisions-${yesterday}.md`, summary.strategy);
await writeFile(`memory/bank/sop/new-sop-${yesterday}.md`, summary.sop);
// 归档原始 WAL
await moveFile(walFile, `memory/wal/archive/${yesterday}.md`);
}
踩坑总结
坑1:让 Agent 自己维护长期记忆
错误做法:让 Agent 每次自己决定"这条信息该写进哪个文件"
正确做法:Agent 只管无脑追加到 WAL,由定时任务统一整理
坑2:高频读写同一文件
错误做法:所有 Agent 共享一个 MEMORY.md,读写冲突频繁
正确做法:写入走 WAL(追加),读取走 bank/(已整理),读写分离
坑3:依赖 Agent 的"主动性"
错误做法:"你记得把重要信息写进 MEMORY.md"
实际情况:Agent 只有在明确指令时才会写入,日常对话中的关键信息会丢失
正确做法:在 Agent 的 System Prompt 中明确:"每次执行完任务后,必须将关键决策追加到 wal/events/今日.md"
坑4:过度依赖文件结构
错误做法:硬编码读取路径 memory/bank/strategy/current.md
问题:一旦改目录结构,Agent 全部找不到文件
正确做法:始终通过 memory_search(query) 语义搜索,Agent 不需要知道具体路径
最终架构图
workspace-ceo/
├── memory/
│ ├── bank/ # 长期记忆库(只读)
│ │ ├── strategy/ # 战略决策
│ │ │ └── 2026-q1.md
│ │ ├── sop/ # 标准流程
│ │ │ ├── daily-review.md
│ │ │ └── content-publish.md
│ │ └── reference/ # 参考资料
│ │ ├── team-roster.md
│ │ └── wechat-articles.md
│ └── wal/ # 写入缓冲区(只写)
│ ├── events/ # 当日事件流
│ │ └── 2026-03-23.md # 今天的所有记录
│ └── archive/ # 已整理归档
│ └── 2026-03-22.md
└── SOUL.md # Agent 人格定义
性能数据对比
| 指标 | MEMORY.md 方案 | WAL Protocol 方案 |
|---|---|---|
| 写入延迟 | 2-5 秒 | < 0.5 秒 |
| 读写冲突率 | 15% | 0% |
| 信息查询准确率 | ~60% | ~90% |
| 维护成本 | 每周人工整理 | 每日自动 Compaction |
| 文件膨胀问题 | 严重(单文件数万字) | 无(结构化分散) |
还在继续优化的方向
- Compaction 智能化:现在是每日全量整理,计划改为增量整理(只处理新增事件)
- 多 Agent 协作记忆:PM 和编辑如何共享"待发布文章列表"
- 记忆优先级:有些信息应该常驻(如团队分工),有些可以淘汰(如临时任务)
可复用的设计原则
如果你也在做 AI Agent 团队,这套架构的核心思想可以直接复用:
- 读写分离:写入走临时缓冲区(WAL),读取走稳定存储(bank)
- 追加优于修改:Agent 只追加日志,避免读-改-写冲突
- 异步整理:不要让 Agent 现场整理数据,交给定时任务
- 语义检索优于路径查找:用
memory_search代替硬编码路径 - 结构化分类:不同类型信息分目录存储
📖 本文首发于微信公众号「Wesley AI 日记」
📚 AI Agent 实战系列(微信搜索「Wesley AI 日记」关注):
👆 微信搜索「Wesley AI 日记」关注,不错过每一篇更新。