AI 编程智能体已经很擅长回答一个问题:

下一步该做什么?

它们能查看代码、提出猜测、修改实现、运行测试、分析失败原因、修复代码,然后继续推进。

这很了不起。

但也带来了一个新的工程问题:

它们应该在什么时候停止验证?

这个问题听起来很简单,直到你让智能体在一个真实仓库里连续工作一小时。

一个小改动可能这样开始:

修复重试条件。

→ 运行相关单元测试
→ 运行模块测试
→ 运行全部测试
→ 检查 lint
→ 检查类型
→ 再补一个边界测试
→ 查看一条无关警告
→ 修复这条警告
→ 把所有检查再跑一遍

单看每一步,都能找到理由。

问题出在这一连串操作上。

整个循环中,没有任何一步会说:

对于这个任务,现有检查已经足够了。

于是,智能体不断用更多工作来换取更多把握。

这就是我认为 AI 编程智能体需要验证预算的原因。

验证是一场没有明确终点的搜索

软件验证没有天然的终点。

单元测试通过后,可以运行整个包的测试。

包的测试通过后,可以检查整个仓库。

然后是集成测试。

再换一个平台。

再换一个运行时版本。

然后查看覆盖率。

再为刚想到的边界情况补一个测试。

几乎总有一件事是你还可以继续验证的。

开发者通常靠判断来决定做到哪里。

我们会在心里问:

  • 这次改动的风险有多大?
  • 它可能影响哪些地方?
  • 哪些检查能帮助确认改动是否正确?
  • 这次需要有多大把握?
  • 再花一小时测试,真的值得吗?

编程智能体并不会自然形成同样的停止判断。

它们通常因为做得周全而受到肯定。

所以,当指令只有一句:

确保这次改动能正常工作。

“确保”就可能变成一次没有边界的搜索。

验证做得更多,不一定意味着工程做得更好

这个区别很关键。

假设智能体修改了一个独立解析器中的判断条件。

合理的验证计划可能是:

1. 运行解析器的相关测试。
2. 运行相关类型检查。

两项都通过了。

这时候,把大型单体仓库里的集成测试全跑一遍,也许能再增加一点把握。

但它同样有成本:

  • 消耗更多 token;
  • 花费更多时间;
  • 需要分析更多日志;
  • 遇到更多无关失败;
  • 更容易扩大任务范围;
  • 更容易让智能体去“修复”原本没让它碰的东西。

验证本身就可能导致任务范围不断扩大。

这是自主编程中一个有些反常的现象:

智能体可能在确认实现正确的过程中,走出了原本允许修改的范围。

一个已有的失败出现了。

智能体开始排查。

它发现一个旧的配置问题。

于是顺手修复。

另一个测试又受到影响。

原本只改两行的任务,最终变成了仓库清理。

这期间,没有哪一步看起来明显不合理。

但整个过程仍然走偏了。

验证预算不只是命令次数上限

我所说的“验证预算”,并不是指:

你只能运行四条命令。

这太粗糙了。

有用的验证预算包含三个部分。

1. 事先安排好要做的检查

开始验证前,先确定这次改动需要通过哪些检查来确认。

例如:

必须执行:
- 相关单元测试
- 受影响包的类型检查

只有出现具体理由时才执行:
- 整个包的测试

关键在于,验证从具体计划开始,而不是从一句没有边界的“充分测试”开始。

2. 限制自动扩大的范围

智能体不应该仅仅因为还有检查可做,就扩大验证范围。

扩大范围需要理由。

合理的理由包括:

  • 当前检查发现了另一个受影响的依赖;
  • 实际修改涉及的范围比原计划更大;
  • 失败结果表明问题可能跨越了模块边界;
  • 后续代码修改让之前的检查结果不再适用。

不合理的理由是:

还有时间,再多跑点测试,更放心。

预算没用完,不是继续消耗预算的理由。

3. 明确停止条件

这是大多数智能体工作流程中缺少的一部分。

系统需要能够进入这样的状态:

计划中的检查已经完成。
没有发现新的风险。
停止验证。

如果没有明确的停止条件,“做得更周全”就永远占上风。

检查失败,不应自动换来更多预算

失败会让问题变得更复杂。

想象下面这个循环:

运行测试
↓
失败
↓
修改代码
↓
运行测试
↓
失败
↓
修改代码
↓
运行更多测试
↓
排查另一个模块
↓
修改更多代码

从技术上看,智能体一直在推进。

但几轮之后,它可能已经不再解决最初的任务。

更合适的规则是:

检查失败带来了新信息,并不代表智能体获得了无限修改的许可。

例如:

1. 运行计划中的检查。
2. 如果失败由当前改动引起,查看相关错误信息。
3. 做一次有针对性的修复。
4. 重新运行失败的检查。
5. 如果同一个失败重复出现,超过少量尝试次数后,停止并说明已查到的情况。

