给 Coding Agent 加上质量门禁:测试如何进入 Agent Loop

简介: 本文提出“质量门禁(Quality Gates)”理念,将测试、静态分析等确定性检查嵌入Coding Agent工作流,形成分层验证闭环:修改后触发快速门禁(Lint/类型检查/关联单测),任务完成前执行集成与回归测试,PR阶段由CI进行全量验证。Harness自动接管测试执行与反馈解析,失败结果结构化送回Agent驱动修复,同时通过Hook机制(如PostToolUse、Stop)实现流程管控,兼顾效率与可靠性。

在过去的软件开发流程里,Code Review、测试、CI 和静态分析共同承担着代码质量检查。Coding Agent 加入开发工作流后,一个新的问题出现了:Agent 持续读取代码、修改文件和运行命令,代码生成速度越来越快,人工逐行 Review 很难同步跟上。

《Agentic Code Quality》中给了一个应对之策:Quality Gates,把测试和其他确定性约束放进 Agent 周围的 Harness 和执行环境,用它们判断一次代码变更能否进入下一阶段,以及是否满足最终交付条件。

落到 Coding Agent 具体的执行流程里,便是测试应该如何进入 Agent Loop?比起只在 Prompt 中要求“完成修改后,记得运行测试”,更推荐让 Harness 接管这部分流程。代码变化后自动触发验证,失败结果重新进入上下文,Agent 再根据反馈继续修复,满足门禁条件后进入下一阶段。

测试进入控制流

一个基本的 Coding Agent Loop 是模型持续与执行环境交互:读取代码、修改文件、运行命令,获取工具返回结果,再根据新的环境状态决定下一步。

测试很适合作为这条 Loop 中的反馈信号。Anthropic 在 Agent Eval 的拆解中也采用了类似结构:Coding Agent 通过多轮工具调用修改环境,任务结束后,再由单元测试等评估器(Grader)根据最终环境状态判断任务结果。

例如,Agent 修改了一段认证逻辑:

pytest tests/test_auth.py

如果测试失败,断言信息、失败用例和堆栈信息会重新进入上下文,方便 Agent 定位问题,再修改代码实现并运行下一轮测试。此时,系统形成了一条基本闭环:

修改代码
   ↓
运行测试
   ↓
获得结果
   ↓
FAIL → 分析错误 → 修改代码
   ↓
PASS
   ↓
继续任务

问题在于,如果“运行测试”仍由模型自行决定,这条闭环就可能被跳过。Agent 可能认为任务已经完成,也可能因为上下文接近上限,或者判断当前修改无需测试,从而提前结束验证。

Harness 可以把测试固定到流程里。文件发生修改后,根据预设规则触发对应的测试;测试通过,Agent 就继续执行后续任务;测试失败,Harness 将失败结果重新送回模型,让 Agent 进入下一轮修复循环(Repair Loop)。

这就形成了反压(back-pressure):编译器拒绝非法代码、测试返回失败、安全策略拦截某些操作、CI 阻止部署,都会把问题及时挡在当前阶段。反馈越早回到 Agent,错误状态继续向后传播的范围就越小。

到这里,测试在 Agent Loop 中承担的角色也发生了变化:它不仅负责给出验证结果,还会影响 Agent 下一步是继续执行、进入修复,还是结束任务。

图 1:来自 Anthropic《Demystifying evals for AI agents》中 Simple Eval / Multi-turn Eval 的示意图

分层验证与反压

测试进入 Loop 后,需要解决另外一个问题。如果 Agent 每修改一个文件,都要运行完整的测试集,验证本身可能会成为新的瓶颈。

因此,质量门禁适合根据执行阶段和验证成本进行分层。第一层放在代码修改后,重点是尽快返回结果给 Agent。Harness 根据 changed files 触发格式检查、类型检查和相关单测,尽早发现语法错误、类型问题和局部行为回归。

这里的要点是确定一次代码修改应该触发哪些测试。小型项目可以维护源码与测试目录的映射,例如 auth/ 下的修改对应 tests/auth/;项目规模扩大后,还可以结合模块依赖关系、Build Graph 或历史覆盖信息,推导本轮改动可能影响的测试范围。

