很多人用Claude Code都有一个共同体验:这玩意儿单次会话里聪明得吓人,但只要关掉终端、第二天再打开,它就像失忆了一样,把昨天聊定的架构方案、踩过的坑、甚至刚改完的文件结构忘得一干二净。你只能不厌其烦地重新粘贴背景说明,把上下文从零喂回去——项目越大,这种重复劳动就越让人崩溃。
所以我自己折腾了一套叫 planning-with-files 的持久化记忆方案。核心思路非常朴素:与其靠工具内置的会话记忆,不如把关键信息主动落盘成文件,让Claude Code每次开工前先从文件里恢复“记忆”。这套方案不依赖任何第三方服务,纯粹用Markdown文件+CLAUDE.md配置就能跑起来,实测对中大型项目的连续性帮助非常明显。这篇文章我把自己从踩坑到成型的完整设计过程写出来,适合正在用Claude Code做真实项目、且受够了“失忆”问题的开发者。
1. 为什么Claude Code需要持久化记忆
1.1 无状态会话的天然缺陷
Claude Code本质上是一个终端里的AI编程Agent,它的工作方式是“每轮对话独立推理”。官方不是没做上下文管理,但默认机制是把你当前会话里的聊天记录和文件内容临时塞进上下文窗口,一旦会话结束,这些东西就烟消云散。你下次输入 claude 启动新会话,它对你项目唯一的了解就只剩两个来源:一是它现场扫描文件树,二是你在CLAUDE.md里留下的静态说明。
在实际项目里,这个机制会导致一个很典型的问题:你上午让Claude Code重构了一个模块,下午想继续优化它,新会话的Claude Readme虽然知道这个模块的文件路径,但完全不知道你重构的动机、约定的接口规范、以及你否掉了哪些方案。于是它可能会按照自己的理解重新设计一遍,甚至把你上午刚定好的代码风格推翻。这不是Claude变笨了,而是它确实什么也不记得。
1.2 上下文窗口不是记忆,是短期工作台
很多人的直觉是:把上下文窗口调大不就行了?比如Claude Code支持通过参数控制上下文长度,那干脆把历史对话全塞进去。这里有个认知误区——上下文窗口更像是你的桌面,而不是你的书架。桌面堆满了文件确实方便随时取用,但桌面越大,你找到特定文件的时间就越长,注意力被无关信息稀释得越厉害。
实测下来,往Claude Code里塞大量历史对话,最先崩溃的不是质量,而是token消耗和响应延迟。每轮请求都要把所有历史重新计算一遍,账单哗哗涨,响应速度肉眼可见地变慢。更麻烦的是,历史对话里充满了临时性信息(比如中间改错的代码、废弃的调试输出),这些噪声会直接影响Claude的推理质量。
所以正确思路不是想方设法把更多对话塞进上下文,而是把“值得长期记忆的信息”筛选出来,固化到文件里,每次只加载这部分精华。这就是planning-with-files最核心的设计动机。
1.3 file-based记忆方案到底比MCP记忆服务器强在哪
有人可能会问:现在不是有MCP(Model Context Protocol)记忆服务器吗?比如一些社区方案会用一个SQLite或JSON文件存储记忆,Claude Code通过MCP接口读写。我也试过这类方案,但最后放弃了,原因有三点:
第一,MCP记忆服务器普遍依赖外部进程,一旦服务没启动,Claude Code直接报错,整个工作流就断了。文件方案零依赖,任何环境都能跑。
第二,MCP记忆的存储格式通常不透明,你没法在不用Claude Code的时候直接浏览和修改记忆内容。而Markdown文件你可以用任何编辑器打开,人类可读、可审查、可手动修正。
第三,文件方案天然兼容Git。你的记忆变更可以纳入版本管理,哪天改坏了直接回滚。这对于记录技术决策尤其重要——你能看到“为什么当初选了A而不是B”的完整演进过程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 记忆体系整体架构设计
2.1 三层记忆模型:从静态到动态
在设计planning-with-files时,我把Claude Code的记忆划分成三个层次,各层职责不同、更新频率也完全不同。这样既避免把所有信息堆在一个文件里,又保证了不同粒度的记忆各得其所。
第一层是全局记忆,存放在用户主目录下的 ~/.claude/CLAUDE.md。这一层记录的是跨项目的通用偏好和习惯,比如你惯用的代码风格、希望Claude默认遵守的安全规范、常用的命令行工具链等。对任何项目都适用,可以视为Claude的“性格设定”。
第二层是项目静态记忆,存放在项目根目录的 CLAUDE.md。这一层记录的是这个项目的固定背景:技术栈、目录结构、构建命令、测试命令、代码规范、部署方式等。这些信息不怎么变化,属于Claude在项目里工作的“基本面”。我会在CLAUDE.md里大幅引用下游文件来减少冗余。
第三层是项目动态记忆,这就是planning-with-files的核心。它放在 docs/planning/ 目录下,专门记录任务进展、当前状态、技术决策、踩坑记录等会频繁更新的信息。静态记忆负责“知道项目是什么”,动态记忆负责“知道项目现在进行到哪了”。
2.2 目录结构与文件职责划分
我在项目里固定维护这样一个目录结构:
code复制docs/
└── planning/
├── PLANNING.md # 当前任务总览与状态看板
├── DECISIONS.md # 架构决策记录(ADR)
├── PROGRESS.md # 工作日志与进展追踪
├── TASKS.md # 任务拆解与上下文详情
└── REFERENCE.md # 参考文档与资料索引
每个文件的定位必须清晰,否则写作时容易乱。PLANNING.md是入口,类似于项目当前状态的“首页”——包含总体目标、当前进行中的任务列表、每个任务的状态标记(待办/进行中/已完成/阻塞)。DECISIONS.md用来记录重要的技术决策,每条决策包含背景、方案选项、选定方案、理由、日期和关联人。PROGRESS.md是持续追加的工作日志,按日期倒序记录每次会话的做了什么、验证了什么、下一步要做什么。TASKS.md则承载任务级别的细节——每个任务的目标定义、验收标准、相关文件路径、约束条件。
这套结构的关键在于:Claude每次会话开始时只读PLANNING.md一个文件,就能了解全局,然后按需去TASKS.md或者DECISIONS.md里找细节。如果让Claude每次把所有文件全读一遍,token消耗会上去,而且信息噪音也大。
2.3 状态流转机制:从“待办”到“完成”的闭环
光有文件还不够,文件里的信息会因为长期不更新而腐烂。所以我还设计了一个简单但严格的状态流转规则,并且让Claude Code在会话结束前强制写回状态变更。
每个任务的生命周期是:待办(open) → 进行中(in_progress) → 待验证(verify) → 已完成(closed)。如果遇到阻塞,标记为 blocked,并在TASKS.md里说明阻塞原因和需要的帮助。这套机制借鉴了Kanban的最小化模型,但对于AI辅助开发来说,不需要更复杂的流程——因为Claude没有主动推动任务的能力,它只能被任务状态“牵着走”。
状态流转的操作方式是在PLANNING.md里用简单的文本标记,比如:
code复制- [x] 完成用户认证模块重构 | 12月20日 | 关联DECISIONS.md#ADR-003
- [ ] 优化数据库查询性能 | 阻塞中:等待压测数据
为了让Claude遵守这套规则,我在CLAUDE.md里明确写了一条硬性指令:“每次会话结束时,检查docs/planning/下的文件,将所有会话中完成的工作同步更新到对应状态”。实践证明,只要规则写得足够明确,Claude的执行力还是很可靠的。
3. 核心文件模板与实现细节
3.1 PLANNING.md:任务看板与状态总览
PLANNING.md是整个记忆体系的心脏,Claude Code每次开工前读它,就相当于在看一张项目作战地图。我把写作模板固定成四个区块:当前目标、活跃任务、阻塞事项、最近状态。
code复制# 项目规划与状态看板
> 最后更新:2025-01-15
> 更新者:Claude Code (session #42)
## 当前迭代目标
v2.0 数据迁移工具上线,支持断点续传
## 活跃任务
| ID | 任务 | 状态 | 关联文件 | 备注 |
|----|------|------|----------|------|
| T-101 | 实现迁移断点记录 | in_progress | src/migrator/checkpoint.ts | 张工负责接口评审 |
| T-102 | 迁移性能基准测试 | todo | benchmark/migrate.test.ts | 等T-101完成 |
## 阻塞事项
- T-102 阻塞中:等待压测环境配置完成
## 最近状态摘要
- 12/19 完成T-100 断点续传协议设计,详见DECISIONS.md#ADR-004
- 12/18 确认迁移任务的目录遍历策略采用BFS
这里有一个容易被忽视的细节:日期和会话编号一定要写清楚,否则过两周回看时你根本不知道这条记录是哪个会话留下的、什么时候留的。我一开始没写日期,后来查一个决策的来龙去脉时完全对不上时间线,很痛苦。
3.2 DECISIONS.md:让AI和人都记住“为什么”
技术决策记录(ADR)是我认为整套方案最有价值的部分。开发过程中最贵的不是写代码,而是团队对“为什么这么设计”的一致理解。Claude Code经常会在几轮对话后忘记之前的架构权衡,如果没有ADR,它很可能在后续开发中用一套完全不同的思路去写代码——你说不清这种漂移是错,但代码风格和架构一致性肯定会被破坏。
我的DECISIONS.md每个条目格式如下:
code复制## ADR-004:迁移任务采用BFS目录遍历
日期:2025-01-18
状态:已接受
### 背景
数据迁移工具需要扫描大规模文件树,原方案用递归DFS
可能造成调用栈过深,且不支持并行处理。
### 选项
A. 递归DFS:实现简单,但栈溢出风险高
B. 显式栈BFS:支持并行调度,可按层控制资源
C. 外部工具find/ripgrep:不做遍历,自行处理结果
### 决策
选择B,理由是BFS便于按目录层级做并发限制,
并为后续断点续传提供天然的分层检查点。
### 后果
需要多维护一个显式队列结构,但整体控制力更强。
我会告诉Claude Code,当你面临方案选择时,先检查DECISIONS.md里有没有相关决策;如果没有,等决策后必须追加一条新ADR。这一步能让技术债的源头变得可追溯。
3.3 PROGRESS.md:工作日志的价值不止于记录
PROGRESS.md是个容易被人轻视的文件,它的价值在长周期项目里才会完全体现。我维护一个按日期倒序的日志列表,每条记录包含:会话目标、做了什么、验证结果、遗留问题。
code复制## 2025-01-18 (session #42)
会话目标:实现迁移断点记录
完成事项:
- 新增 CheckpointStore 类,支持SQLite持久化断点状态
- 编写迁移中断恢复测试,单测通过
验证结果:断点续传在5万文件测试集上恢复耗时<3s
遗留问题:并发场景下断点写入存在竞态,需加锁
工作日志最大的价值是给Claude Code一个“自我复盘”的窗口。当新会话启动时,如果PLANNING.md里的状态摘要写得不够细,Claude可以快速翻最近的日志来理解项目脉落。我自己也经常靠它来回想起当时的思考轨迹——因为Claude会话结束时生成的总结,往往比我手动写纪要更完整。
3.4 TASKS.md:任务级的详细上下文
TASKS.md用来承载单个任务的详细描述。PLANNING.md里的表格只放摘要,但一个中型任务的背景可能很长,比如涉及多个文件、需要理解既有代码、还要考虑兼容性。这些详细信息放在PLANNING.md里会爆炸,所以我单独抽出TASKS.md。
任务条目模板:
code复制## T-101:实现迁移断点记录
状态:in_progress
优先级:高
创建:2025-01-16
### 目标
迁移工具在中断后可以从上次位置继续,无需从头扫描
### 验收标准
- 5万文件测试集,随机kill进程后恢复,迁移进度不丢失
- 断点存储使用SQLite,数据量不超过1MB
### 约束
- 不引入新的运行时依赖(SQLite已内置)
- 兼容Windows路径分隔符
### 相关文件
- src/migrator/checkpoint.ts
- src/migrator/checkpoint.test.ts
### 上下文备注
- 需要参考DECISIONS.md#ADR-004的BFS遍历设计
Claude Code在接到任务时,会读取TASKS.md中对应条目的完整描述,这样它就不需要你重新复述需求,也不会遗漏验收标准。我见过最可惜的一种情况是,Claude辛苦写完一个模块,结果验收标准里有一条“不引入新依赖”被漏了——它引了个流行npm包,导致项目体积暴增。这些不该靠“运气”来保证,而应该在任务描述里写清楚。
4. 实操:从零搭建可用的记忆工作流
4.1 三步初始化记忆框架
刚开始用这套方案时,最容易犯的错误是想把文件模板设计得尽善尽美、一步到位。我的建议是先跑起来再迭代,三步就能完成初始化。
第一步,创建目录和初始文件。在我的项目根目录执行:
bash复制mkdir -p docs/planning
cd docs/planning
touch PLANNING.md DECISIONS.md PROGRESS.md TASKS.md REFERENCE.md
第二步,往PLANNING.md里写入当前迭代的目标和至少一个正在推进的任务。如果你手上刚好有空档期,也可以直接写“当前无活跃任务”,但建议最好有真实内容,这样Claude第一次读取时就有东西可依据。
第三步,在项目根目录的CLAUDE.md里追加planning相关的加载指令(详见4.2节)。然后启动Claude Code,用一句话测试:“先读一下docs/planning目录下的文件,然后总结当前项目状态。”如果Claude能准确说出当前任务和阻塞事项,说明初始化成功。
4.2 在CLAUDE.md中注册记忆读取规则
光有文件还不行,你得让Claude Code知道“开工前先读这些文件”。这需要在项目的CLAUDE.md里写清楚加载指令。我目前使用的模板大致如下:
markdown复制## Project Context
你是本项目的编程助手。每次开始任务前,你必须先阅读以下文件恢复上下文:
- docs/planning/PLANNING.md(必读)
- docs/planning/TASKS.md(按需,当PLANNING.md中有进行中任务时)
- docs/planning/DECISIONS.md(按需,当需要做技术选择时)
阅读后,先向用户简要汇报当前任务状态,再等待指令。
如果PLANNING.md中没有任何任务信息,则主动询问用户是否希望记录当前工作内容。
## 会话结束要求
每轮会话结束前,必须将本会话的工作成果同步到docs/planning/目录:
- 完成任务则更新PLANNING.md中的状态标记
- 新增重要决策则追加到DECISIONS.md
- 记录工作日志到PROGRESS.md
- 新增/变更任务细节则更新TASKS.md
这里划线部分“必须先阅读”,Claude Code一般会认这条指令。但有个坑:如果你没有给Claude足够明确的任务“触发方式”,它可能只在第一次启动时读一次,后面你在同一会话中直接提问,它就直接进入问题响应模式,不再读文件。所以我习惯在每次开启一轮新工作(比如切换功能模块)时,先说一句“读一下PLANNING.md,然后我们继续”。这个习惯能显著减少上下文漂移。
4.3 与Claude Code Skills机制联动
Claude Code新版本引入了Agent Skills机制,Backlog里也有官方文档,本质上就是给你提供一种把常用操作封装成“技能”的方式。我基于planning-with-files做了一个非常简单的skill,实现了一键加载规划文件并汇报状态。
具体做法是,在项目根目录建一个 .claude/skills/load_planning/SKILL.md 文件,内容大致如下:
markdown复制# Skill: load_planning
一句话描述:加载并汇总 docs/planning/ 下的项目记忆
## 用途
在会话开始或切换任务时,快速恢复项目上下文。
## 执行步骤
1. 读取 docs/planning/PLANNING.md
2. 列出所有状态为 in_progress 或 blocked 的任务
3. 从 TASKS.md 中读取这些任务的详细描述
4. 输出一个简洁的上下文汇总:当前目标、活跃任务、阻塞项、最近进展
有了这个skill,我在终端里只要说“load_planning”,Claude就会自动执行上面几条动作,省去了反复口头叮嘱的麻烦。如果你的项目聊天记录经常被打断,强烈建议把这类高频操作沉淀成skill,它能帮你把工作流程标准化。
4.4 用脚本辅助状态同步(可选)
文件方案有一个小小的手动负担:每次会话结束后,你得记得让Claude写回状态。如果你跟我一样有时候聊完就关终端、忘了让Claude更新文件,可以考虑写一个极简的shell脚本来自动提交变更。
bash复制#!/usr/bin/env bash
# save_planning.sh - 在Claude会话结束后运行,防止忘记更新
cd "$(git rev-parse --show-toplevel)"
git add docs/planning/ CLAUDE.md 2>/dev/null
git commit -m "chore: sync planning memory $(date +%Y-%m-%d)" 2>/dev/null || echo "没有变更需要提交"
echo "Planning files已提交到git"
这个脚本本质上是把记忆文件变成可版本控制的状态快照。你会得到一个“记忆的Git历史”,未来某天想追溯某个决策是怎么产生的、哪个会话引入了什么变更,直接 git log 就一清二楚。这也凸显了file-based方案的另一个优势:记忆可以走完整的代码评审与审计流程。
5. 常见问题与排查技巧实录
5.1 记忆不生效?先检查CLAUDE.md的加载优先级
很多人在配置完planning文件后反馈:Claude Code根本不主动读文件,答非所问。我排查过几例,最常踩的坑是CLAUDE.md文件优先级混淆。Claude Code存在多个层级的CLAUDE.md:用户级(~/.claude/CLAUDE.md)、项目级(当前工作目录的CLAUDE.md)、以及子目录的CLAUDE.md。其中用户级会在所有项目里生效,项目级只对当前工作目录生效。
如果你的系统级CLAUDE.md里没有读取planning的指令,而项目级CLAUDE.md里配了,那么只有在这个项目目录下启动Claude Code才会触发读取。我建议把“开工前读planning文件”的通用习惯写在用户级里,把项目特有的细节放在项目级——这样可以避免每个项目都要重复配置。另外,CLAUDE.md里不要写太泛的指令,比如“记住一切”,Claude无法理解“一切”是什么;要写成“读取docs/planning目录下所有markdown文件”这种明确路径指令。
5.2 token消耗爆炸?量化PLANNING.md的合理长度
有人担心这套方案会增加token成本。我先给一个估算:一份结构良好的PLANNING.md,假设3000字,大概消耗4000-6000个token(中文字符+Markdown语法),按Claude Code常见的输出价格折算,单次读取成本约0.02-0.06美元,完全在一个可接受的范围。真正的问题不是“读取成本”,而是“阅读顺序”——如果PLANNING.md写得啰嗦,Claude会在加载阶段就消耗大量上下文窗口,挤压后续代码生成的空间。
所以我强烈建议PLANNING.md控制在5000字以内,只放当前迭代和目标状态。背景知识、详细设计放TASKS.md和DECISIONS.md,让Claude按需读取。如果你发现某次会议后PLANNING.md膨胀飞快,说明你写进了太多不属于“总览”层面的内容。用我的话来说,PLANNING.md是电梯汇报,不是文档库。
5.3 文档腐化与记忆幻觉的预防
文件久了会腐烂——这大概是我用这套方案超过半年最深的教训。一种典型情况是:PLANNING.md里写着任务T-101是in_progress,但其实代码早就合并了,PLANNING.md没更新。结果Claude Code一读到这个条目,就会反复问你是否要继续推进T-101,搞得你满头雾水。这就是“记忆腐化”引发的上下文幻觉。
我的解决手段有两个。第一,每周安排一次“记忆整理”会话:让Claude对照实际代码情况,检查PLANNING.md中所有任务的真实状态,把已经不存在的任务标为closed,把“进行中”但代码里无进展的标为blocked。第二,利用Git历史追踪文件更新时间。如果一个任务在PLANNING.md里很长时间没动,但代码提交记录显示相关文件一直在变化,这就说明记忆同步没跟上,需要手动校准。这些整理成本虽然不高,但必须定期做,否则planning文件会慢慢失去可信度。
5.4 安装配置类问题速查(面向刚上手的读者)
如果你还没成功跑起Claude Code,那么记忆设计没有意义。我根据这段时间社区里的高频问题,整理了一份速查表:
| 问题 | 症状 | 解决方案 |
|---|---|---|
| 安装失败 | 执行安装命令后提示找不到cli路径 | 检查Node.js是否在系统PATH中,重新安装后务必重启终端窗口 |
| VSCode插件不能用 | 在编辑器中无法调用claude命令 | 确保CLI端安装成功,且VSCode内置终端继承了系统PATH |
| 提示“组织已禁用订阅” | 企业环境限制 | 用个人账号登录,或联系管理员开通权限 |
| 接入Ollama本地模型失败 | 模型响应质量差或报错 | 确认Ollama版本支持Claude Code的API兼容层,且模型名称正确 |
| 对话乱码 | 中文显示为乱码 | 检查终端编码,Windows下建议在PowerShell里执行chcp 65001切换UTF-8 |
| 想接入DeepSeek等第三方API | 官方API不可用时 | 使用兼容Anthropic API格式的中间层进行适配 |
| 与Codex CLI的比较 | 不确定选哪个 | Codex适合深度绑定GitHub仓库工作的场景;Claude Code更擅长灵活的文件级操作与长任务规划 |
这些是实操里最常卡住人的几个点。顺带说一句,安装Claude Code时,如果卡在权限报错,大概率是你没有用当前用户安装而是用了sudo——Claude Code更推荐安装到用户目录,避免后续PATH混乱。
最后的实操心得
项目跑了快半年,planning-with-files这套方案给我最大的改变,不是省了多少token或者让Claude Code显得多聪明,而是让我重新建立了对AI辅助开发的掌控感。以前我总觉得Claude是一个“用完即走”的临时帮手,每次都得迁就它的短暂记忆;现在它更像是团队里一个真正有长期记忆的同事——开工先看板,结束写纪要,有问题翻档案。
如果你决定尝试这套方案,我的建议是别一上来就照搬所有文件模板,先只维护PLANNING.md一个文件,跑通“开工读档、结束存档”的闭环,两周后再逐步加入DECISIONS.md和PROGRESS.md。记忆系统本质上是一个习惯系统,你的操作越简单,坚持的概率就越高。等你习惯了之后,再慢慢往里加新的记忆类型——你会发现AI和人的协作关系,会因为这一层薄薄的文件而变得完全不同。
