Harnessing AI, not surrendering to it
上一篇聊 Agent Coding 实践的文章发出去,转眼四个月过去,年中都过了。本来没打算这么快再写一篇,但这半年 AI 圈变化太快,快到我年初总结的那套方法论现在已经有点不适合了。倒不是说之前那套错了,是得跟着 AI 的变化一起调整。
模型这条线倒没什么悬念,还是一直在发展。Claude 出到了 Fable 5,Codex 也迭代到 5.6,能力比年初又强了一截。让我更在意的是另一条线:社区好像没那么热衷讨论”怎么让模型更聪明”了,转头开始琢磨”怎么把模型管住”。harness engineering、loop engineering 这些新词就是这么冒出来的,叫法五花八门,指的却是同一件事:模型本身已经够强,接下来要下功夫的地方在模型外面。
我上一篇讲的 Rule、Spec、Plan、Task 那套流程,其实就是朝这个方向走的雏形。只是这半年又做了几个真实项目,踩了不少坑,自己那套东西也前前后后重做了好几版。所以这篇不打算讲什么大概念,就想老实聊聊我现在怎么看这件事,还有我自己的 harness 是怎么一步步长成现在这个样子的。
模型变强,为何依然难以掌控
先说个我自己的体感。现在的模型确实强,强到你会有种错觉:是不是随便丢个需求过去,它就能把活儿漂亮地干完?真上手做几个项目就知道,不是这么回事。
它最大的问题不是不会做,是不听话。你让它改登录页一个按钮的样式,它顺手把整个组件重构了;需求里白纸黑字写着”不要动数据库层”,它还是自作主张加了个字段;同一个需求,今天做是一个方案,明天再来一遍又是另一套。能力越强,它自己发挥的空间反而越大,一旦跑偏就越难收场。说白了,模型给你的永远是那个概率最高的答案,未必是你真正想要的那个。
这半年做下来我越来越确信:模型能力已经不是瓶颈了,真正的瓶颈是我能不能把它稳定地控制在我想要的轨道上。这也是这篇标题的意思:掌控 AI,而不是屈服于它。这件事现在看,是真的迫在眉睫。

Harness Engineering:模型之外的工程
那怎么才能把模型控制住?这就要说到 harness engineering 了。
这个词听着有点玄,拆开看并不复杂。harness 本来是马具的意思,套在马身上,让你能驾驭这匹马。放到 AI 上,harness engineering 就是一套软件工程:在模型之外,把约束它、纠正它、验证它的那一整套基础设施搭起来。模型会犯错是它的本性,你没法指望它自己不犯错,能做的是在外面架一层,让错误在造成后果之前就被拦下来。少了这层工程化的验证,项目的可靠性基本无从谈起。
道理不难懂,难的是这套东西到底该怎么搭。我也不是一上来就想明白的,是靠把前面几版做砸,才慢慢摸出点门道。所以在讲我现在这套之前,先花点篇幅复盘一下自己走过的弯路。
前四版的迭代与教训
第一版基本就是一个 AGENTS.md/CLAUDE.md 打天下,所有约束全压在这一个全局规则文件里。问题是这个文件本身写得就不规范,agent 实现功能非常不稳定,同一个需求跑出来的东西时好时坏。
第二版加上了项目级的上下文,把这个项目特有的规则、代码风格、SQL 该怎么写这些单独拎出来讲清楚。效果比第一版好了一大截,但两个硬伤没解决:做需求还是得反复跟 AI 确认,缺一个能自动循环验证的机制;规则写在那儿,agent 到底遵没遵守,也没法保证。
第三、四版我推倒重来,照着 OpenAI 那套 harness 的思路做了一遍 SDD,用 Spec、Plan、Task 一层层往下拆需求,还配了一套很重的 workflow 去规定 agent 每一步该怎么走。现在看,这一步错得很彻底:workflow 越重,token 烧得越快,agent 的思考反而被框死了。更麻烦的是规则打架。我给后端配了一整套 Spring Boot、Java、SQL 的通用规则,跟项目自己的规则一冲突,agent 就开始做错误判断;测试和代码审查这两块,我在规则里写一遍、在 skill 里又写一遍,项目规则里还另有一份测试规则,三处各说各话,乱成一锅粥。最后上下文被这些打架的规则撑得巨大,agent 的行为却越来越不受控。这一版折腾到头,效果可能还不如第二版。
第五版:五个子系统
经历这些之后,我静下来做了一次反思。前面几版栽跟头,共性很清楚:我一直在往里加东西,加规则、加流程、加 workflow,总觉得约束越多越可控,结果约束自己先打起来,agent 越来越懵。第五版我干脆换了个问法,不再问”还能加什么约束”,而是先想清楚:agent 跟我协作,到底有哪几个问题是非回答不可的。
数下来是五个:它该怎么工作、现在该做什么又不该碰什么、上次做到哪、怎么证明做对了、一次会话怎么开始怎么收尾。每个问题对应一个子系统:指令(Instruction)、范围(Scope)、状态(State)、验证(Verify)、生命周期(Lifecycle)。别把它们看成并排的五个模块:前四个各管一次干活里的一个维度,最后一个负责把它们在一次次会话之间接住,不让链条断掉。