因此,快速门禁(Fast Gate)的重点是找到与本轮代码变化相关的最小验证集合

Changed Files
      ↓
影响范围分析
      ↓
Lint / Type Check
      ↓
Affected Tests

映射越准确,Agent 获得反馈的等待时间就越短,也就越能减少反复运行完整测试集带来的计算成本。

第二层可以放在任务接近完成时,验证范围进一步扩大到模块测试、集成测试和验收测试。对于 Bug 修复型任务,还可以先通过测试复现问题,再修改实现;修复完成后,这条测试继续保留在测试集中,作为新的回归测试。

这样,一次 Bug 就能沉淀成后续任务持续生效的约束:

发现 Bug
   ↓
建立复现测试
   ↓
Agent 修复
   ↓
测试通过
   ↓
Regression Test 保留
   ↓
约束后续代码修改

Addy Osmani 在《Agent Harness Engineering》中提出过类似思路:Agent 的错误可以成为新的 Harness 规则。例如发现 Agent 会提交被注释掉的测试,就可以增加规则、Hook 或 Reviewer 检查,让同类错误在后续任务中更早暴露。

第三层位于 Agent Session 外部。Agent 创建 PR 后,由 CI 在独立环境里运行完整的回归测试、E2E、安全检查和其他必需检查。GitHub 的受保护分支可以要求指定状态检查满足条件后才允许合并,这就形成了一道独立于 Agent 判断的最终门禁。

最终可以形成三个不同速度的验证层:

代码修改
   ↓
Fast Gate
Lint / Type Check / Affected Tests
   ↓
任务完成
   ↓
Task Gate
Integration / Acceptance / Regression
   ↓
创建 PR
   ↓
CI Gate
Full Regression / E2E / Security
   ↓
Merge

越靠近 Agent Loop 内部,门禁越强调反馈速度,检查范围也相对更小;越接近最终交付,验证范围越完整,也可以接受更高的执行成本。这样才能兼顾验证强度和 Agent 的执行效率。

失败结果的反馈回路

测试进入 Agent Loop 后,并不代表测试跑起来就够了。对于 Agent 而言,测试结果本身也是上下文输入

一个大型测试套件可能产生几百甚至几千行日志,真正会影响模型判断的,可能只有少量的失败用例、断言信息、相关文件位置和 stdout。把这些原始日志完整地塞进上下文,不仅会消耗大量上下文预算,也容易让关键错误被无关信息淹没。

因此,Harness 可以先解析测试输出,再将整理后的结构化结果交给 Agent:

{
  "gate": "unit-test",
  "status": "failed",
  "failed_tests": [
    "tests/test_auth.py::test_empty_password"
  ],
  "error": "Expected 401, got 200",
  "retry_count": 1
}

Agent 可以先根据失败用例定位相关的实现,完成修改后优先重跑失败用例;确认这些用例通过后,再运行受本次改动影响的测试集。只有任务门禁也通过了,工作流才会进入下一阶段。

整个修复链路可以整理为:

Test FAIL
   ↓
失败结果解析
   ↓
定位相关代码
   ↓
最小范围修复
   ↓
重跑失败测试
   ↓
Affected Tests

另一个需要控制的问题,是重复修复

如果相同的断言错误连续出现,或者 Agent 修改了多轮代码,失败用例的数量始终没有减少,继续执行“失败 → 再修改”的流程,容易陷入无效的循环。

Harness 可以为 Repair Loop 设置资源预算,例如记录:

  • 同类错误的重试次数;

  • 测试累计运行时间;

  • 当前任务累计 Token 消耗;

  • 连续几轮修改后,失败用例是否减少。

例如,同一个 error_signature连续出现三次,就可以暂停当前修复路径;如果多轮修改后,失败用例仍没有减少,也可以退出当前 Repair Loop,转交人工处理或新的调试 Agent

