工具 · · 12 min

AI 的记忆按项目锁着,我想要一把总钥匙
AI memories are locked per project — I wanted a master key

那是一个很普通的周二下午。

我在一个新开的项目里跟 Claude 讨论怎么升版本号——pyproject.toml 里改、__init__.py 里改、CHANGELOG 里加一行、tag 怎么打。我突然有种很强烈的既视感。

这个 checklist 我写过

不是写过类似的,是字对字写过。我记得我连”如果 hooks 失败要重 stage 后再开新 commit,绝不 amend”这句话都原模原样存进了某个项目的 memory。

但那是哪个项目?

我打开 ~/.claude/projects/,一长串编码后的文件夹整整齐齐躺在那里:D--dev-code-AI-related-claude-repathD--dev-code-AI-related-anthropic-watchD--dev-code-quantitative-trading……

七十多个项目。

我开始一个个点进去翻 memory/ 子目录。十分钟后,我找到了——它在 D--dev-code-AI-related-claude-repath/memory/feedback_version_bump_checklist.md 里。

但找到的那一刻我没有高兴。

我盯着那一串文件夹想:为什么我每次想用之前写的笔记,都要靠肉眼翻七十个目录?

Claude Code 在 v2.1.59 之后开了一个新东西,叫 auto memory。