最后一步很关键。

停止并不等于失败。

有时候,停下来才是正确的工程选择。

智能体已经获得了一条新信息:

当前计划不足以解决问题。

这时需要重新做决定,而不是陷入无限循环。

验证结果应对应当前代码

还有一个不太显眼的问题。

假设智能体完成了这些检查:

单元测试 ✓
类型检查 ✓

之后又修改了一个共享依赖。

它还能说两项检查都通过了吗?

不一定。

验证结果对应的是仓库在某个时刻的状态。

代码变了,部分结果就可能不再适用。

因此,合理的智能体工作流程应该大致这样处理验证结果:

仓库状态 R1
    ↓
测试 A 通过
测试 B 通过

代码修改 → R2

测试 A 的结果可能仍然有效
测试 B 的结果可能已经失效

目的不是每改一次代码,就把所有检查重跑一遍。

而是要知道哪些检查结果仍适用于当前代码。

这样使用验证预算,才更有价值。

真正的问题,不只是智能体测试得太多

“智能体运行了太多测试”只是表面现象。

更深层的问题是,大多数编程智能体围绕这个问题进行优化:

下一步该做什么?

但自主完成工程任务,还需要回答另一组问题:

我被允许做到什么程度?

检查到什么程度才算足够?

什么理由可以支持继续扩大工作?

什么时候应该停下来,请人做决定?

这些是执行控制的问题,不能只靠推理能力来回答。

模型可能完全有能力选出技术上合理的下一步。

但这不代表,这一步对整个任务仍然合理。

编程智能体越强,这个区别就越重要。

能力不足的智能体会因为解决不了问题而失败。

能力很强的智能体可能以另一种方式失败:

它总能找到更多看起来合理的事情去做。

一套可执行的规则

我目前更倾向于采用这样的验证规则:

实现之前

明确:
- 验收条件
- 预计修改的文件
- 初始验证计划
- 验证额度

然后:

实现之后

只执行计划中的检查。

如果检查失败:

失败是否由当前任务引起?

是
→ 查看相关错误和检查结果
→ 做有针对性的修复
→ 重新运行受影响的检查

否 / 原本就存在
→ 记录下来
→ 不自动扩大任务范围

如果智能体想再加一项检查:

这项检查具体要确认哪个尚不确定的问题?

没有具体答案,就不要运行。

如果验证反复失败:

停止。
保存检查结果和错误信息。
请求决定,或调整计划。

这套规则刻意保持平淡。

这正是目的。

好的工程控制,往往就是这样朴素。

预算限制的是自动执行,不是质量

验证预算绝不应该意味着:

四条命令已经用完,即使代码明显有问题,也到此为止。

预算控制的是自动开展的工作,不是降低正确性的要求。

当原定预算不够时,正确的处理过程是:

继续自动执行
        ↓
       停止
        ↓
说明为什么需要增加验证
        ↓
批准 / 调整计划 / 进一步调查

关键是让扩大范围这件事变得可见。

智能体不能悄悄把一个小任务变成大任务。

模型越强,为什么这件事越重要

人们很容易认为,推理能力更强的模型会自动解决这个问题。

我觉得结果可能恰好相反。

随着模型能力提高,它们会更擅长发现:

  • 另一个可能的边界情况;
  • 另一个架构问题;
  • 另一项值得运行的测试;
  • 附近另一处不一致;
  • 另一个“顺手做的小改进”。

每条建议单独来看,都可能是对的。

这会让明确边界变得更加重要。

编程智能体的未来,大概不会是:

给模型一套完美指令,让它永远不去考虑多余的工作。

而更接近于:

让模型自由推理,同时明确它可以采取哪些行动、做到什么程度。

推理可以灵活。

执行需要约束。

TaskBelay 在其中做什么

这个想法,是我开发 TaskBelay 的原因之一。

它并不试图告诉 Codex、Claude Code、DeepSeek 或其他编程智能体,应该如何解决工程问题。

智能体仍然负责阅读代码、选择技术方案、编辑文件,以及判断下一步做什么。

TaskBelay 则把这些决定所处的任务范围和规则保存清楚:

  • 明确范围——允许修改什么;
  • 有限验证——计划做多少验证,以及什么理由可以扩大验证;
  • 持久状态——以哪些已保存的状态和检查结果为准;
  • 安全恢复——操作失败或结果不确定时,应该如何处理。

我反复思考的区别是:

编程智能体决定下一步做什么。
配套系统应该控制这一步可以扩展到什么程度。

对于持续时间较长的 AI 编程任务,验证不应该是无休止地追求更多把握。

它应该是一项有计划、有边界的工程工作。