此外,还要考虑不稳定测试。同一份代码在多次运行中可能得到不同的结果,如果 Harness 把每次失败都视为业务代码问题,Agent 可能会围绕着环境波动或测试本身的不稳定性反复修改正确的代码。因此,测试执行层还需要区分代码错误、测试基础设施错误和环境异常;必要时,可以针对同一个提交版本重新运行失败用例,再决定是否进入 Repair Loop。

当测试结果开始参与控制 Agent 的下一步动作时,这些反馈本身是否可靠,也成了 Harness 需要管理的问题

测试本身的可信度

还有一个更棘手的问题,就是 Agent 可能同时拥有业务代码和测试代码的写权限。

当现有测试与当前实现发生冲突时,Agent 既可以修改业务代码,也可以调整测试。如果 Harness 最终只检查退出码,那么修复实现和放宽测试条件,最后都可能得到相同的 PASS 结果。

《Harness Engineering》中就举过类似例子:Agent 提交 PR 时把测试注释掉。针对这类情况,可以把 .skip(xit( 等模式加入 Hook 或 Reviewer 的检查规则,让修改测试约束的行为本身触发阻断。

一种处理方式,是为测试建立明确的权限边界。Agent 可以为新功能补充测试,但关键的验收测试、回归测试,或是隐藏测试都由独立的 Harness 和 CI 管理。如果 Agent 修改已有测试文件,则触发额外检查,判断是否删除断言、增加 Skip、扩大 Mock 范围,或者放宽原有测试条件。

变异测试也可以用来检查测试本身是否足够有效。它会按照预设的变异规则对程序做小幅度的修改,例如改变条件边界、替换运算符,再重新运行原有测试。如果这些会改变程序行为的修改仍然无法让测试失败,就说明当前测试对这类错误缺少足够的约束力。变异测试与单元测试、性质测试、验收测试一起列为 Quality Gate 的重要组成部分。

因此,PASS 本身还可以继续被追问:

代码通过测试
      ↓
测试有没有真正约束行为?
      ↓
Mutation / Coverage / Test Diff
      ↓
可信 PASS

这也解释了为什么“每修一个 Bug,就补一条回归测试”很重要。它能把一次人工发现的问题沉淀为可重复执行的约束,后续 Agent 再修改相关模块时,这条测试仍会继续发挥作用。

质量门禁也会在这个过程中不断积累:Agent 出现过的问题、线上暴露的 Bug、Review 中发现的风险,都可以逐步转化为新的测试、静态规则或 Harness 检查。

Hooks 与停止条件

这些机制落到 Coding Agent 中,并不需要重新实现一套完整的运行时。现有 Coding Agent 提供的 Hooks,就可以承担其中一部分门禁工作。

以 Claude Code 为例,PostToolUse 就可以在完成工具调用后触发 Hook。官方文档中就有一个例子:在 Edit|Write 修改文件后,自动执行代码格式化,并通过 Matcher 将 Hook 限定在特定工具调用上。

沿用这套机制,也可以在文件发生变化后触发快速门禁:

Write / Edit
     ↓
PostToolUse
     ↓
检测 changed files
     ↓
Lint / Type Check / Tests
     ↓
PASS → 继续
FAIL → 返回错误信息

不过,这里还要考虑一种情况:Claude Code 也可以通过 Bash 或 PowerShell 修改文件,因此只监听 Edit|Write 无法覆盖所有文件变化。官方文档建议,如果检查需要覆盖整个工作区,可以在 Stop 阶段统一扫描改动,或者额外监听命令行工具,再通过 git status 等方式获取实际变更的文件。

因此,Stop 也很适合作为质量门禁的另一个检查节点。当 Claude 准备结束当前任务时,Stop Hook 可以再次检查工作区状态、测试结果和任务完成条件;如果仍有条件未满足,就返回 decision: "block",阻止任务结束,并让 Claude 根据反馈继续处理任务。

这样,一次完整的任务流程就可以变成:

Agent 修改代码
      ↓
PostToolUse
      ↓
Fast Gate
      ↓
Agent 继续任务
      ↓
Agent 请求结束
      ↓
Stop Gate
      ↓
Required Tests 是否满足
   ↙              ↘
 FAIL             PASS
  ↓                 ↓
返回 Loop          创建 PR

这两个 Hook 分别负责不同阶段的检查:PostToolUse 在文件修改后及时返回反馈,尽早发现问题;Stop 则负责在任务结束前做最后一轮确认,避免 Agent 在验证尚未完成时提前结束任务。

图 2:Claude Code Hooks 官方文档的 Hook 生命周期图

Session 外的最终门禁

Loop 内的测试负责帮助 Agent 根据反馈不断修复代码,但 Agent 在当前环境中跑出的 PASS,不能作为最终交付的唯一依据。

Agent 的工作环境里可能存在未提交文件、特殊依赖、缓存等本地状态,测试文件本身也可能在当前 Session 中被修改。因此,当任务离开 Agent 工作区后,还需要在独立环境中重新执行验证。

GitHub 的必需状态检查就可以承担这一层职责。受保护分支可以要求指定检查全部通过后才允许合并,也可以限定检查结果必须来自指定的 GitHub App,降低结果被其他来源替代的风险。

这样一来,测试在整个流程里就承担了两种不同的角色:

Agent Loop 内
测试 = Feedback
失败后继续修复

Agent Loop 外
测试 = Gate
失败后禁止合并

前者强调快速反馈,帮助 Agent 尽快发现问题并完成修复;后者强调独立验证,决定代码是否具备进入主干的条件。两者配合,才能形成完整的质量门禁。

不过,测试本身也覆盖不了所有软件质量问题。Addy Osmani 在《Agentic Code Quality》中把可维护性、性能、安全、效率和可理解性都纳入质量范围。因此,类型检查、代码规范检查、复杂度规则、依赖检查和安全扫描,也可以按照类似方式接入门禁。

至于需求意图、架构取舍、代码是否易于理解这类更依赖判断的问题,仍然需要工程师参与。自动化门禁负责确定性检查后,人的注意力就可以更多留给这些难以用通过或失败完整表达的问题。意图、品味和架构这些部分,更适合保留人工判断环节。

从测试命令到质量门禁

测试一直存在于软件开发流程中。Coding Agent 带来的变化,是测试开始更深入地进入 Agent 的控制流程,从任务完成后的验证手段,逐步变成影响 Agent 下一步动作、任务是否结束以及代码能否交付的一部分。

当这些验证条件被写进 Harness,质量门禁也就有了更明确的工程意义:系统可以持续给 Agent 提供反馈,在发现问题时及时把任务送回修复流程,并把一次次失败沉淀成后续任务继续生效的约束。

对于 Coding Agent 来说,真正重要的并不只是生成代码的速度,还包括它能否在持续验证中稳定地完成任务。

相关文章
人工智能 缓存 前端开发
6361 22
人工智能 JavaScript 开发工具
3368 6
缓存 JavaScript Shell
1585 2
开发工具 Swift git
1228 1
Shell API 调度
900 2
|
14天前
|
存储 弹性计算 缓存
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
本文更新了2026年阿里云全系列云服务器租赁活动报价,所有特惠资源均可前往阿里云活动中心选购,整体覆盖从个人入门到企业级高性能场景的全梯度需求。其中轻量应用服务器主打极致性价比,2核2G峰值200M带宽配置每日10点、15点限时抢购价仅38元/年,2核4G配置379元/年起;高性价比的经济型e实例、通用算力型u2i实例覆盖2核4G至4核32G全档位,适配开发测试与中小型企业业务;搭载英特尔至强6处理器的第九代c9i企业级实例算力较上代提升20%,支撑高并发生产环境,不同实例规格价差清晰,用户可根据自身业务负载与预算灵活选型。
2143 121
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
|
15天前
|
人工智能 程序员 API
Codex 接入 DeepSeek-V4-Flash:还能补上识图,提供两套方案
Codex 接入 DeepSeek-V4-Flash 怎么配?本文覆盖 CLI 与桌面端,再用 qwen3-vl-flash 补识图,两套方案可直接照做
1817 13
安全 机器人 API
660 2
缓存 人工智能 算法
743 1

热门文章

最新文章