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-repath、D--dev-code-AI-related-anthropic-watch、D--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 的作者,我需要的是另一个视角:
- “我在哪个项目里写过版本号升级的 checklist?“(我知道写过,忘了在哪)
- “搜所有 memory 里提到
subprocess.run的”(横向看我在不同项目里怎么处理同一个东西) - “列出我所有 feedback 类型的笔记”(看看半年下来积累了什么模式)
这三个问题,Claude 不需要问——它干一个项目时眼里只有那个项目。
但我经常问——我是那个穿越七十个项目的人。
✵
我想了想这件事的本质。
Claude 是一个个独立的工人,每个工人只管自己的工地。这没问题。
但我是那个雇主。
雇主需要纵向看每一个工地,也需要横向比所有工地。雇主需要知道:我让小王在工地 A 学过的教训,是不是该传给在工地 B 的小李?我半年下来记下了哪些规矩,有没有冗余、有没有矛盾、有没有可以提炼成通用规则的?
工人不需要”全局视角”——给他全局视角反倒会分心。
但雇主离不开全局视角。
Anthropic 给了工人侧的工具(项目级 memory),但雇主侧的工具是缺失的。
所以我自己写了一个。它叫 mmcc——memoryManager for ClaudeCode。
✵
写之前我先列了一遍它该是什么样。
第一原则:它不能比 memory 本身还重。
memory 这种东西本身极轻——几十个文件夹,几百个 markdown 文件,加起来几 MB。如果我做一个”管 memory 的工具”,结果它装起来要 200MB、要起一个 daemon、要配一个数据库——那这工具就完全错了。
工具不能比要管的东西还沉。
所以我定了几条硬规矩:
- 纯 Python stdlib,零运行时依赖。PyYAML、click、rich、pydantic——一个不准引。frontmatter 解析直接拿
re写。 - 不建索引。直接读那七十个
.md文件。我测过,80+ 个文件全读一遍 80 毫秒。我没必要为节省 80ms 引入一个 SQLite + 一套同步逻辑 + 一套失效逻辑。 - 不起后台进程。每次调用就是一次 CLI 进程,跑完就退。
后来证明这几条是对的。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 实际上有四个层:
MemoryStore:纯 stdlib 库,读文件、解 frontmatter、原子写、guard 边界。CLI / web / 未来 MCP 都调它。mmccCLI:6 个子命令(tree/list/search/cat/add/edit),覆盖”我知道我要什么”的场景。mmcc notepad:本地 web SPA,覆盖”我想浏览/想发散看”的场景。memory-searchSkill: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 不该困在项目隔间里。
雇主,应该有钥匙。