Claude Code 源码泄露的事,大家应该都知道了。npm 包里多带了一个 .map 文件,512,000 行 TypeScript 就这么公开了。
研究完它的源码后,我们的结论是:Claude Code 的记忆系统,比想象中初级。
它确实做了不少工作,分了好几层,甚至搞了一个让 agent 做梦的机制来整理记忆。听起来很高级。但扒开来看,本质上还是在一个 agent 的沙盒里打转,记忆出不去,也活不长。
下面一层一层拆开来说。
01 Claude Code 记忆拆解
第一层:CLAUDE.md,用户写给 agent 的规矩
CLAUDE.md 是一个 Markdown 文件,用户自己写,放在项目根目录下。内容可以是任何你想让 Claude 记住的规矩:代码风格约定、项目架构说明、测试命令、部署流程。这个大家用过的应该都比较熟悉了。
每次会话开始时,Claude Code 会把这个文件完整加载进 context。文件越短,Claude 遵守得越好。
它支持三个作用域:项目级的 CLAUDE.md 放在项目根目录,个人级的放在 ~/.claude/CLAUDE.md,组织级的放在企业配置里。
第二层:Auto Memory,agent 自己记笔记
CLAUDE.md 解决了用户主动告诉 agent 的问题,但很多有价值的信息是在对话过程中冒出来的,用户不会专门去写。
Auto Memory 就是干这个的:Claude 在工作中自己判断哪些信息值得记下来,写进一个独立的记忆目录里。
Claude 会把值得记住的东西分成四类:user 类型记录用户的角色和偏好,feedback 类型记录用户的纠正和确认,project 类型记录项目相关的决策和背景,reference 类型记录外部资源的位置。
这些记忆存在 ~/.claude/projects/<项目路径>/memory/ 目录下。每条记忆是一个独立的 Markdown 文件,带 frontmatter 标注类型和描述。入口文件是 MEMORY.md,起索引作用,每行不超过 150 个字符,只存指针,不存内容。
每次会话开始时,MEMORY.md 的前 200 行会被注入 context。真正的知识分散在各个话题文件里,按需加载。
大概长这样:
~/.claude/projects/-Users-me-myproject/memory/
├── MEMORY.md ← 索引文件,每行一个指针
├── user_role.md ← “后端工程师,熟悉 Go,React 新手”
├── feedback_testing.md ← “集成测试必须跑真实数据库,不要 mock”
├── project_auth_rewrite.md ← “auth 重构是因为合规要求,不是技术债”
└── reference_linear.md ← “pipeline bug 在 Linear INGEST 项目里追踪”
MEMORY.md 内容示例(每行 ≤150 字符):
- [User role](user_role.md) — 后端工程师,Go 熟练,React 新手
- [Testing rule](feedback_testing.md) — 集成测试不要 mock 数据库
- [Auth rewrite](project_auth_rewrite.md) — 合规驱动,非技术债
- [Bug tracker](reference_linear.md) — pipeline bug 看 Linear INGEST
这里有一个设计细节值得注意:Claude 被明确要求不要信任自己的记忆。泄露的系统提示词里写得很直白,大意是「记忆只是提示,行动前先去读真实文件做核实」。在模型幻觉率还在两位数的阶段,这种怀疑自己的策略反而很实用。
第三层:Auto Dream,让 agent 做梦整理记忆
记忆写入的问题解决了,但跑了几十次会话之后,MEMORY.md 越来越乱。
矛盾的条目堆积起来就会出现:“昨天的 bug”类似这样的表述写进记忆后,过一周就没人知道是哪天了。于是,已经被重构掉的函数还留在记忆里,文件越来越长,越来越脏。
Auto Dream 模拟的是人脑在睡眠中做的事:整理和巩固记忆。
它扫描现有记忆文件,翻阅历史会话记录,找到用户的反馈和反复提到的话题,然后做整合:把相对时间换成绝对日期,合并矛盾条目,剔除过时内容,最终把 MEMORY.md 控制在 200 行以内。
触发条件两个同时满足:距离上次整合超过 24 小时,且积累了 5 次以上新会话。也可以手动输入“dream”触发。整个过程跑在后台的子 agent 里,不阻塞主会话。