指令系统(Instruction)
它回答”agent 该怎么工作”。核心是一张地图,也就是 AGENTS.md。我把它压到一百行以内,只放最高频、每次都得看的东西:项目是什么、跑哪些命令、红线在哪、什么算做完。详细的专题规则,比如 SQL 怎么写、某个模块的边界在哪,全都拆到 docs/ 下的专题文档,AGENTS.md 里只留一行”什么时候去读哪份”。CLAUDE.md 不重复任何内容,只做一个指向 AGENTS.md 的桥接。至于”某一步具体怎么做”这类过程性指令,我交给 skills,需求、实现、排障各有一个,用到哪个调哪个,平时不占上下文。这么一来 agent 每次进来看到的是一张干净的地图,而不是一本它自己都读不完的几百行百科全书。
范围系统(Scope)
它回答”现在该做什么、不该碰什么”,就是给 agent 划边界,免得像前面吐槽的那样,让它改个按钮,它顺手把半个模块重构了。全局的硬边界放在 AGENTS.md 顶部,就几条红线,每次必看。具体到某个需求的边界,靠 grill 和 spec 两个 skill 在动手前先钉死:grill 把模糊的需求一个一个问清楚,spec 再把谈拢的东西写成明确的验收条款。边界先划死,agent 才没那么容易自作主张地扩大战场。
状态系统(State)
它回答”上次做到哪、为什么这么定”。这块是我这一版花力气最多的,因为跨会话、换模型的时候,最容易丢的就是状态。核心是一个 feature_list.json,我拿它当任务的唯一权威源:每个任务一条记录,带着状态和验收方式,任何要动代码的活,都得先在这儿挂个号。旁边两个文件各管一段:PROGRESS.md 是会话的叙事快照,写”现在做到哪、下一步干嘛”,每次覆盖重写;DECISIONS.md 只增不改,专门记那些难撤销、过一阵自己都会看不懂的关键决策。这几个再加上 git,凑成了 agent 的记忆。
验证系统(Verify)
它回答”怎么证明做对了”。这是前面几版最要命的短板:那些版本清一色是”推理式”的前馈,写一大堆规则告诉 agent 该怎么做,却没有任何东西在事后核一核它到底做没做到。第五版我把验证做硬:完成的标准写死在 AGENTS.md 里,一个 check 命令把 lint、类型检查、测试、项目自己的检查脚本全串起来,任何一项没过就是没过。更要紧的是,任务状态不能由 agent 自己嘴报。它想把一个任务标成”通过”,唯一的路是跑验证脚本、拿真实证据回写;手动改状态,下一次 check 就当场抓出来。凡是能沉淀成脚本、hook、ArchUnit 的约束,我绝不让它停在文档里靠自觉。
生命周期系统(Lifecycle)
它回答”一次会话怎么开始、怎么收尾”。前四个子系统管的都是一次干活内部的事,可真实项目是跨几十次会话、甚至跨不同模型和工具在推进的,接力棒一旦掉地上,前面做得再漂亮也白搭。所以我给每次会话定了固定的开工和收尾动作:开工先读 PROGRESS.md、跑一遍基线自检,确认接手的是个绿的状态,再动手;收尾把状态更新好、check 跑绿、提交,干干净净交给下一棒。这套动作打包在一个 handoff skill 里,AGENTS.md 底部也钉着一份开工/收尾清单。图的就是一件事:随便哪个新会话,读几个文件就能在三分钟内接上,不用指望”上下文还在”。
五个子系统各自落在哪些工件上,汇总成一张表:
| 子系统 | 回答什么问题 | 落在哪些工件上 |
|---|---|---|
| 指令 Instruction | agent 该怎么工作 | AGENTS.md(地图)+ docs/ 专题规则 + CLAUDE.md(桥接)+ skills |
| 范围 Scope | 现在该做什么、不该碰什么 | AGENTS.md 硬约束段 + grill / spec skill |
| 状态 State | 上次做到哪、为什么这么定 | feature_list.json(唯一权威源)+ PROGRESS.md + DECISIONS.md + git |
| 验证 Verify | 怎么证明做对了 | AGENTS.md 命令契约 / 完成定义段 + check 聚合器与检查脚本 + feature-verify |
| 生命周期 Lifecycle | 一次会话怎么开始、怎么收尾 | AGENTS.md 开工/收尾清单段 + handoff skill |
子系统如何协同运转
这五个子系统单独看是五个部分,跑起来是一条链。拿一次具体的任务走一遍就清楚了。
会话一开始,agent 先读那张地图(AGENTS.md)和状态文件(PROGRESS.md),知道规则在哪、上次做到哪;然后从 feature_list.json 里挑一个任务,挂号、置为进行中,这一步就把它摁在”一次只做一件可验证的事”上,不许它顺手夹带别的。接下来该写代码了,对应的 skill 把这一步的过程管住:需求不清就先 grill,动手就走实现的红绿环。等它说”做完了”,脚本闸门上场,check 命令真刀真枪跑一遍,验证脚本拿到证据,才允许把任务标成通过。最后 handoff 把状态写回 PROGRESS.md、提交 git,接力棒交给下一个会话。
这条链的关键,是每一环都咬着下一环:需求侧钉死了要什么,执行侧不许账外施工,验证侧用脚本说话,审查侧对着最初的验收条款核,会话侧保证换人换模型也不断档。你想在哪一环偷懒,后面一环就会对不上账,当场把问题顶出来。说到底就一句话:在这套体系里,不存在台账之外、验证不了的”完成”。
三个阶段的实际收益
这套东西的价值,是把关口分散到 agent 干活的三个阶段:动手前、干活中、交付后。每个阶段,都有对应的子系统在兜底。

