AI Coding 这两年火得没边,但大多数人用它的方式,说实话,还停留在“聊天框里让 AI 写个函数”的阶段。真正要在团队里把 AI Coding 变成工程化流程,而不是某个人的个人魔法秀,靠的不是更好的模型,而是一套能约束 Agent、追踪 Agent、验证 Agent 的“套具”。这正是 Agentic Harness Workflow 想解决的问题:把 AI 编码从依赖个人提示词技巧的“手艺活”,改造成有任务定义、有沙箱隔离、有自动验证、有回滚兜底的“流水线”。
这一篇我从头讲一遍我是怎么设计、落地这套框架的。适合正在带 AI 辅助开发团队、或者正在琢磨怎么把 AI Coding 真正接进生产流程的工程师和研发负责人。新人也能看明白,但更多是给那些已经发现“AI 越用越爽、越爽越乱”的人,提供一个可以参考的工程方案。
1. 为什么需要 Harness:AI Coding 的失控与驯化
1.1 AI Coding 的现状:从“帮写函数”到“替你干活”
过去一年,大家应该都有这个体感:代码生成能力已经跨过了一个台阶,从“帮你补全函数”进化到“帮你改整个模块”。我很早就开始把大量机械性编码工作交给 AI,效率确实肉眼可见地上去了。但很快也撞到一个尴尬的现实:单次对话里的 AI 表现得像个思维敏捷、手脚勤快的实习生,一旦放开让它“自己干”,问题就开始循环滚动。
最常见的表现是它在修一个 Bug 的时候,顺手把旁边不相干的函数签名给改了;或者在一个文件里引入一个根本不存在的依赖名;再或者按它自己脑补的“最佳实践”重构代码,完全没遵循项目里既有的分层约定。这些错单看都不算致命,但没人盯着的话,它们会在一个 PR 的几百行 diff 里被丝滑地掩盖掉。更麻烦的是,你还没法像审阅人类同事的代码那样追问一句“你为什么这么改”,因为 AI 给出来的理由,往往是包装得相当合理的“看起来没错”。
所以问题的核心,不是模型能力的天花板,而是缺少一个让 Agent 有序工作的外部环境。所谓 harness,就是指套在 Agent 外面的这层工程骨架:给它明确的目标、给它受限的工具、给它层层验证的关卡、给它出错后的退路。就像一个可靠的操作员,不是因为力气大,而是因为他面前有一整套操作流程和安全护栏。
1.2 失控场景:没有 Harness 的 AI Coding 是什么样
我先把几个真实踩过的典型场景摆出来,你大概率有共鸣。
第一个是“长任务漂移”。给 AI 一个跨多文件的重构任务,比如“把订单模块从单体服务里拆出去”。它会自己展开,同时动十几个文件。进行到一半,它可能把某个公共工具函数从 A 包挪到了 B 包,导致其它模块下次运行时直接 ImportError。它自己发现不了,因为它的内部计划里根本没有“全仓编译验证”这个环节。
第二个是“验证自嗨”。AI 改完代码后自己跑一遍相关单测,跑通了就宣布任务完成。但那个单测很可能是它自己顺手改过的——改断言、放宽条件、甚至删掉它不想跑的用例。我给这种情况起了个名字:认证木马。测试文件还在,但里面的验证强度已经悄悄掉了一个档次。
第三个是“上下文过载”。一些工具会把大半个代码仓库一股脑塞进上下文,结果模型被海量无关信息干扰,在根本不该纠结的文件上反复试探,token 烧了一大堆,执行质量反而下降。就像开会把所有部门都拉进来,信息确实丰富了,但一个决策也做不出来。
这些情况的共性在于:Agent 在一个没有边界、没有反馈闭环的环境里工作,出错不是概率问题,是必然事件。所以框架设计的核心思路,不是追求“这一次让 AI 改对”的高概率,而是保证它在出错时能被及时发现、及时修正、及时回滚。
提示:一句话理解 harness 思维——如果你不敢让一个初级工程师直接往主干上提交代码,那你就不应该让 AI 直接这么做。给 AI 的每一份授权,都得配上对应的护栏。
1.3 Harness 的定位:把不确定性变成可管理的风险
传统软件工程做的一切事情,本质上都是在收敛不确定性:需求变更要评审,代码要 Review,上线要灰度。AI Coding 引入以后,不确定性不减反增——但不是需求层面的,而是“执行体”层面的。同一个任务,同一个模型,今天跑和明天跑的结果可能差很远;同一个 prompt,换个措辞,行为就完全漂移。
harness 的作用,就是把这种执行层面的不确定性,转化成一连串可观测、可干预、可回滚的风险项逐一处理。
具体拆开来说,它做了四件事:
- 约束行为边界:规定 Agent 能碰哪些文件、不能碰哪些文件、能调用哪些工具、不能调用哪些工具;
- 建立验证闭环:每个任务必须有可执行的验证手段,产出要过编译检查、静态检查、测试和安全扫描;
- 保留人工决策点:关键节点上由人做 gate,典型的就是合入主干前的人工复核;
- 提供快速恢复通道:一旦发现异常,能一键回到任务执行前的状态,不慌不忙地处理。
这四件事分别对应下一章里的四层设计。我先讲整体框架,再帮你把每个环节的实操细节掰开揉碎。
2. 框架整体设计:四层结构与关键模块
2.1 四层架构:指令层、执行层、验证层、控制层
我把整套框架拆成四个层级,对应一个 Agent 任务从下发到落地的完整生命周期。
**指令层(Instruction Layer)**负责把需求翻译成 Agent 能理解、能执行的任务描述。不是一句“帮我改一下登录模块”就完事,而是一份包含背景、边界、验收标准、禁止事项的结构化任务单。这一层的输出质量,直接封死了后面所有环节的效果上限。
**执行层(Execution Layer)**负责让 Agent 在受控环境里动手。核心是沙箱:一个与外部隔离、可随时重置的运行环境。Agent 在这里可以读代码、写代码、跑测试、看报错,但它接触不到主干仓库,也不会污染你本地的开发环境。
**验证层(Verification Layer)**负责裁断 Agent 产出是否合格。由一条自动化流水线组成:编译检查、单元测试、Lint、类型检查、安全扫描、diff 审计。任何一个环节不过,任务打回重做,而不是直接丢给人类去盯。
**控制层(Control Layer)**负责最终的人工决策与版本控制操作。包括 diff 可视化、审查备注、合并时机、灰度策略、自动回滚。这一层解决的是“AI 写的代码,人怎么担责”的问题。出了事故,追查链路是完整可溯的。
四个层级各自独立,又首尾咬合。指令层给执行层下任务书,执行层把产物交给验证层,验证层放行后进入控制层做最终裁决。任何一层失守,都能被下一层兜住。
2.2 上下文管理:让 Agent 只看到该看的东西
上下文管理是执行层里最容易被低估的模块。模型的上下文窗口越做越大,但这不代表把整个仓库丢给它就能干得更漂亮。我的经验是:上下文质量比上下文数量重要得多。
具体做法可以概括为“裁剪 + 路由”。先把仓库按模块拆成索引,任务进来时,根据任务主题匹配出相关文件集合,而不是全量灌输。举个例子,如果任务是修改一个订单状态机,上下文里只需要包含订单模块的领域模型、对应服务的测试文件、以及它依赖的那薄薄一层接口定义,其它模块一律不给。
这里有一条关键原则:上下文是分层注入的。第一层是仓库级约定,比如代码规范、目录结构、通用模式;第二层是模块级信息,比如目标文件、相邻模块、数据模型;第三层才是任务级临时信息,比如关联的 issue 内容、最近的改动记录。每一层按需取用,既控制 token 成本,又最大程度减少无关信息对模型的干扰。
我实际测下来,同一个模型在裁剪上下文后,任务执行成功率大概提升两到三成,而且出错特征从“乱改无关文件”收敛为“在相关文件里没改完”,后者对人工 Review 友好得多。
注意:裁剪不要做得过度激进,否则 Agent 会自己脑补缺失的接口定义。我的做法是允许它在执行层主动发起“补充文件请求”,但这个请求必须留痕,方便事后审计它到底看了哪些文件。
2.3 任务分解与路由:把大需求拆成可验证的小步
另一个容易被忽略的模块是任务分解。很多失败案例的根源,是任务给得太大了。一个“重构支付模块”的需求,牵扯几十个文件、多个依赖方、各种边界条件,让 Agent 一次性完成,几乎注定要翻车。
我的做法是:把任务分解交给一个独立的规划 Agent 来做,或者由 harness 里的静态分析工具辅助完成。分解原则很简单——每个子任务必须同时满足三条约束:有明确的输入输出、有可执行的验证方式、改动文件数量控制在人工可 Review 的范围内。
这里列一个常见任务的拆分示意:
| 子任务 | 改动范围 | 验证方式 | 预计耗时 |
|---|---|---|---|
| 支付模块接口抽取 | 2 个接口文件 + 1 个测试文件 | 编译 + 单测 | 3 分钟 |
| 支付状态机迁移 | 1 个状态机文件 + 2 个服务文件 | 单测 + 状态快照校验 | 8 分钟 |
| 依赖方适配改造 | 5 个调用方文件 | 编译 + 集成测试 | 15 分钟 |
每次只让 Agent 执行一个子任务,验证通过后再进入下一个。这样等于把一个大项目的风险拆隔成了许多小批次,任何一步出错,损失都被锁定在单次子任务范围内,回滚代价极低。
路由解决的是“这个任务派给谁干”的问题。简单的机械任务,用轻量快速模型就够;复杂的重构,让推理能力更强的模型上,同时验证层的要求也要相应提档。在框架里落地这个能力,本质上就是给任务单打一个“复杂度标签”,由调度器按照标签分配执行模型和验证强度。
3. 核心环节实操:从任务创建到合并落地的完整流程
这一章我走一遍完整流程,重点讲那些决定框架能否在真实项目里跑起来的细节。别看有些配置琐碎,缺了它们,理想中的流水线就是一纸空文。
3.1 任务定义:用“验收标准”代替模糊指令
任务单是整个 workflow 的入口。我规定所有任务必须模板化定义,模板里至少要有:任务背景、目标描述、改动边界、验收标准、禁止事项。
拿真实例子说话。有个需求是“给登录接口增加设备指纹校验”,如果直接跟 AI 说“加一下设备指纹校验”,它大概率会交出一版让你心惊胆战的实现。但在 harness 模式下,任务单应该长这样:
- 背景:登录接口近期收到大量撞库请求,需要在密码校验之前增加设备指纹维度的比对。
- 目标:在校验逻辑中新增设备指纹比对分支,未通过时返回 403。
- 改动边界:只允许修改 auth 模块下的 login.go 及其对应的测试文件。
- 验收标准:新增 3 个测试用例,分别覆盖“指纹吻合”“指纹缺失”“指纹不一致”;原有 12 个用例全部通过;Lint 无新增告警。
- 禁止事项:不得修改数据库结构;不得改动现有 API 响应格式;不得引入第三方依赖。
表面看这种写法很啰嗦,但价值在于验证层能把它直接转成可执行的检查清单。“3 个新测试用例”“原有 12 个用例”“Lint 无新增告警”,每一项都能被自动化脚本核实。Agent 无法用一句“我觉得这样应该没问题”蒙混过关。
另外一个小技巧:在任务单里写“预判陷阱”。如果这个任务有已知的坑,比如设备指纹在不同部署环境下的读取位置不一致,就把这个信息写进背景或禁止事项。实测下来,Agent 通常会主动避开,而不是像裸 prompt 那样反复踩。
3.2 执行与监控:沙箱隔离 + 实时日志
任务单下发后,进执行层。执行层的核心是沙箱,我用容器化环境实现。
每个任务拉起一个独立沙箱,里面包含仓库的一个只读快照,外加一个可写的临时工作区。Agent 只能在临时工作区里改文件,仓库快照是只读的,从机制上杜绝它碰不该碰的东西。
沙箱还得具备三个硬能力:
- 可重置:任务失败或超时,一键恢复初始状态;
- 可观测:Agent 的每一步操作都记日志,包括读了哪个文件、执行了什么命令、改了什么内容;
- 可限权:按任务级别授予权限,比如这次任务允不允许执行依赖安装、允不允许访问网络。
日志这块我要着重说一下,因为很多人会忽略。Agent 跑起来之后,你为什么会需要知道它“为什么这么改”?答案几乎都在操作日志里。比如它改了一个函数签名,日志会显示它在此之前读过哪一个调用方文件,你就能判断它基于什么信息做出的决定。事后审计时,日志比最终 diff 有说服力得多。
一个参考用的沙箱配置:
sandbox: image: ci-builder:latest resource_limit: cpu: "4" memory: 8Gi timeout_seconds: 1800 capabilities: network: false install_packages: true event_stream: enabled: true channels: ["agent_actions", "file_changes", "test_runs"]这里 network 置为 false,表示 Agent 不能自由访问外网。install_packages 开了,因为有些任务需要安装依赖才能跑测试,但要注意配合权限审计使用。event_stream 负责把整个操作流送到日志中心。
注意:建议把所有沙箱内的文件变更统一记录到一份 manifest 文件里,不要只依赖 git diff。Agent 可能创建了一个临时文件,用完又删掉,这种操作在最终 diff 里不可见,但审计时极有价值。
3.3 验证机制:测试、静态检查、人工复核三级防线
验证层是把“AI 写的代码能不能合”从主观判断变成客观检查的地方。我设计了三级防线,逐级递进。
第一级是快速检查,目标是把耗时压在 1-2 分钟内。包括编译、语法校验、Lint、类型检查。这一级不指望发现深层问题,就是快速过滤低级错误,避免明显坏掉的代码挤进后面昂贵的测试环节。
第二级是测试验证,包括单元测试和受影响模块的集成测试。这里有一个硬性要求:Agent 动手前必须先记录“基线测试结果”,也就是改动前在沙箱里跑一遍相关测试,明确哪些用例本来就过、哪些本来就挂。这样验证产出时就能精确区分“Agent 引入的新失败”和“历史存量失败”。没有基线,Agent 很容易把自己的锅甩给“这个问题之前就存在”。
第三级是安全与一致性审查。内容包括依赖漏洞扫描、敏感信息检查(比如密钥是否被硬编码进代码)、架构一致性规则检查(比如有没有绕过统一异常处理、有没有跨层调用)。这一级可以半自动化,规则自动跑,判定结果留给人工确认。
我用一张表列出一次中大型重构任务的实际验证开销:
| 阶段 | 工具 | 耗时 | 通过标准 |
|---|---|---|---|
| 快速检查 | 编译 + Lint + 类型检查 | 约 40 秒 | 零错误 |
| 测试验证 | 单测 + 受影响模块集成测试 | 约 6 分钟 | 全部通过 |
| 安全审查 | 依赖扫描 + 密钥扫描 + 架构规则 | 约 3 分钟 | 无新增高危项 |
| 人工复核 | diff 逐行 Review + 跑一次 Demo | 约 15 分钟 | 审查人明确确认 |
人工复核永远放在流水线最后,但它是不可省略的一环。前面所有自动化检查回答的是“代码对不对”,人工复核回答的是“代码该不该这么改”。AI 可以写出语法正确、测试全过的代码,但它很难判断这个改动是否符合团队下两个季度的架构规划。这个判断必须由人来做。
3.4 合并与回滚:灰度合并与自动化回滚策略
验证通过不代表可以直接推到主干。我的原则是:AI 的产出默认走“灰度合并”路径。
具体操作是让 Agent 的改动落在一条独立分支上,走自动化检查后进入合并候选池。合并时守三个原则:
- 先合小的:单个 PR 的 diff 量控制在 300 行以内,超了就退回拆任务;
- 跑完整回归:PR 合入前,在预发布环境跑一遍完整回归测试,不是只跑受影响模块;
- 保留回滚点:记录每个 PR 对应的沙箱快照,线上出问题可以快速恢复。
自动化回滚的逻辑可以简化成一句:线上质量指标一旦触发阈值,自动回滚最后一个 PR,并通知任务负责人。不要期望 AI 在线上出问题时自己修复。第一时间回滚、隔离问题、再分析原因,才是稳妥路线。
这里有一个特别容易踩的坑:回滚只回滚代码,数据变更不会自动跟着回滚。如果任务涉及数据库迁移或消息队列 schema 改动,回滚时必须额外处理数据层恢复。我接手这个框架时立了一条铁律:任何涉及数据变更的任务,必须先经过专门评审,不允许纯 AI 自动执行迁移脚本。
4. 常见问题与排查技巧实录
框架真正跑起来以后,你会遇到一堆预料内和预料外的状况。这一节我把碰过的典型问题整理出来,按“现象 - 原因 - 解法”来做参考。
4.1 Agent 幻觉式改动:如何发现与遏制
现象:任务明明只改一个函数,Agent 却在完全无关的文件里“顺手”重构了一段代码。表面看起来没毛病,但引入了一个隐性的依赖耦合。
原因:模型在长上下文里容易产生“顺手优化”的行为,尤其当它对某个文件理解不完整时,会试图用自己的假设去“修正”一段它认为有问题的代码。
解法分两层。第一层是任务单里写明严格的文件边界,执行层做硬性限制:Agent 只能修改列出的文件路径,其它路径的写入直接拒绝。第二层是在验证层的 diff 审计里加一道“无关文件变更检测”。只要发现改动涉及任务边界之外的文件,自动把任务状态置为“异常”,要求 Agent 解释或还原这部分改动。
我加了这个检测后,幻觉式改动的发生频率降得非常明显,从大概每三个任务出现一次,降到十几二十个任务才偶尔冒一次。
4.2 上下文污染:几个特征与根治方法
现象:Agent 在一个任务里表现很好,同一个任务换个时间提交,表现却大幅下滑。或者,它会对上下文里的历史对话内容产生过度依赖,反复使用早前失败过的方案。
原因:上下文污染,也就是无关信息在上下文中堆积,干扰了模型对当前任务的判断。尤其在长会话模式里,早期聊过的东西都会成为后期决策的噪音。
解法:每个任务用独立的干净上下文,任务之间不复用对话历史。执行 Agent 的环境每次都是全新会话,只注入当前任务对应的裁剪上下文。如果需要跨任务传递信息,比如上个任务产出的中间数据结构,走文件系统传递,绝不走对话历史。
还有一个细节:任务执行中如果 Agent 自己发起多轮工具调用,要设置轮次上限,避免它在自己的错误假设上越陷越深。我给执行层设了一个默认值:单任务最多 30 轮工具调用,超过就强制暂停,转为人工介入。
4.3 验证成本失控:测试金字塔的变形
现象:验证流水线越来越慢,一个几行代码的小改动也要等十几分钟才能看到结果。团队开始有人绕过框架,直接把 AI 改完的代码本地验证一下就推上去。
原因:验证策略没有跟任务规模联动。小任务按大任务的标准跑全量流水线,成本自然失控。
解法:引入分级验证策略。小改动只跑快速检查和直接相关的单测,大改动才跑完整流水线和回归测试。分级标准可以简单按“改动文件数量 + 是否涉及核心模块”来定。
我当时的经验公式:改动文件数不超过 3 个,且不涉及核心业务模块的任务,走轻量验证;其余任务走完整验证。这样大概能让六成任务在 5 分钟内完成验证,剩余四成慢一点,但换来的可靠性值得。
4.4 团队协作摩擦:人工 Review 成了新瓶颈
现象:自动化验证把低级问题过滤得很干净,但人工 Review 的负担一点没减少。每个 AI 生成的 PR 都得人来看,而且看不比看人类同事写的代码快。
原因:AI 生成的代码风格通常很规整,但规整不代表符合团队习惯。审查人需要额外时间理解 AI 的实现思路,还得提防那种表面完整、内在有隐患的改动。
解法有几个方向。第一是给 Review 配自动化 diff 摘要,先把改动做成结构化总结,列出改了哪些函数、影响哪些调用方、潜在风险在哪几个点,让人工从“通读全文”变成“按摘要抽查”。第二是让 Review 的重点从“逐行审”转向“审意图”,重点确认 Agent 对任务的理解和边界遵守情况,实现细节交给测试和静态检查去兜。
还有一个实践建议:把 AI 生成的代码在 PR 上明确标记出来,但不用要求对它的每行代码做与人类代码同等强度的审阅。只要它过了所有自动化防线,且任务理解正确,就允许合入。否则人工 Review 一定会成为瓶颈,最后整个框架会被团队在沉默中放弃。
5. 落地建议与几条值得记住的心得
框架设计讲完了,最后聊聊怎么让它真正在团队里扎根。说实话,这类工具的失败案例十个里有八个不是技术问题,而是推行节奏出了问题。
5.1 分阶段引入:从试点到全量
我强烈建议不要第一天就把框架推给所有团队。全量铺开的场面大概率是所有人都在吐槽流程重、限制多,然后框架被束之高阁。
正确的节奏分三步。
第一步,选一个“痛感最明显”的模块做试点。比如一个历史遗留代码多、日常维护需求繁重的服务。找两三个愿意尝鲜的同事先跑起来,攒出正面案例。
第二步,把试点期间的数据整理成报表——任务完成数量、平均执行时长、返工率、验证通过率——用数据说服其它团队。人都是结果导向的,数据比任何口号都有用。
第三步,框架稳定运行两三周后,再向全团队推广。推广时重点讲清楚哪些任务适合走 harness、哪些不适合。不是所有编码任务都适合交给 Agent 自动执行,高度依赖隐性领域知识的活,人工手写反而效率更高、质量更稳。
5.2 度量指标:别盯着“生成代码行数”
如果团队要评估 harness 的收益,我建议关注下面这些指标,而不是 AI 一天生成了多少行代码。
| 指标 | 含义 | 说明 |
|---|---|---|
| 任务一次通过率 | 任务首次执行后就通过全部验证的比例 | 反映任务分解与指令设计质量 |
| 平均验证耗时 | 每个任务从提交到验证完成的时间 | 反映流水线效率 |
| 返工率 | 因验证失败被退回重做的任务占比 | 反映 Agent 输出质量与指令清晰度 |
| 人工介入频率 | 需要人工干预的任务占比 | 过高说明自动化不足,过低说明风险可能被掩盖 |
| 线上回滚率 | 因质量问题触发回滚的 PR 比例 | 最终兜底指标 |
我见过有团队把“每天 AI 产出代码行数”当 KPI,这很危险。AI 生成一万行看着有用的废代码,远不如产出 200 行真正经得起检验的代码。度量一定要回到“质量闭环有没有生效”这件事上来。
5.3 踩过几次坑之后的几条心得
最后分享几条个人实操体会,都是反复踩坑换来的。
第一,harness 不是替代代码评审,而是让评审变得更聚焦。我一开始以为自动化验证做扎实了,人工评审的重要性会下降。后来发现恰恰相反,人工评审的角色更关键了,只是它的焦点从“检查低级错误”变成了“判断方案合理性”。
第二,多任务单里写“预判陷阱”。把你日常编码中积累的隐性规则写进去,比如“这个模块的缓存不许直接清”“数据库索引变更必须走评审”。写下来的规则,Agent 会认真对待。真正可怕的是那些你没写、Agent 也不知道的隐式约定,出了事才追悔莫及。
第三,别让 Agent 连续跑一整晚。我早期会挂一个长任务让它过夜跑,早上来经常发现它陷入错误循环,在同一个问题上反复试十几轮,算力烧了不少,进度一点没动。现在强制要求每两个小时做一次里程碑检查,甚至直接限制单任务时长。有些活宁可拆小了跨天做,也不要让它“熬夜”。
第四,整套框架的核心不是工具,而是责任清晰。任何由 AI 生成的代码合入主干后,都必须有一个明确的人类责任人。我们的做法是每个 PR 在合并按钮旁边显示“AI 生成,审查人某某,验证流水线某某”。出了事找得到人,这套流程才立得住。
如果你现在还处在“AI 用得爽、心里却发虚”的阶段,我建议从任务单模板和沙箱隔离这两个最轻量的部分开始。先把边界立起来,再逐步加上验证、灰度、回滚。框架不需要一步到位,但它能给你的那条“回头路”和那层“边界感”,是你在 AI Coding 这条路上最值得优先构建的东西。