第四层:KAIROS,泄露代码里还没上线的野心
前面三个都是已经上线或正在灰度的功能。泄露的代码里还藏着一个更大的东西。
KAIROS 在源码中出现了超过 150 次。这是一个后台守护进程模式,想把 Claude Code 从被动响应的工具变成主动观察的自治体。
KAIROS 维护 append-only 的日志文件,持续记录观察、决策和行动。它在固定间隔收到 <tick> 信号,自行决定是该主动做点什么,还是保持安静。设计里有一个 15 秒的阻塞预算,任何会打断用户工作超过 15 秒的操作都会被推迟。
KAIROS 内部整合了 autoDream,但走得更远。它不只在会话间隙做记忆整理,而是把整个观察-思考-行动的循环搬到了后台。代码里可以看到它的系统提示词开头:“# Dream: Memory Consolidation — You are performing a dream, a reflective pass over your memory files.”
不过 KAIROS 目前被一个编译时 feature flag 控制,没有出现在任何公开版本里。更像是 Anthropic 对下一步方向的探索。
02 这套体系的天花板在哪?
回头看这四层,用过一段时间就会发现几个绕不过去的问题:
- 200 行硬上限。
MEMORY.md 最多 200 行,25KB。项目跑几个月,记忆就开始互相挤占,老的被新的顶掉。
- 只有 grep 检索。记忆的查找靠 grep 关键词匹配,没有语义理解。你记得讨论过“部署时端口冲突的问题”,但记忆里写的是“修改 docker-compose 的 port mapping”,grep 搜不到。
- 细节容易丢。Auto Memory 记的是 agent 自己觉得重要的东西,粒度很粗。具体的代码片段、调试过程、讨论的上下文,大部分会被丢掉。
- 层层修补的复杂度。CLAUDE.md 不够加 Auto Memory,Auto Memory 乱了加 Auto Dream,Dream 不够又搞 KAIROS。每一层都是在补上一层的问题,越叠越复杂,但根上的限制没变。
- 跨工具不通用。记忆锁在 Claude Code 一个工具里。换到 OpenCode、Codex CLI,从零开始。
- 终究是短期记忆。不管做不做梦,本质还是会话粒度的东西。「上周讨论的 Redis 配置最后怎么处理的」「三个月前的 auth 重构到哪一步了」,这类长跨度的回溯基本无望。

这不是 Claude Code 的设计失误。单 agent、会话粒度、本地存储,架构走到这里,天花板就在这。
03 memsearch:记忆应该比任何一个 agent 都活得更长
这是我们做 memsearch 的核心想法。agent 会换,工具会换,但你在项目里积累的知识不应该跟着消失。memsearch 把记忆从 agent 里抽出来,放到一个独立的持久层。agent 来来去去,记忆一直在。