它的设计意图很合理:每个项目有自己的上下文、自己的规矩、自己的踩坑笔记——所以记忆按项目隔离,存在 ~/.claude/projects/<encoded>/memory/*.md 下面,互不串。

每个项目有一个 MEMORY.md 作目录索引,下面是若干 feedback_*.md / user_*.md / project_*.md / reference_*.md 文件。前两种是行为修正和用户画像,后两种是项目背景和外部指针。

我用了大半年,越积越多。开始的时候每个项目一两条,现在某些活跃项目有三四十条 feedback。

问题是这样的——

每个项目里的 memory,只对那个项目里的 Claude 可见

当我在 quant-trading 项目里 Claude 不知道我在 claude-repath 项目里教它的事。我在 anthropic-watch 项目里反复跟 Claude 强调过的”破坏性命令必须 dry-run”,在我刚开的新项目里它一无所知。

每个 Claude 都是一个新的实习生。

这事乍听起来是 Claude Code 的问题——为什么不全局共享?

但你想下去,会发现按项目隔离这个设计其实是对的。

我在 A 股量化项目里给 Claude 的规矩,跟我在博客项目里的规矩,几乎没有重合。A 股项目里”涨跌停板必须按 ±10% 算”这种规则,扔到博客项目里完全是噪声——它只会污染上下文、稀释真正相关的指令。

让一个新项目的 Claude 不带任何旧规矩进来,反倒是好的——它干净,它只学这个项目的事。

所以 Anthropic 的设计没错。

错的是工具链——我作为人,需要的视角和 Claude 不一样。

Claude 在干活时只该看到本项目的 memory,没问题。但我作为那些 memory 的作者,我需要的是另一个视角:

这三个问题,Claude 不需要问——它干一个项目时眼里只有那个项目。

但我经常问——我是那个穿越七十个项目的人。

我想了想这件事的本质。

Claude 是一个个独立的工人,每个工人只管自己的工地。这没问题。

我是那个雇主

雇主需要纵向看每一个工地,也需要横向比所有工地。雇主需要知道:我让小王在工地 A 学过的教训,是不是该传给在工地 B 的小李?我半年下来记下了哪些规矩,有没有冗余、有没有矛盾、有没有可以提炼成通用规则的?

工人不需要”全局视角”——给他全局视角反倒会分心。

但雇主离不开全局视角。

Anthropic 给了工人侧的工具(项目级 memory),但雇主侧的工具是缺失的

所以我自己写了一个。它叫 mmcc——memoryManager for ClaudeCode。

写之前我先列了一遍它该是什么样。

第一原则:它不能比 memory 本身还重。

memory 这种东西本身极轻——几十个文件夹,几百个 markdown 文件,加起来几 MB。如果我做一个”管 memory 的工具”,结果它装起来要 200MB、要起一个 daemon、要配一个数据库——那这工具就完全错了。

工具不能比要管的东西还沉。

所以我定了几条硬规矩:

后来证明这几条是对的。pipx install --editable . 一行装上,跑 mmcc tree,七十个项目的 memory 树形结构 80 毫秒打出来:

PROJECT                           COUNT  INDEX?  MTIME
quantitative-trading                 37     yes  2026-04-28 09:17
Life-time-blocks                      8     yes  2026-04-24 12:22
AI-related-ai-gateways                2     yes  2026-04-28 22:43
AI-related-claude-repath              2     yes  2026-04-24 16:53
...

mmcc search "version" 一搜,跨七十个项目全文搜索,所有命中行(含项目名 + 文件名 + 前几行 body)瞬间列出来。

那个我花了十分钟翻文件夹才找到的 checklist,现在零点几秒。

但只有 CLI 还不够。

CLI 适合”我知道我要什么”——我已经想好了搜什么关键词。但 memory 这东西很多时候是反着用的——我想浏览,想看看半年下来都积累了什么,想散漫地翻一翻。

CLI 不适合浏览。

所以我加了第二个东西:mmcc notepad——在本地起一个浏览器视图。

这件事我本来是抵触的。一个 Python CLI 工具突然要嵌一个 web UI,第一反应是请 React 进来、请 Vite 进来、请 tailwind 进来——立刻就违反第一原则了。

所以我又加了一条硬规矩:stdlib 的 HTTP server,单文件 SPA,CDN 拉 marked.js 渲染 markdown。 全部塞在一个 notepad.py 里。

$ mmcc notepad
listening on http://localhost:8765

浏览器自动打开。左边树状结构列七十个项目,每个项目下面是 memory 条目;右边是预览面板,markdown 渲染好的 frontmatter + body。

后来我又给它加了 inline 编辑——在 notepad 里直接改 memory(Ctrl+S 保存),加了删除(带二次确认),加了 MEMORY.md 索引面板(每个项目顶上有一行 📋 索引),加了内链跳转(点 MEMORY.md 里的 [标题](filename.md) 直接跳到对应 memory)。

到这一步它实际上变成了一个跨项目的 Notion——但是是纯文件的,没有云、没有账号、没有后端,所有数据就是你硬盘上那七十个目录。

这几条硬规矩,看起来很硬。但它们在做一件具体的事:保护一个原则。

那个原则是:MemoryStore 必须是纯库,CLI / 网页 / 未来的 MCP server / 未来的 TUI,全都是它的薄包装。

我后来加了 V2.1 的 web 视图、V2.2 的 CRUD、V2.3 的索引面板——每一次扩张都没动那个核心 store.py。它现在还是几百行的纯 stdlib,每一个新功能只是又写一个薄包装,调用同一个 store。

V3 还计划做 MCP server——但那也只是再加一个薄包装。

我后来意识到,这就是 Anthropic 在 claude-repath 那次让我学会的同一件事:底层抽象做对了,上层任何花活都不破坏它。

把核心数据访问做成纯库(无 IO、无 UI、无外部依赖),后面想加任何接口形态都不动核心。

中间踩了几个有意思的坑,顺便记一下。

坑一:Windows 上端口检测不能信 bind

V2.1 起 HTTP server 时我顺手写了个端口冲突处理——bind(8765) 失败就试下一个。

在 Linux 测得好好的。Windows 上出诡异现象:8765 明明被另一个进程占着了,我这边 bind 居然成功了。然后请求来了——一半路由到旧进程,一半路由到新进程。

后来才知道,Windows 的 socket 默认行为有个叫 SO_REUSEADDR 的逻辑差异——它允许多个 socket bind 同一个 LISTEN 端口,不报错。

修法是改用 connect_ex 主动握手探测:

sock.connect_ex(('127.0.0.1', port))  # 0 = 有进程在听 = 端口被占

bind 不能信,但 connect 不会骗你。如果连得上,就是有进程在那。

坑二:用户在 memory 里讨论 XSS。

我用 marked.js 渲染 markdown。markdown 本身允许嵌 HTML——这是合法语法。

然后我意识到一件可怕的事:用户的 feedback memory 里很可能就在讨论 XSS 漏洞——里头会贴 <script> 例子作为”我之前踩过的坑”。

如果 marked 把那个 <script> 当 HTML 渲染——

marked.use({ renderer: { html: () => '' } });

直接在 marked v12 渲染器层把 html token 中和掉。memory 里的 <script> 标签会显示为纯文本,永远不会被浏览器执行。

这是一个自指的坑——用户写”如何防 XSS”的笔记,被显示笔记的工具自己 XSS 了。

坑三:编辑模式空白。

PR #7 修的一个 bug。点 [edit] 进入编辑模式,本来应该看到带预填值的输入框——结果输入框是空的。

DOM 还没挂载完,我 document.getElementById('name') 就去问那个还没出生的 <input> 的 value——拿到 null。然后我把 null 当成”用户没输入”,把字段清空了。

修法是在创建 element 时就用闭包捕获引用,不靠 getElementById 二次查询。

这件事教我一个原则:JavaScript 里”按 ID 找”是一种迟绑定——它假设你查的时候那个 ID 已经在 DOM 里。但 React 之外的 detached element 树里,这个假设并不总是成立。

写到这里,整个 mmcc 实际上有四个层:

  1. MemoryStore:纯 stdlib 库,读文件、解 frontmatter、原子写、guard 边界。CLI / web / 未来 MCP 都调它。
  2. mmcc CLI:6 个子命令(tree / list / search / cat / add / edit),覆盖”我知道我要什么”的场景。
  3. mmcc notepad:本地 web SPA,覆盖”我想浏览/想发散看”的场景。
  4. memory-search Skill:Claude Code 的 Skill,让 Claude 自己在需要时调 mmcc search——“我之前在哪写过 X” 这类问题被自动触发。

第四个最有意思——我做了一个工具,让 AI 自己来找它之前在别的项目里写过的 memory

它不是替代项目隔离设计,是补完它——Claude 干活时还是只看本项目,但它可以主动去查其他项目里有没有写过相关的事。

雇主视角变成了可以被工人按需借用的能力

回看这个工具,我有点想笑。

memory 这件事本身极轻——一些文本文件,按项目放在文件夹里,加起来几 MB。它的”管理”按理说应该比它本身还轻。

但市面上的同类工具——claude-mem 自己写一份 memory 不读 Claude 的、memsearch 要 Milvus + 嵌入、cccmemory 读 session JSONL 不读 memory——每一个都做得比要管的东西重十倍。

我做 mmcc 的时候定了个标准:装上、不感知、关了就消失。

pipx install 一行装上,pipx uninstall 一行卸掉,硬盘上不留任何东西——它只读你已经有的文件。没有自己的 database、没有自己的索引、没有自己的 daemon。

它不该有它自己的”存在感”。

它只是给那些已经存在的文件,开一个穿越项目的视角

也想聊聊——

你最近半年跟 AI 协作积累的那些 memory:行为修正、用户画像、项目背景、外部指针……

你还记得它们都在哪吗?

你有过那种”我之前写过类似的,但忘了在哪”的瞬间吗?

如果你也每天在七十个项目之间穿梭,欢迎试试 mmcc——它在 GitHub 上(xPeiPeix/memoryManagerCC),MIT,pipx install --editable . 一行装上。我希望它能帮你找回那些你以为自己忘了、但其实早就写过的东西。

memory 不该困在项目隔间里。

雇主,应该有钥匙。