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 编程任务,验证不应该是无休止地追求更多把握。
它应该是一项有计划、有边界的工程工作。