整体架构:
┌─────────────────────────────────────────┐
│ Agent Plugins (用户层) │
│ Claude Code · OpenClaw · OpenCode · Codex│
└──────────────────┬──────────────────────┘
↓
┌──────────────────┴──────────────────────┐
│ memsearch CLI / Python API │
│ (开发者层) │
└──────────────────┬──────────────────────┘
↓
┌──────────────────┴──────────────────────┐
│ Core: Chunker → Embedder → Milvus │
│ (引擎层) │
└──────────────────┬──────────────────────┘
↓
┌──────────────────┴──────────────────────┐
│ Markdown Files (Source of Truth) │
│ (持久存储层) │
└─────────────────────────────────────────┘
最上面是四个主流 agent 平台【Claude Code · OpenClaw · OpenCode · Codex】的插件,负责自动捕获对话。中间是 CLI 和 Python API,给开发者做自定义集成。底下是 Milvus 向量索引加 Markdown 持久存储。
四个平台各自接入,但往下走都是同一套 core。
安装:两条命令搞定
Claude Code 用户在 marketplace 里装就行:
/plugin marketplace add zilliztech/memsearch
/plugin install memsearch
全程不需要配置任何东西。
其他平台也一样简单。OpenClaw 一条命令:openclaw plugins install clawhub:memsearch。Python API 用 uv 或 pip:
uv tool install "memsearch[onnx]"
自动记忆,全量记忆,按需回忆
装好之后,memsearch 通过 shell hook 接入 agent 的生命周期。
自动记忆,全量记忆。 每轮对话结束时,对话被 Haiku 模型自动总结要点,被追加到当天的记忆 Markdown 文件,再异步向量化进索引。后台完成,用户无感。且不会漏任何对话轮次,历史所有操作都有记忆,永不遗忘。
记忆召回通过 skill 机制触发。 memsearch 注册了一个 memory-recall skill,Claude 可以主动调用,也可以在判断用户问题需要历史上下文时被动触发。skill 跑在 fork 出来的子 agent 里(context: fork),不会把搜索工具的定义塞进主会话的 context,零 token 开销。

记忆文件的目录结构长这样:
.memsearch/
└── memory/
├── 2026-03-28.md ← 每天一个文件
├── 2026-03-29.md
├── 2026-03-30.md
└── 2026-04-01.md
一天一个 Markdown 文件。打开就能读,编辑器能直接改。想迁移?复制文件夹。想版本管理?git 跟踪。
向量索引只是缓存层,Markdown 才是 source of truth。 索引丢了随时重建。数据不会锁死在某个工具里。
检索:不只是向量搜索
Claude Code 的记忆检索靠 grep。记忆少的时候够用,多了就不行了。
memsearch 走混合搜索。语义向量抓相近的内容,BM25 抓精确关键词,RRF 做融合排序。
你问「上周那个 Redis 超时怎么解决的」,语义搜索能理解意图。你说「搜一下 handleTimeout」,BM25 精确命中函数名。两条路互补。
recall 被触发后,子 agent 内部会从 L1 往 L3 逐层下探。Claude 在每一层看到的东西大概是这样:
L1:语义搜索
子 agent 先跑 memsearch search,从 Milvus 里召回最相关的结果:
┌─ L1 搜索结果 ─────────────────────────────────┐
│ │
│ #a3f8c1 [score: 0.85] memory/2026-03-28.md │
│ > 部署时 Redis 端口冲突,默认 6379 被占用, │
│ 改用 6380,同时更新了 docker-compose... │
│ │
│ #b7e2d4 [score: 0.72] memory/2026-03-25.md │
│ > auth 模块重构完成,JWT 换成 session token, │
│ 原因是移动端对 token 刷新支持不好... │
│ │
│ #c9f1a6 [score: 0.68] memory/2026-03-20.md │
│ > 数据库索引优化,在 users 表加了复合索引, │
│ 查询时间从 800ms 降到 50ms... │
│ │
└────────────────────────────────────────────────┘
每条结果带 chunk_hash、相关性分数、来源文件,内容截断到 200 字。大多数时候到这一层就够了。
L2:上下文展开
如果 L1 的预览不够用,子 agent 拿 chunk_hash 跑 memsearch expand a3f8c1,拉出完整段落:
┌─ L2 展开结果 ─────────────────────────────────┐
│ │
│ ## 2026-03-28 部署问题排查 │
│ │
│ Redis 端口冲突排查过程: │
│ 1. docker-compose up 后 Redis 容器启动失败 │
│ 2. 宿主机 6379 被另一个 Redis 实例占用 │
│ 3. 改 docker-compose.yml: “6380:6379” │
│ 4. 更新 .env: REDIS_PORT=6380 │
│ 5. config/database.py 连接字符串同步修改 │
│ │
│ 注意:只影响本地开发环境,生产环境不受影响。 │
│ │
│ [source: memory/2026-03-28.md lines: 42-55] │
└────────────────────────────────────────────────┘
不用截断,完整上下文,所有细节都在。
L3:原始对话
极少数情况下需要看当时到底聊了什么。子 agent 跑 memsearch transcript <path> --turn <uuid>,拉出原始对话记录:
┌─ L3 原始对话 ─────────────────────────────────┐
│ │
│ [user] docker-compose up 跑不起来,Redis 报 │
│ 端口冲突,帮我看看怎么回事 │
│ │
│ [agent] 检查了宿主机端口占用情况... │
│ 运行 lsof -i :6379 发现... │
│ 建议修改映射端口为 6380... │
│ (tool_call: Bash “lsof -i :6379”) │
│ (tool_call: Edit “docker-compose.yml”) │
│ │
│ [user] 好了,还有别的地方要改吗? │
│ │
│ [agent] 还需要更新 .env 和 database.py... │
│ │
└────────────────────────────────────────────────┘
用户的提问、agent 的回复、调用了哪些工具,一个不漏。
三层从轻到重,子 agent 自己判断需要下探到哪一层,最后把整理好的结果返回主会话。省 token,不丢信息。
跨 agent 共享:真正的永久记忆
这是 memsearch 和 Claude Code 记忆最本质的区别。
Claude Code 的记忆锁在自己一个工具里。你用 OpenCode、OpenClaw、Codex CLI,得各自从零开始。MEMORY.md 存在本地,绑定单个用户、单个 agent,天然就是一座孤岛。
memsearch 目前支持四个主流 coding agent:Claude Code、OpenClaw、OpenCode、Codex CLI。它们共享同一套 Markdown 记忆格式,和同一个 Milvus collection。
不管从哪个 agent 写入的记忆,其他 agent 都能搜到。

