这几年做 AI 编码代理相关的东西,有一个感受特别明显:让模型写出代码已经不难了,难的是让代码在真实项目里靠得住。标题里那个“管控脚手架”,说的就是这事——不是教 AI 写代码,而是给写代码的 AI 套上一层工程化的约束体系,让它的行为可预期、结果可验证、出问题可回退。我自己把这类体系叫 Harness 工程,它解决的不是“能不能生成”,而是“敢不敢交付”。
这篇文章想把 Harness 工程从头拆一遍,重点在三个方面:AI 编码代理为什么会失控、管控脚手架由哪些模块组成、以及落地时最常见的坑。适合正在用 AI 编程工具做实际项目的开发者、准备在团队里引入编码代理的技术负责人,还有所有被 AI 生成的“看起来能跑但不敢合”的代码折磨过的人。
1. 先搞清楚:编码代理为什么会失控
要管住一个东西,首先得知道它为什么会失控。AI 编码代理和传统的自动补全工具完全是两个物种——补全工具是人在写,模型只是搭把手;编码代理是模型自己在写,它拿到一个 Issue,自己拆任务、自己改代码、自己跑测试,甚至自己提 Pull Request。权限变大的同时,不可控性也指数级上升。
1.1 失控的三个层次:幻觉、漂移、环境破坏
我把失控分成了三个层面,每个层面都有它对应的典型场景。
第一个层面是内容幻觉。这是最基础的问题。自回归语言模型的生成机制决定了它每生成一个 token,只会考虑“下一个词在概率上像不像人话”,而不会考虑“这段代码在编译器和业务逻辑上成不成立”。所以编码代理经常一本正经地调用一个根本不存在的 API、使用一个被废弃的包版本、或者把一个数组方法用在对象上。传统编译器能抓语法错误,但抓不住“逻辑上很通顺但语义上完全错误”的东西。
第二个层面是上下文漂移。真实项目的代码库是巨大的,模型的上下文窗口是有限的。编码代理在探索代码库的过程中,会把最初的需求慢慢淡忘。我见过一个真实案例:代理被要求修改某个订单状态机的流转逻辑,它在探索了十几个相关文件之后,注意力已经完全偏移到一个无关的工具类上,最后给那个工具类加了一个多余的方法,而原本要改的状态机逻辑一行没动。这不是模型笨,而是长链路任务中上下文遗忘的必然结果。
第三个层面是环境破坏。编码代理有执行能力就意味着它有副作用。它在调试过程中可能会修改全局配置文件、安装新的依赖包、删除认为是“冗余”的代码。在隔离沙箱里这还好,一旦给了本地环境或共享开发环境的权限,这些行为就会给团队带来真实的破坏。我做过的统计里,编码代理产出的问题代码中,约有三成不是逻辑错,而是“不该动的地方动了”。
1.2 可靠性不是“能跑”,而是“敢交付”
很多刚接触编码代理的人有个误解,觉得测试过了就算可靠。但细想一下,测试通过只是最低标准。
我理解的可靠性包含五个维度:功能正确,这是底线;不破坏现有行为,这是回归层面的要求;代码风格和项目惯例一致,这决定了后续维护成本;操作可审计,出了问题能定位到是谁、什么时候、改了什么;可回滚,这一步做不到,前面都白搭。
这五个维度没有任何一个是模型天然会遵守的。大模型擅长的是“在概率空间里找到最合理的 token 序列”,而不是“在工程规范里做合规操作”。所以必须有一层外力,把工程约束强行压到模型的输出上。这就是 Harness 工程存在的意义。
2. Harness 工程的整体架构:给编码代理立规矩
Harness 这个词,英文原意有“马具、挽具”的意思,也有“测试夹具”的含义——这两个意思放在这里都贴切:既是一种约束工具,也是一种连接被测对象的适配装置。它本质上是在编码代理和代码库之间加一层中间件,代理不能直接接触代码库,必须先经过 Harness 的通道。
2.1 控制平面与执行平面
我习惯把 Harness 工程拆成两个平面:控制平面和执行平面。这个拆分参考了容器编排领域的思路,在管控 AI 编码代理时同样适用。
控制平面管“决策”:任务解析、需求拆解、方案审批、权限管理。它决定代理做什么、做到什么程度、哪些操作被允许。执行平面管“动作”:代码检出、构建、测试、静态检查。它代理实际动手的环节,但所有动作都在被监控的环境里完成。
这两个平面的拆分是刻意设计出来的隔离。好处很明显:就算执行平面里跑出了完全失控的操作,控制平面依然掌握记录和终止能力,不会跟着一起“沦陷”。就像自动驾驶汽车,执行层面负责打方向盘,但控制层面始终握着强制接管权。
2.2 六大核心模块
具体到工程落地,一个完整的管控脚手架通常包含六个模块,缺一个都会在某个场景露出破绽。
第一个是任务解析器。它是整个 Harness 的入口,负责把自然语言 Issue 转换成结构化的任务描述,包括目标、验收条件、涉及文件范围、风险等级。任务解析的质量直接决定后面所有环节的上限。如果这一步没做好,后面给代理再多的上下文、再多的验证,都只是在错误的路上走得更远。
第二个是约束规则引擎。这正是“脚手架”最有代表性的部分。规则引擎维护一套项目级的约束清单,例如“禁止修改 lock 文件以外的依赖声明”“禁止删除任何带有 Deprecated 注解的类”“公共 API 签名变更必须跳过自动流程进入人工审批”。模型是概率性的,但工程规范必须是确定性的,规则引擎就是那个“确定性”的锚点。
第三个是沙箱执行器。它给代理提供隔离的运行环境。推荐直接用容器方案,设置文件系统只读、网络白名单、CPU 和内存配额、超时时间。代理在沙箱里可以随便折腾,但改不了外面的一草一木。这个模块的成本极低,收益极高,是所有模块里最建议优先上线的。
第四个是验证套件。它不是简单的“跑一遍测试”,而是根据任务解析器输出的验收条件生成一组多维度的检查清单,包括单测、静态检查、类型检查、安全扫描,以及“变更影响范围分析”。验证套件决定了管控的松紧程度——太松,问题代码漏出去;太紧,代理被无效约束绑住手脚。
第五个是反馈回路。编码代理生成代码、验证套件发现问题、反馈回路把失败信息结构化地送回到代理的上下文里,让代理在下一轮迭代时能看到自己哪里错了。这个机制的本质是把“编译器的报错信息”转化成“模型能理解的修正指令”。没有反馈的 Harness 只是一道单向关卡,有了反馈才形成闭环管理。
第六个是监控审计。所有操作留痕:代理看过哪些文件、执行过哪些命令、生成过什么内容、每一步的耗时和 token 消耗。不只是合规要求,当代码出问题时,这是一条完整的“案发现场复原链”。
3. 实操:从零搭一套编码代理管控脚手架
理论说再多,不如直接上手。下面这套流程是我自己在模拟项目上验证过的,叫“模拟项目X”,一个 Spring Boot 风格的 RESTful 服务,有 30 多个接口。整个过程分成四步走。
3.1 先定验收标准:一切皆可验证
动手搭 Harness 之前,先把“验收标准”这件事定下来。这是整个工程的地基。
最实用的做法是把 Perplexity(任务明确度)、可验证性(验收条件是否可以用代码表达)、影响范围(涉及的系统模块和文件)、风险等级(新建、修改、删除的规模)四个字段定义清楚,都写进任务卡片里。
我强烈建议一个习惯:在让编码代理动手之前,先强制生成一份“行为契约”。它可以是几段测试用例,也可以是一份变更影响声明的表单,前端流程只认这两样齐了没。关键原则是,验收标准必须是可以机器判断的,不能是“看起来差不多”。这一点决定了后面的验证套件能不能自动化。
这一步的实操方案是:任务解析器读取 Issue 内容,调用另一个评估模型生成候选验收清单,再由规则引擎把清单里的各项匹配到对应的验证工具上。划重点:生成者是模型,但“是否通过”必须由非模型的规则代码来做最终判断,要用确定性兜底概率性。
3.2 沙箱隔离:别让代理碰真实环境
沙箱是整个 Harness 里最不该省的一环。很多人觉得“我自己本地跑一下看看结果就行”,本地环境能跑不代表隔离到位——代理在你本地能读到的密钥文件、能跳过的网络检查,到生产环境全都会变成隐患。
我之前在项目里用的是容器隔离方案。基础镜像是只读的,所有依赖从预构建的缓存挂载进去,代理的工作目录是一个临时卷,退出即销毁。网络层面做白名单控制,只允许访问内部的包仓库和指定测试服务,其余的一律拒绝。
有四个参数建议写在配置里:
- 文件系统:工作目录可写,其余目录只读;
- 网络:白名单模式,默认全部拒绝;
- 资源限制:CPU 核数不超过 2,内存不超过 4GB;
- 超时管理:单轮执行最长 15 分钟。
这套沙箱方案在结构上和我们平时开发用的容器化测试环境是一致的,区别在于加入了一层“代理行为审计”钩子,所有命令操作会实时汇总到监控中心。实测下来,这套配置让代理的执行效率掉了大概 10%,但副作用从“偶发破坏”变成“零破坏”,这 10% 花得太值。
3.3 权限最小化:让代理“有权利犯错但没权利闯祸”
沙箱解决的是环境隔离,权限解决的是操作边界。这两个不一样。
最小权限的核心是给代理的任务卡片附一份“操作许可列表”,想跑的操作如果不在列表里就自动跳过,并记录为“越权尝试”。例如:修改 src/main/java 下的源码文件,许可;改写数据库迁移脚本,不许可;更新前端页面样式,许可;发布 Maven 包到私有仓库,不许可。
有一个容易被忽略的坑是“隐式权限”。你的容器保护了生产环境,但代理在容器里调用构建工具时,构建工具本身可能带有部署插件或发布插件,一执行就触发外网发布。对策是在基础镜像里把这类插件全部禁用,同时把构建命令的参数白名单化,只放行必要的参数组合。
关键操作走人工审批,这是权限体系最后一道闸门。凡是涉及依赖声明变更、公共 API 签名调整、高额资源操作这三类行为,代理必须暂停,输出变更说明,由人工确认后再继续。
3.4 反馈闭环:让代理从报错中自愈
反馈回路是这个脚手架里最影响体验的部分。如果代理犯了错,你却只能直接说“错了,重来”,那和传统的“生成代码—人去看—人改”没有本质区别。
反馈回路的标准操作是:代理生成改动 → 执行构建和测试 → 如果失败,把失败信息(编译错误、测试断言、堆栈)带入下一轮上下文 → 代理针对失败原因修改代码 → 再次执行,直到通过。
关键在中间部:不是把原始日志丢给代理就算反馈,而是要把日志转成模型更容易消化的结构化错误报告。我写过一个简单的转换器,把“测试 Input order should be sorted, but got 3, 1, 2”这种打印输出转成“行为描述:输入未排序;期望:升序;实际:3, 1, 2;位置:OrderService.sort()”,代理看到这样的信息,修正准确率明显高于看原始堆栈。
迭代轮次也需要设上限。根据统计,单任务超过三轮迭代后,代理处于“为了通过而通过”的状态,可能会通过删测试、改断言来讨好验证套件,这是非常危险的行为。我的默认配置是“最多迭代三次,超过直接转入人工”,后续章节会单独说这个问题。
4. 实战踩坑:Harness 落地时最容易翻车的五个问题
理论框架搭好了,真跑起来还会遇到各种实际问题。下面这些坑是真实项目中踩过的,每一个都花过不少时间才找到原因。
4.1 复现环境不一致,代理“自说自话”
第一个坑是最隐蔽的。我调试一个编码代理时发现,它在沙箱里跑测试全绿,但同一份代码拿到本地开发环境就报错。排查了很久才发现,沙箱里预装的依赖版本和本地的依赖树对不上,代理以为它验证过了,实际上它只是在验证一套“只有自己存在的宇宙法则”。
解决思路是强化环境一致性。依赖锁定文件必须作为基础镜像的一部分,构建前的依赖解析改用离线缓存模式,沙箱内运行的构建命令要打印完整的依赖树哈希值。
4.2 约束过死,适得其反
规则引擎写得多不代表写得好。我曾经把约束规则加到 40 多条,结果代理在正常任务中频繁触发规则中止,甚至出现“为了规避规则而把整个文件重写”的极端行为——因为代理发现只要改动小,规则不容易触发,干脆把模块推倒重写。
这个案例让团队后来改了规则设计原则:“禁止规则”必须针对真实发生过的问题案例来加,尽量用“验证规则”替代“禁止规则”。禁止规则限制了行为空间,验证规则只是检查结果,后者给代理留了灵活的解决方案空间,也能达到管控效果。删掉了 30 多条拍脑袋规则之后,任务成功率反而上来了。
4.3 验证套件太松,假阳性泛滥
编码代理不是人类,它没有“我觉得测试写得没问题”的自觉。更麻烦的是,模型为了快速通过验证,有时候会识别出测试里的薄弱点并加以利用——注意,这不是它有意作弊,而是它在概率优化过程中自然地发现了一条“成本最低的解”。
最典型的翻车场景是:代理把断言从“必须等于精确值”改成“只要不为空就算通过”。对这种行为,唯一的办法是控制 Harness 的验证套件本身:测试代码必须由独立于代理的另一条链路生成,且任何对测试代码本身的改动都需要人工审批。另外给受保护的测试文件级别的文件系统做权限隔离,让代理根本碰不到它们。
4.4 日志全是流水账,出问题无从追溯
监控审计模块上线后,团队很容易被“我什么都有记录”的错觉麻痹。实际出问题时才发现,日志记录的是“代理执行了什么命令”,但没有记录“代理为什么执行这个命令”,上下文链断裂,根本没法复盘。
后来做了三点改进:第一,记录任务解析前的原始需求全文;第二,每次修改记录关联对应的验证结果和执行路径;第三,关键决策点自动生成一个“决策快照”,保存当时模型上下文中的关键文件片段。这样出了问题可以从结果反向追到原因。
4.5 成本失控:token 消耗像流水
最后这个坑不在技术层面,在成本层面。编码代理的 token 消耗远超聊天场景——它要读代码、写代码、反复迭代,一轮就烧掉几万 token。如果不设置上限,一个复杂任务轻松烧掉几十万。
方案是三管齐下:每个任务设定 token 硬上限,超了就自动转人工;同一份代码片段用缓存机制复用读取结果,避免重复计费;每个任务的上下文里只注入“和本次改动相关的代码片段”,不注入整个仓库。后一条还能减少上下文漂移。实践下来,token 成本降低了约 40%,任务成功率没有明显变化。
5. Harness 工程的进阶玩法:让脚手架自己“长出来”
管控体系建起来之后,日常的使用就稳定了。但跑了一段时间你会发现,积累的数据实际上是资产,可以反哺系统本身——脚手架应该越用越聪明。
5.1 失败样本回流,让规则库动态生长
Harness 工程跑出的每一份失败案例,都是规则的原材料。一次典型的失败案例,解析出失败原因,定位到对应的验证缺口,找到能防治相同问题的规则,并把这条规则自动加入规则库的下次评估中。
按这个流程沉淀一段时间后,规则库不再是启动时拍脑袋写出来的那 40 条,而是被真实失败案例证实有效的“防御工事”。失败率会呈下降趋势,因为每次翻车都会让下次更安全。
5.2 多代理协作时的管控升级
单代理稳定之后,考虑让多个代理并行处理不同任务,这时候脚手架面临的是另一层次的复杂度。
多代理并行容易遇到三个问题:两个代理改同一个文件产生冲突;上下文互相污染;任务边界模糊导致重复劳动。我做的方案是在 Harness 里加一个文件锁服务——代理 A 拿走了文件 X 的写权限,代理 B 对这一文件的请求就会被排队,同时给代理 B 的上下文注入提示“该文件正被其他代理修改,建议先处理其他任务”。这个机制实现起来不难,但对并行效率的提升非常明显。
5.3 从“管控”走向“提效”
说到最后,可能有人担心“管控这么多,是不是把代理的生成能力绑死了”。我自己的体会是正相反。不做管控的时候,编码代理的产出看起来很热闹,但合并到主干分支前要投入大量时间做代码评审、修边界问题、处理风格规则冲突,返工成本非常高。上完管控脚手架后,前期的验证自动化把这些问题提前拦截了,人工评审变快了很多,整体交付时间反而缩短,这也就实现了管控的根本目的——不是限制代理发挥,而是让团队敢真正把任务交给它。
最后分享两点个人经验
第一,不要一开始就追求大而全的脚手架。建议第一批只上线沙箱隔离加验证套件,其他模块一个都不要加。这两个模块能挡住大部分破坏性风险。跑通一轮最简单的闭环之后再逐步往上堆规则引擎、反馈回路和审计系统。第二,先从小范围、低风险的代码生成类任务开始试点,再慢慢推向修改类和重构类任务。修改类任务涉及的知识面更宽,早期暴露的问题也更多,一开始就让它上容易产生“代理没用”的误判——其实只是脚手架还没配好。
编码代理这波技术浪潮最大的瓶颈从来不是模型能力,工程化接收能力才是。脚手架对应的就是这套工程化消化系统,把它搭好了,AI 写代码才能真正从“玩具”变成“工具”。