
1. 这个项目到底在解决什么问题先把这个标题拆开来看。核心场景是AI 编码代理在长会话中遭遇上下文压缩后会丢失关键状态导致行为退化。标题里用了“脑叶切除术”这个比喻非常精准——上下文窗口被截断或摘要压缩的那一刻代理就像被切掉了部分记忆之前建立的架构决策、变量命名约定、正在进行的重构进度全部变得模糊甚至消失。我最初遇到这个问题是在一个持续数天的重构任务里。代理在前 30 轮对话中表现很好能记住“所有数据库访问必须走 repository 层”“错误码统一用 E_ 前缀”“不要动 legacy 目录下的文件”这些约束。但当对话累积到接近上下文上限、系统触发压缩之后它开始重新引入直接数据库调用错误码风格也变了甚至试图修改明确禁止触碰的目录。这不是模型能力问题是状态管理问题。这个项目要解决的就是让 AI 编码代理在上下文被压缩后依然能恢复到正确的工作状态。思路借鉴了两个东西Unix 的 init.d 服务管理机制以及电影《记忆碎片》里主角用纸条和纹身对抗短期记忆丧失的方法。前者提供了“系统启动时按顺序恢复服务”的框架后者提供了“用外部持久化载体补偿记忆缺失”的核心策略。适合谁来参考如果你正在用任何 AI 编码代理做超过单次会话能完成的任务或者你在构建需要跨会话保持状态的代理系统这套方法可以直接抄作业。不需要你懂内核开发但需要你对代理的工作流程有基本理解。2. 核心设计思路为什么是 init.d 加《记忆碎片》2.1 上下文压缩为什么会导致代理“失忆”主流 AI 编码代理的上下文管理策略通常是这样当 token 数接近模型上限时系统会把较早的对话轮次做摘要压缩或者直接截断。摘要压缩看似保留了信息但丢失的是精确性和约束的强制性。比如原始对话里写的是“禁止修改 /legacy 下的任何文件”摘要可能变成“用户提到过 legacy 目录”约束强度从“禁止”降级为“提及”。代理在后续决策时就不会把这条当作硬性规则来遵守。截断更粗暴直接丢掉最早的内容。而最早的内容往往包含项目初始化时设定的全局约定这些约定恰恰是后续所有工作的基础。我实测过一个场景在一个 15 万 token 的会话中压缩发生后代理对“使用 pnpm 而非 npm”这条规则的遵守率从 100% 降到了约 40%它会混用两者导致 lock 文件冲突。2.2 init.d 机制给了什么启发Unix 系统的 init.d 目录下存放的是一系列启动脚本系统启动时按编号顺序执行每个脚本负责恢复一个特定的服务或状态。关键特性有三个顺序性、幂等性、自描述性。顺序性保证依赖关系正确幂等性保证重复执行不会产生副作用自描述性让每个脚本能独立说明自己做什么。把这套机制映射到 AI 代理的状态恢复上我们可以把代理需要记住的关键状态拆分成独立的“状态恢复脚本”每个脚本负责恢复一个维度的信息比如项目结构约定、编码规范、当前任务进度、已知问题列表。当检测到上下文被压缩后按顺序执行这些恢复脚本把关键状态重新注入到代理的上下文中。2.3 《记忆碎片》的方法论外部载体补偿电影里的主角无法形成新的长期记忆他的应对方式是把关键信息纹在身上、拍立得照片上写备注、重要事项写在纸条上随身携带。核心逻辑是不依赖内部记忆而是依赖外部持久化载体。对应到代理系统不依赖上下文窗口来保存状态而是把状态写到代理可以主动读取的外部文件中。每次代理开始工作前先读取这些文件恢复状态每次做出重要决策后把决策写入文件。上下文压缩只会影响对话历史不会影响文件系统上的持久化内容。这两个思路结合就形成了完整的方案用外部文件作为状态载体用 init.d 式的顺序恢复脚本在每次会话开始时重建代理的工作上下文。3. 状态持久化的具体实现3.1 状态文件的目录结构设计我采用的目录结构如下放在项目根目录下的.agent-state/中.agent-state/ ├── 00-project-context.md ├── 01-coding-conventions.md ├── 02-current-task.md ├── 03-known-issues.md ├── 04-decision-log.md └── 05-file-map.md编号前缀对应 init.d 的执行顺序确保恢复时依赖关系正确。比如02-current-task.md可能引用01-coding-conventions.md中定义的规范所以必须先加载规范再加载任务。每个文件都是纯 Markdown 格式原因很简单代理读取和写入 Markdown 的准确率远高于 JSON 或 YAML。我试过用 JSON 存状态代理在写入时经常出现括号不匹配、引号转义错误的问题而 Markdown 几乎没有这个问题。3.2 每个状态文件的职责与内容模板00-project-context.md存放项目的全局信息技术栈、目录结构概览、关键依赖、构建命令、测试命令。这个文件变化频率最低通常只在项目初始化时写入一次。# 项目上下文 ## 技术栈 - 语言TypeScript 5.x - 运行时Node.js 20 LTS - 包管理器pnpm禁止使用 npm 或 yarn - 测试框架Vitest - 构建工具tsup ## 关键命令 - 安装依赖pnpm install - 运行测试pnpm test - 构建pnpm build - 类型检查pnpm typecheck ## 目录约定 - src/core/核心业务逻辑不允许引入外部 IO 依赖 - src/adapters/外部服务适配层 - src/utils/纯函数工具 - legacy/历史代码禁止修改01-coding-conventions.md存放编码规范这是代理最容易在压缩后遗忘的部分。每条规范都要写成强制性的祈使句避免模糊表述。# 编码规范强制 1. 所有数据库访问必须通过 repository 层禁止在 service 中直接调用 ORM 2. 错误码统一使用 E_ 前缀格式为 E_MODULE_REASON 3. 所有公开函数必须有无歧义的返回类型标注 4. 禁止使用 any必要时用 unknown 加类型守卫 5. 日志使用结构化 logger禁止 console.log 6. 文件命名使用 kebab-case类型命名使用 PascalCase02-current-task.md记录当前正在进行的任务包括任务描述、已完成步骤、下一步计划、阻塞项。这个文件更新频率最高每次代理完成一个子步骤后都应该更新。03-known-issues.md记录已知但尚未解决的问题、临时绕过方案、需要后续处理的技术债。这个文件的作用是防止代理在压缩后重复踩已经踩过的坑。04-decision-log.md记录重要的架构决策及其理由。格式是“决策 理由 替代方案 为什么没选替代方案”。这个文件在代理需要做类似决策时提供参考避免前后不一致。05-file-map.md记录关键文件的作用和相互关系。当项目文件较多时代理在压缩后容易忘记某个文件是干什么的这个文件提供快速索引。3.3 状态写入的触发时机状态写入不能太频繁否则会拖慢代理的工作节奏也不能太少否则压缩发生时状态不完整。我的经验是设置以下触发点代理完成一个完整的子任务后更新02-current-task.md代理做出架构决策时追加到04-decision-log.md代理发现新问题时追加到03-known-issues.md代理修改了文件结构时更新05-file-map.md每 N 轮对话我设的是 10 轮强制刷新一次所有状态文件注意状态写入应该是代理的主动行为而不是外部脚本的被动抓取。被动抓取只能拿到对话文本无法区分哪些是决策、哪些是闲聊。主动写入虽然增加了代理的负担但状态质量高得多。4. 恢复流程的完整实现4.1 检测上下文压缩是否发生恢复的前提是知道压缩发生了。有几种检测方式第一种是显式信号。如果你的代理框架支持在压缩时触发回调直接监听即可。这是最可靠的方式。第二种是隐式检测。在每轮对话开始时让代理回答一个只有记得早期上下文才能答对的问题比如“当前项目的包管理器是什么”。如果答错或犹豫说明压缩已经发生。第三种是轮次计数。根据模型的上下文窗口大小和平均每轮 token 消耗估算压缩可能发生的轮次提前触发恢复。这种方式最粗糙但不需要框架支持。我实际用的是第一种加第二种的组合框架回调触发恢复同时每 5 轮做一次隐式检测作为兜底。4.2 恢复脚本的执行顺序恢复流程模拟 init.d 的启动过程按编号顺序执行# 伪代码示意恢复流程 for state_file in .agent-state/0*.md; do content$(cat $state_file) inject_to_context $content verify_state $state_file doneinject_to_context把文件内容注入到代理的当前上下文中verify_state让代理确认它理解了该文件的关键约束。验证步骤很重要我试过只注入不验证代理有时会“读了但没记住”验证能强制它处理这些信息。验证的方式是让代理用自己的话复述该文件的核心约束。比如注入编码规范后问它“列出三条你必须遵守的编码规则”。如果复述不准确重新注入。4.3 恢复后的状态校验恢复完成后做一次综合校验给代理一个需要综合运用多个状态文件才能回答的问题。比如“当前任务进行到哪一步下一步要做什么有什么已知问题需要注意”。如果代理能准确回答说明恢复成功如果遗漏了某个维度说明对应的状态文件没有被正确处理。这个校验问题本身也可以作为恢复流程的一部分每次恢复后都问一遍形成固定流程。4.4 与代理主循环的集成恢复流程不应该是一个独立的外部脚本而应该集成到代理的主循环中。我的做法是在代理的 system prompt 中加入以下指令## 状态管理协议 1. 每次会话开始时依次读取 .agent-state/ 下所有编号文件 2. 读取每个文件后用一句话总结其核心约束 3. 如果检测到上下文被压缩表现为你无法回忆早期对话细节立即重新执行步骤 1-2 4. 完成子任务后更新 02-current-task.md 5. 做出决策后追加到 04-decision-log.md 6. 发现新问题后追加到 03-known-issues.md这段指令本身也会被压缩所以需要在恢复流程中重新注入。这就是为什么恢复流程要包含“重新读取 system prompt 中的状态管理协议”这一步。5. 实操中踩过的坑与解决方案5.1 状态文件膨胀导致恢复变慢最初我把所有东西都往状态文件里塞结果04-decision-log.md涨到了几千行每次恢复都要读取大量无关内容既浪费 token 又稀释了关键信息的权重。解决方案是给每个状态文件设置容量上限和淘汰策略。04-decision-log.md只保留最近 20 条决策更早的归档到archive/目录。03-known-issues.md只保留未解决的问题已解决的移到resolved/目录。02-current-task.md只保留当前任务已完成的任务移到completed/目录。淘汰策略的关键是归档不等于删除。代理在需要时仍然可以主动去 archive 目录查找历史信息只是恢复流程不会自动加载这些内容。5.2 代理不遵守状态写入协议这是最常见的问题。代理在忙碌时容易忘记更新状态文件尤其是在连续处理多个子任务时。我试过几种解决方案第一种是在每轮对话结束时强制检查。在代理的输出解析器中加入一个检查如果本轮对话涉及代码修改但02-current-task.md的修改时间没有更新就自动提醒代理更新状态。第二种是把状态更新作为任务完成的条件。在任务定义中明确写“完成任务后必须更新状态文件否则任务不算完成”。这利用了代理对任务完成度的敏感性。第三种是外部定时器。每 10 分钟检查一次状态文件的修改时间如果超过阈值就主动提醒代理。这种方式适合长时间运行的代理。实测下来第一种加第二种的组合效果最好第三种作为兜底。5.3 恢复后的行为不一致有时候恢复流程执行了代理也确认理解了状态但实际行为还是和压缩前不一致。排查后发现原因是状态文件中的约束不够具体。比如01-coding-conventions.md里写“使用结构化日志”代理恢复后可能选择了一个和之前不同的日志库。改成“使用 pino 库通过 src/utils/logger.ts 导出的 logger 实例”之后行为就一致了。教训是状态文件中的每一条约束都要具体到可验证的程度。模糊的约束在恢复后会产生歧义具体的约束才能保证行为一致。5.4 常见问题速查表问题现象可能原因排查方法解决方案恢复后代理重复已完成的工作02-current-task.md 未及时更新检查文件修改时间强制在子任务完成后更新恢复后代理违反编码规范01-coding-conventions.md 约束不够具体让代理复述规范把约束具体化到可验证恢复流程执行但代理行为不变状态文件未被真正注入上下文检查注入日志增加验证步骤让代理复述状态文件越来越大没有淘汰策略统计各文件行数设置容量上限和归档策略代理忘记写入状态写入协议未被遵守检查文件修改时间把写入作为任务完成条件恢复后代理不知道当前进度02-current-task.md 内容过时对比实际进度每轮对话后强制刷新6. 进阶优化与扩展思路6.1 状态文件的版本控制把.agent-state/目录纳入 Git 管理每次状态更新都产生一个 commit。好处是可以追溯代理的决策历史出问题时可以回滚到之前的状态。坏处是 commit 历史会很碎需要定期 squash。我的做法是每天 squash 一次把当天的状态更新合并成一个 commitcommit message 自动生成列出当天的主要决策和任务进展。6.2 多代理场景下的状态共享如果你同时运行多个代理处理不同的子任务状态文件需要区分共享状态和私有状态。共享状态放在.agent-state/shared/下私有状态放在.agent-state/agents/{agent-id}/下。恢复流程先加载共享状态再加载私有状态。共享状态的写入需要加锁机制避免多个代理同时写入导致冲突。简单的做法是用文件锁复杂的做法是引入一个轻量的状态协调服务。6.3 状态压缩与摘要当状态文件确实太大时可以对历史部分做摘要压缩。但摘要必须保留约束的强制性和决策的理由只压缩描述性的内容。比如“决定使用 pnpm 因为 npm 在 monorepo 场景下有已知的 hoisting 问题”这条决策摘要后应该是“使用 pnpm理由monorepo hoisting 问题”而不是“考虑过包管理器选择”。6.4 与向量检索的结合当状态文件数量很多时可以用向量检索来按需加载。把每个状态文件切片并嵌入向量数据库恢复时根据当前任务描述检索最相关的片段。这种方式适合状态信息量极大的项目但增加了系统复杂度小项目没必要上。7. 我在实际使用中的几点体会这套方案我用了大概三个月覆盖了多个持续数天的重构和开发任务。最明显的感受是代理的“性格”变稳定了。压缩前和压缩后它的行为风格、决策倾向、对规范的遵守程度基本一致不会出现之前那种“换了个人”的感觉。另一个体会是状态文件的质量比数量重要。我一开始追求状态文件的覆盖面恨不得把每个细节都记下来结果恢复流程又慢又容易稀释关键信息。后来精简到六个文件每个文件只保留最核心的内容效果反而更好。还有一个反直觉的发现状态写入的负担反而提升了代理的工作质量。因为代理知道自己的决策会被记录它在做决策时会更加谨慎会主动考虑“这个决策值得记录吗”“理由充分吗”。这种自我审视的过程客观上减少了随意决策。最后分享一个小技巧在04-decision-log.md的每条决策后面加一个“置信度”标注高置信度的决策在恢复后直接执行低置信度的决策在恢复后重新评估。这个标注不需要很精确用高/中/低三档就够了但能帮助代理在恢复后合理分配注意力。