举两个实际场景。
第一个:你在 Claude Code 里花了一下午搞清楚项目的部署流程,踩了几个坑。对话被自动总结、索引。第二天切到 OpenCode 继续开发,问「昨天部署时那个端口冲突怎么解决的」。OpenCode 直接搜到昨天在 Claude Code 里的记忆,给你正确答案。
第二个:团队协作。把 Milvus 后端指向 Zilliz Cloud,多个开发者在不同机器上、用不同 agent,读写同一份项目记忆。新人加入,不用翻几个月的 Slack 和文档,agent 自己就知道项目的历史上下文。
这些事情,Claude Code 的记忆体系做不到。
给开发者的接口
如果你不只是用现成的 agent 插件,而是自己搭 agent 工具,memsearch 提供了 CLI 和 Python API 两种方式。
CLI 可以直接在终端操作记忆:
# 索引 markdown 文件
memsearch index ./memory
# 搜索记忆
memsearch search “Redis 端口冲突”
# 展开某条记忆的完整内容
memsearch expand a3f8c1
# 启动文件监视,自动索引变动
memsearch watch ./memory
# 压缩老记忆
memsearch compact
Python API 几行代码就能接进任何框架:
from memsearch import MemSearch
mem = MemSearch(paths=[“./memory”])
await mem.index() # 索引 markdown
results = await mem.search(“Redis config”) # 混合搜索
await mem.compact() # 压缩老记忆
await mem.watch() # 文件变动自动索引
底层用 Milvus 做向量数据库。本地跑用 Milvus Lite,零配置。云端协作接 Zilliz Cloud,有免费层。自托管 Docker 也行。
嵌入模型默认用 ONNX,CPU 跑,不用 GPU 不用调 API。想换 OpenAI 或 Ollama 随时切。
尾声
Claude Code 的记忆设计有它的价值。KAIROS 和 autoDream 的思路也值得关注。
但单 agent 内部的记忆优化,走到头也就是这样了。记忆应该比任何一个 agent 都活得更长。通过独立的向量存储层和开源统一的记忆协议,或许才是打破智能体“记忆孤岛”的关键一步。
希望这份基于源码的分析,以及对跨平台记忆方案 memsearch 的介绍,能给大家在构建更“持久”的 AI 编程工作流时带来一些启发。关于 AI 编程、向量数据库以及各种工程实践的更多讨论,也欢迎来 云栈社区 交流。
