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

关注腾讯云开发者，一手技术干货提前解锁👇

开发者公众号专属群聊

扫码加入获取更多一手教程、科技前沿报告

用 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。

重手段只对增量跑。 比如"注入已知缺陷、看规则能不能抓到"这类验证手段，全量跑代价太高，只在增量或本次改动上做。

-End-
原创作者｜焦成杰

感谢你读到这里，不如关注一下？👇

扫码领取腾讯云开发者专属服务器代金券！