前馈:执行之前
agent 开口第一句之前,指令、状态、范围三个系统已经把该给的上下文摆好了。最大的变化在这儿:以前指望它读完一篇长长的规则文档就能自觉遵守,现在是把要紧的东西拆成它必然会读到的结构,动手前就喂进去。驾驭一匹马,得一开始就套好挽具,等它跑出去了再追就晚了。前馈做扎实,它从第一步就大概率走在正轨上。
运行中:执行之中
这一步靠的是状态和范围。agent 最容易犯的毛病,是做着做着就飘了:上下文一长,把最初要什么忘得干干净净,或者顺手把不该动的地方也动了。feature_list.json 就是那个锚,它随时知道自己在账上的哪一条;Scope 划下的边界时刻提醒它别越线。这一版用下来,我很少再碰到”跑到一半发现它在做另一件事”的情况了。
后馈:交付之后
这也是我觉得变化最大的一环。以前 agent 一句”功能已实现,测试通过”,我是没法直接信的,得自己去翻、去核,常常发现它要么没真跑、要么避重就轻。现在完成的判定权不在它嘴里,在 Verify 手里:证据是验证脚本自己写的,它想蒙混,check 那关就过不去。“做完了”这三个字,从一句需要我反复确认的话,变成了一个有 exit code 和证据背书的事实。
三个阶段连起来,就是标题那句话的落地:不是我追在 agent 后面到处救火,而是提前把局布好,让它在我划的轨道里使劲。它还是那个能力很强的模型,只不过这一次,方向盘攥在我手里。
结语
写到这儿,我最想说的不是”你该照着我这套做”。恰恰相反。
这半年冒出来的新词,harness engineering、loop engineering、context engineering,我都翻过、试过,有用的留下,不合的丢掉。但我越来越警惕一件事:别把这些社区造的词当成教条。今天被奉为圭臬的最佳实践,很可能就是明天要删掉的累赘。前四版踩的坑,很大一部分就是太拿某套现成框架当真,照搬了一堆根本不属于我项目的东西。
所以比起记住某个词,更要紧的是想清楚它背后到底要解决什么。不管叫 harness 还是别的什么,做的都是同一件事:跟着 AI 的能力一起调整。它变强了,就把多余的限制删掉,别挡它的路;它还够不着的地方,就把必须的约束加上,别让它闯祸。约束的多少没有定数,刚好卡在它当前能力的边界上就行。
模型还会继续变强,这套 harness 我大概也还得接着改。但有一点应该不会变:真正的目标一直很简单,让 AI 在能撒开手的地方尽情发挥,同时把方向牢牢攥在自己手里。掌控它,而不是被它牵着走,这大概就是现阶段,我能给”人到底该怎么和 AI 协作”这个问题的答案。
参考资料
这篇里聊的不少想法,是我读了下面这几篇、再对着自己项目消化出来的。想系统了解 harness engineering,原文比我这篇讲得透彻得多:
- Harness engineering: leveraging Codex in an agent-first world — OpenAI · 2026.02
- Effective harnesses for long-running agents — Anthropic · 2025.11
- Harness design for long-running application development — Anthropic · 2026.03
- Harness engineering for coding agent users — Birgitta Böckeler · martinfowler.com · 2026.04