# AI写得快 ≠ 真正提效：一文讲清Harness“记忆”和“验证闭环”

**来源：** 微信公众号「腾讯云开发者」
**作者：** 焦成杰
**发布时间：** 2026-09-23 08:30
**原文链接：** https://mp.weixin.qq.com/s/SVm_GONXEElhEsX6CXylKg
**获取时间：** 2026-09-23（Asia/Shanghai）
**抓取方式：** 浏览器核对正文，Chrome UA 获取 HTML，BeautifulSoup 清洗引流头尾
**正文 SHA-256：** caeff2770aeafb14f3640f5184b102f647d9c147656600a65276efdcb87fae84

---

## 正文

用 AI 写代码，"写得快"和"真正提效"是两件事。我踩的坑集中在三处：一是 AI 没有记忆，每次新会话都从零开始，同一个模块的链路、隐藏的命名规则、上次踩过的坑，它都要重新摸索一遍；二是 AI 改完就停，它把代码改对就认为任务结束，而真正的交付还要验证、提交、等上线、确认线上生效；三是 AI 会犯错，还会自作主张，它会挑"看起来对"的接口调用，会在长链路里忘记上下文，也会为了让结论好看而缩小验证范围。所以我做的不是"让 AI 更聪明"，而是给它套上一套驾驭系统（Harness）：有记忆、能动手、跑完闭环，并且在关键路口有人守着。
01
让 AI 有记忆：知识库与上下文工程
1.1 整体是怎么运转的：写入、读取、全自动
后面各小节都是这一节的展开，先给一张全景。
写入：知识怎么进知识库
知识只有一个来源：会话复盘——任务结束后，AI 自己回顾全过程，把"代码里没有、要反复探索才能拼出来"的知识写成文档，落到两级索引结构里。
写入时必须守三条硬规则：① 只写三类内容（阴性知识、代码位置索引、隐含关联关系），代码里已写明的一律不写；② 新主题落到对应模块目录下，并在该模块 agents2.md 追加一行索引，保证索引与目录一一对应；③ 一律"定点修改"，禁止整篇覆盖——覆盖会抹掉 frontmatter 里的成熟度与引用统计。
读取：知识怎么被用上
固定顺序，只读两三篇：
读一级索引 agents.md（只列模块）→ 定位到所属模块；
读该模块二级索引 <模块>/agents2.md（列出文档 + 一句话描述）→ 判断哪篇相关；
读那篇实际文档；
都没覆盖到，才允许自己去探索代码。
这个顺序由一条 hook 强制：本会话没读过一级索引之前，读文件、搜索、执行命令、MCP 调用都会被拦下。也就是说，"先查知识库再动手"不是靠自觉，而是绕不过去。
全自动：不需要人工介入
写入、整理、聚合、索引同步、日志轮转，全部由 AI 与脚本在任务结束时自动完成。人不需要为知识库做任何额外动作——知识库自己长、自己淘汰。
1.2 为什么要建知识库
代码和文档能告诉 AI "这里有什么"，告诉不了它"这些东西怎么串起来"——比如某个字段的真实语义和字面意思不一样、某个配置改了之后要连带改哪三处、系统 A 的动作实际会触发系统 B 的什么行为。这类知识没有任何静态引用能直接看出来，只能靠人反复探索拼出来；而拼出来之后如果只留在某一次会话里，下一个会话、下一个人又要重来。
所以知识库只记这一类连接性知识，具体三类：
阴性知识：代码里没写清、容易误判的事实；
代码位置索引：关键逻辑/配置/入口的具体文件路径；
隐含关联关系：不通过静态引用能看出来的跨系统连接。
代码里已经写明的业务逻辑、参数列表、目录结构，一律不记——那些 AI 自己读代码就知道。
1.3 知识库长什么样：两级索引
知识库是一个 Obsidian vault，通过 MCP 访问。结构是两级索引：
agents.md（只列模块） └─ <模块>/agents2.md（列出本模块所有文档 + 一句话功能描述） └─ <模块>/xxx.md（正文 + 头部 frontmatter）
AI 的固定动作是：读一级索引定位模块 → 读二级索引判断哪篇相关 → 读具体文档。即使知识库长到几百篇，也只读两三篇就能命中，不会一上来就全文检索。
每篇文档头部还有一段 frontmatter（元数据区），记这篇知识的成熟度、被引用次数、用过的场景、依赖的文件路径等。它不参与阅读——AI 读正文时看不到这段元数据，它是给治理机制（见 1.8）和 Obsidian 视图用的。
这带来两个工程上的约束：读整篇用"定向读"只取正文，避免把元数据一起拉进上下文；写入一律"定点修改"、禁止整篇覆盖——覆盖会把看不见的 frontmatter 一起抹掉，成熟度和引用统计就静默丢了。
1.4 让"先读知识库"变成硬约束
只写在提示词里，AI 遵守是概率事件。所以这里用了三种机制，先说明它们分别是什么：
hook（钩子）：在工具调用前后自动触发的脚本，能拦下这次调用。它不是提示词，AI 绕不过去。（使用内部 CodeBuddy 实现）；
rule（规则）：写在项目里的一段约束文本，每次会话自动注入给 AI。表达意图方便，但属于"软约束"，AI 可能不遵守；
command（斜杠指令）：用 /xxx 拉起的一段固定流程，本质是一份写死的任务说明。
三者配合起来：
hook 层强制：一个 PreToolUse hook 挂在所有探索类工具上（读文件、搜索、执行命令、MCP 调用）。当前会话没读过一级索引时，这些调用一律被拦下，并在阻断消息里告诉 AI 该先去读 agents.md。
rule 层引导：同时保留一份规则文本，两层并存——hook 是硬拦截，规则是软引导。
工具链也从知识库同步：command、rule、hook 脚本的源都放在 vault 的 toolchain/，每次会话开始时由 SessionStart 脚本自动同步到工作区。也就是说，知识库不只是"被 AI 读的资料"，它同时是工具链的发布源——改一条规则，下次会话就生效。
一个细节：hook 是同步阻塞的（客户端要等它返回），所以重量级同步放 SessionStart（每会话一次），PreToolUse 上只留轻量判定，否则每次工具调用都要付一遍启动开销。
hook 示例（kb-first-read.py 精简）：hook 从 stdin 拿到工具名与入参，未读一级索引时对探索类工具返回 exit 2 阻断。
GUARDED_TOOLS = {"Read", "Grep", "Glob", "Bash", "mcp_call_tool", "mcp_get_tool_description"}BLOCK_MESSAGE = ( "动手前必须先读知识库一级索引：调用 obsidian MCP 的 vault_read，" '参数 path="agents.md"，读完再执行其他操作。\n')>def main(): ......
注册在 settings.local.json：PreToolUse + matcher: "*"，命令为 /usr/bin/python3 -S .codebuddy/hooks/kb-first-read.py。
rule 示例（read-agents-md-first.mdc，节选）：
---description: 动手前先读知识库一级索引：两级索引导航入口、Obsidian MCP 读取方式与工具选择、MCP 不可用时的降级要求alwaysApply: true---># 规则：动手前先读知识库一级索引 `agents.md`（导航索引是唯一正确入口）>## 二、正确入口（固定顺序，两级索引）1. 先读**一级索引** `agents.md`（只列模块）→ 定位到所属模块2. 再读该模块的**二级索引** `<模块>/agents2.md`（列出本模块目录下所有文档及功能描述）→ 命中主题3. 读对应的实际文档 `<模块>/xxx.md`4. 文档没覆盖的，才允许自己动手探索 / 跑脚本验证
1.5 写入规范：记什么、不记什么
一条知识值不值得写，用一句话判断：脱离"这次任务"的上下文单独拿出来看，是否依然成立、依然有用。
按这条标准，以下内容不写：过程性元信息（日期、执行者、变更记录）、范围声明式表述（"本次改动范围内"）、给不出验证方法的猜测（只能进文档末尾的"待验证"小节）、过度取证细节（具体日志条数、耗时数字）。
这套规范不是我事后总结的，而是直接写进了驱动复盘的规则里，AI 每次按它筛：
规范原文（retro-knowledge-on-session-end.mdc，节选；同一份规则的“执行动作”部分见 1.7）：
只筛出三类、且现有文档尚未记录的内容：
1、阴性知识——代码和现有文档里没有写清楚、需要反复探索才能拼出来的信息
2、代码位置索引——关键逻辑/配置/入口的具体文件路径
3、隐含关联关系——代码之间（跨代码库或同代码库内）不通过静态引用能直接看出来的连接关系
1.6 自动化知识整理
知识库如果不能自动生长，就会变成又一个需要人工维护的文档站。整理由两条指令驱动，一轻一重：
maturity-lint（高频轻任务）：会话结束时自动跑一次。它只读新增的引用数据，不读全库正文、不做内容整理、不做衰减，做四件事——聚合弱/强信号 → 提升等级、累加计数 → 同步索引列 → 日志轮转。只提升，从不降级。 它已沉淀为脚本（.codebuddy/scripts/maturity-lint.py），指令本身只负责"前置探测 + 调脚本 + 转述报告"。
cleanup-knowledge-base（低频重任务）：周期性跑一次。它读全库，做三件事——整理内容（去重、消除歧义）、规范两级索引结构（索引与目录一一对应、补齐缺失元数据）、执行全库衰减扫描。它只整理已有内容，不探索新知识、不做需要代码验证的新增，因此不能拿它代替日常的 maturity-lint。
两者是刻意的频率分工：便宜的机械动作高频跑，昂贵的判断性动作低频跑——把全库巡检塞进每天执行，成本会高到没人愿意跑。
1.7 自动复盘
复盘分两层，目的不同：
会话复盘（沉淀知识）：任务闭环结束后，AI 回顾从排查到线上验证的全过程，把新产生的知识补进知识库。它解决的是"经验不沉淀"。
AI 执行复盘（反哺 skill）：用另一个 AI 分析 AI 的会话记录，检查它是否遵守了排查流程、是否调用了正确的接口、结论是否合理、有没有漏步骤，输出"做对了什么 做错了什么 skill 哪里写得不清楚导致 AI 犯错"。它解决的是"AI 犯同样的错"——改的是 skill，不是骂 AI。
会话复盘由一条规则驱动：它声明"任务结束时必须做这件事"，并写清记什么、落到哪、怎么报账。规则原文（节选）：
规则示例（retro-knowledge-on-session-end.mdc，"执行动作";节选）：
二、执行动作
读知识库：一级索引 agents.md → 模块二级索引 <模块>/agents2.md → 相关实际文档，理清现有主题范围、避免重复记录。
回顾本次会话从探索到实现的完整过程，只筛出三类、且现有文档尚未记录的内容……
落地（两级索引）：模块已有主题 → 定点补充；无 → 新建文档，并在该模块 agents2.md 追加一行索引。
报告本次引用：列出本次实际采用的知识文档，并把清单追加到 <工作区>/.codebuddy/kb-refs.log。
随后执行增量聚合：按 maturity-lint 把本次引用并入知识库统计。
1.8 知识成熟度与引用追踪体系（新手可跳过）
这是让知识库能"自动淘汰"的关键——每篇文档的元数据里带着它的成熟度和引用记录：
四级成熟度：draft < verified（被真实任务采用过）< proven（跨会话、跨场景被复用）< archived。
提升只看强信号：被采用过 1 次 → verified；被采用过 2 次以上、且场景数 ≥2 → proven。
衰减按类型定周期（navigation 6 个月 decision 9 pitfall·process 12 / model 18），算法是幂等的——多跑几次不会加速衰减。
信号有两条通道：弱信号自动采集（hook 在放行"读知识文档"时记一行日志，说明谁读了哪篇；它只作诊断，不参与提升和衰减）；强信号靠声明（任务结束时 AI 明确报出"本次实际采用了哪几篇、用在什么场景、结论是否被验证"，只有这一路参与成熟度计算）。
这套体系怎么自动跑起来：
采信号：弱信号由 hook 自动写 kb-usage.log；强信号由会话结束时的复盘声明写 kb-refs.log。
聚合：maturity-lint 读这两个日志的新增段，按 (session, path) 去重后累加 ref_count、对 scenes 取并集，据此重算等级。
回写：脚本经 Obsidian 的本地 REST API 直接改 vault（与 MCP 是同一个服务），frontmatter 用定点 patch，不做整篇覆盖。
防重复计数：每个日志配一个检查点文件，记"已处理到哪个字节"并带该段前缀的哈希，下次只读新增部分；取舍是"宁可重复计数，不可丢数据"，所以检查点写失败会在报告里明确指出。
同步索引列：等级变化后把新值写回对应模块 agents2.md 的"成熟度"列，AI 下次读索引即可看到。
轮转：日志超过 1 MB 自动归档（保留最近 3 份），检查点归零，避免校验成本无界增长。
也就是说，一篇知识从"被读过"到"被采用"再到"升级/降级"，全程自动，不需要人工介入。
这套机制的价值是：知识库自己长、自己淘汰，不需要人工定期清理。 AI 的入口是索引表里的"成熟度"列——成熟度越高，越优先采信。
02
让 AI 闭环：编码 → 验证
2.1 为什么要做闭环
AI 把代码改对了，任务走了不到一半。后面还有：本地能不能跑起来、测试环境验证是否通过、提交和 MR 门禁是否放行、改动什么时候真正上线、线上问题是否真的不再复现、单子该流转到哪个状态。任何一步断了，前面都白做。
所以闭环的目标很直接：让 AI 从"改完代码"一路走到"线上验证通过"，中间不允许静默停下。
同时有一条原则：把运动员和裁判员分开。写代码的 AI 和审查、验证的 AI 是不同角色，不能既当运动员又当裁判员——这是抵抗惰性（AI 的，也是人的）最有效的办法。
2.2 能力底座：四个 skill 的组合
AI 要闭环，前提是"能动手"——它得知道代码跑在什么系统上，能读到系统里的信息，能触发系统里的动作。这靠的不是一个大而全的程序，而是一组 skill：每个 skill 是一份给 AI 的操作手册（SKILL.md + scripts + references），告诉它这类事情该怎么做、该调什么脚本。
四个 skill 各管一段，串起来才是完整闭环：
> 前提：所有验证一律在测试环境做，禁止在正式环境做任何验证动作。
平台访问 skill：让 AI 能操作你的系统
AI 要排查问题、要验证改动，前提是它能拿到系统里的信息、能触发系统里的动作。做法是把平台 API 封装成脚本，再用 skill 告诉 AI"这类问题该调哪个脚本"：
脚本层：按系统分目录封装 API——查任务、拉日志、取产物、启流水线……人在多个平台之间来回切换登录的操作，脚本把它串成一条命令；
skill 层：每个 skill 只覆盖一类场景，写明触发条件、操作步骤、常见坑；
对外集成：同一批能力可以再封装成 MCP Server，供外部 AI 平台调用。
这部分可以整体替换：换成自己的 CI/CD、日志、工单系统，做法一样——先有脚本，再有 skill。
代码提交 skill：把提交规范固化成流程
提交这件事的难点不在 git 命令，而在规范：单号关联、门禁、分支口径、MR 流程，任何一步靠 AI 自由发挥都会出问题。所以把它固化成 skill，让 AI 按流程走：
关联需求单：commit 消息必须带单号（--story=<短ID> / --bug=<短ID>）；skill 里把"没拿到单号就不进入提交环节"写成硬前置，而不是建议；
门禁自动解决：MR 起来后 QTA 等检查会给出结论。skill 把"等检查 → 读到失败项 → 定位原因 → 修复 → 重跑"这条链固化下来，AI 不需要人盯着就能把门禁过掉；
自动发 MR 与合入：按仓库约定走完整流程（建分支 同步目标分支 发 MR 等门禁 合入 / 切回），而不是停在 push；
自动留痕：每次发 MR 写一条审计记录（发起时间、合入时间、链接、仓库、关联单号），事后可查。
一条经验：对 AI 来说"提交代码"不等于 git commit，而是"提交 → 发 MR → 合入"的完整语义。这个语义必须在 skill 里写死，否则 AI 会在 commit 之后就停下来等你确认。
等待 skill：让 AI 能等到系统跑完（核心）
这是整个闭环里最关键的一块。
AI 的默认行为是"发起了动作就算完成"——它把流水线触发了、把任务启动了，就认为事情办完了，然后拿着旧产物去验证。没有等待能力，闭环就是假的。
等待 skill 要解决三件事：
等到终态：轮询目标对象（流水线 任务 门禁）直到成功、失败或超时，而不是查一次就下结论；
按正确的维度等待：等的是"这个分支的这次构建"，不是"最近一次构建"——等错对象，拿到的产物根本不是你的改动；
超时如实上报：等不到就说等不到，不能假设"应该已经好了"。
有了它，AI 才能做到"改动真的生效了，我再去验证"——这是闭环成立的前提。
本地验证 skill：让 AI 先在本地验证
提交之前先在本地跑通，是最便宜的一道防线。但"本地验证"对 AI 并不直观——不同形态的程序验证方式完全不同，而且坑很多：
服务型程序：本地配置往往不在仓库里（构建时从配置中心拉取），直接读仓库里的空配置文件会误判"没有配置"；
脚本型程序：依赖运行环境注入的环境变量，本地裸跑会缺参数，需要先从真实任务里把环境变量提出来；
客户端 / 服务端型程序：缺环境变量时部分逻辑会静默返回 null——编译照常成功、结果完全没产生，最容易误判"已生效"；
前端：构建命令通常同时包含类型检查和打包，不能只跑打包绕过类型检查。
本地验证 skill 的价值就在这：把这些"看着成功、其实没生效"的坑提前写清楚，让 AI 知道该怎么验、以及什么情况下不能算通过。
2.3 一体化 command：一条指令跑完整个流程
上面四个 skill 是"零件"，command 是把它们串成流水线的"总装"。
/close-loop：给一个需求单 bug 单 + 要做的事，AI 从排查 → 编码 → 本地验证 → 提交 发 MR / 合入 → 等生效 → 线上真实闭环验证 → 流转单子 → 汇报 → 知识复盘，一路走到底，中间不静默停下。简单任务到这一步就够了。
/close-loop-create：连单子都还没有时用——给一段需求描述和所属迭代，它先建单，再自动接上 close-loop。
一般的使用方法是：
/review-req-chat （先聊清楚需求：模糊、缺失、矛盾的地方当场问清） ↓/review-req-doc （把结论落进需求文档，保持简洁） ↓/close-loop-create 或 /close-loop （再进闭环）
close-loop：把八步写死在指令里
一条指令能跑完整个流程，靠的不是"更聪明的 AI"，而是把步骤顺序写死——每一步做什么、什么条件下不许停，都固化在指令里。原文（节选）：
close-loop 原文（节选）：
执行一个“闭环任务”：用户提供需求单/bug 单号 + 想做的事情描述，你负责完成从背景分析/问题排查 → 编码 → 验证 → 推送/发MR/合入 → 等待生效 → 线上真实闭环验证的完整链路，直到任务真正生效完成，不在中间任何一步静默停下。
执行步骤（完整闭环，逐步推进）
背景分析 / 问题排查：…理清：问题现象/需求背景、涉及的代码库与分支、验证口径。
编码：定位并修改代码，只做用户要求的功能，不生成无关文件。
验证（本地/测试）：改完先验证通过才进入提交；验证口径有歧义先与用户确认。
推送 发 MR 合入：按约定执行 commit → push → create MR → merge MR。
等待生效：合入后等待改动上线……轮询到终态。
线上真实闭环验证：用客观依据确认线上已实际生效、问题不再复现……“代码已合入/已发布”本身不算验证通过。
汇报：总结排查结论、改动内容、验证依据、MR 链接、发布/生效状态、单子流转结果。
知识复盘更新：任务闭环执行完毕后……复盘本次执行产生的新知识并更新到知识库。
整条指令里最关键的是开头那句"不在中间任何一步静默停下"——AI 的默认倾向是"做完一步就汇报、等你指示"，不明确禁止，闭环就会断在第 4 步或第 6 步。
前置 review-req-chat：只提问，不编码
需求不清就直接开跑，AI 跑得越快、错得越远，所以闭环前先加一道澄清。这条指令很短，全文如下：
review-req-chat 原文：
帮我对当前的需求进行澄清，找出其中模糊、缺失或矛盾的地方。
1、若需求中提到了具体文件，先读取并理解其内容，再基于此提出问题。
2、只对疑惑的点、或对编码有重大影响的决策提问；无关紧要的细节可自行合理决策，不必询问。
基于我的回答，将澄清后的完整需求写入一份新的 markdown 文件，保存到 docs/design_md/ 目录下。要求保持文档简洁，不要加入设计相关的细节性内容。
在我确认可以开始编码前，不要编码。
它最重要的设计是最后一句："在我确认可以开始编码前，不要编码"——把最容易越界的动作（"我觉得需求清楚了，直接开写"）明确锁住。/review-req-doc 是同一套澄清的文档版：只针对疑惑点和影响编码的决策提问，把澄清结果补进需求文档并保持简洁。
2.4 复杂任务：close-loop-team
单点小改，close-loop 一条指令就够；改动面大、需要多轮审查与验证往复时，用 close-loop-team。
与 close-loop 的区别
优化点：清空上下文，让目标聚焦
团队版真正解决的，不是"人多干得快"，而是长链路里上下文会被压缩、AI 会忘。
每个成员被调用时都是全新上下文，只带着"这一轮该干的事"——目标天然聚焦，不会被前面几十轮的排查过程带偏；
阶段之间靠制品文件传递信息（排查结论、改动说明、审查意见、验证报告），而不是靠对话历史；
由此有一条硬要求：制品必须精确。审查和验证给出的问题要写到"文件:行"；回退时把上一轮的问题清单原文注入给编码成员，不能只说"上次有问题请修复"。制品写得越准，重载后的成本越接近"直接改代码"的下限。
另外两条与效率直接相关的设计：
审查和验证成员在工具层面被禁止改代码（直接去掉写文件的工具），而不是靠提示词约束；
循环有上限：编码⇄审查、验证⇄编码、线上验证⇄编码各最多 3 轮；回退到编码后，验证链必须重新完整走一遍（不允许跳过审查直接验证）；达到上限一律停下报告，blocked failed not_verified 都如实上报，不得静默继续。
流程重量要匹配任务规模：单点小改用顺序版，改动面大才上团队版。
03
怎么抄、抄到哪、边界在哪
3.1 可复制性：最小落地路径
不必一次做全，按这个顺序最省力：
先建记忆：一份两级索引的知识库，只记"反复探索才能拼出来的知识"；再给"先读知识库"加一条硬约束（hook 层拦截）——不加约束，它不会成为习惯。
再给手脚：把最高频的操作能力脚本化、封装成 skill。优先做"等待 skill"——没有等待能力，AI 会在系统还没跑完时就去验证，闭环是假的。
最后串成一条线：把"排查 → 编码 → 本地验证 → 提交 / 合入 → 等生效 → 线上验证 → 汇报"固化成一条 command，并把"达上限就停、如实上报"的循环控制写进去。
改动面大时再上团队版：单点小改不需要，别一上来就套重流程。
第 1、2 步与具体业务系统无关，任何团队都能直接抄；第 3 步里的平台 skill 换成你自己的系统即可。
3.2 适用边界
交付型任务用严格约束，探索型任务要放开。 目标明确、有验收标准的（修 bug、做需求）适合上面这套流程；还不知道要做成什么样的能力建设，套重流程会直接掐死探索。
流程重量匹配任务规模。 单点小改用 close-loop，改动面大才上 close-loop-team。
重手段只对增量跑。 比如"注入已知缺陷、看规则能不能抓到"这类验证手段，全量跑代价太高，只在增量或本次改动上做。

## 清洗说明

移除了正文前的扫码入群引导及文末关注、代金券推广；保留三章正文、代码示意和作者原有论述。
