ChatGPT、Codex实战:让Agent修一个Bug,为什么最后改动范围会越来越大?
最近用Codex处理真实项目时,有一种情况特别常见。
一开始你只是想修一个小Bug。
比如:
某个接口偶尔返回空值。
某个按钮点击以后状态没有更新。
某个权限判断在特殊情况下失效。
看起来都不是什么大问题。
于是你把任务交给Agent:
“帮我定位这个Bug并修复。”
刚开始一切都很正常。
它先读取相关文件。
然后找到一个可疑函数。
接着修改调用逻辑。
但跑测试以后发现还有失败。
于是它继续往下查。
最后你再回头看Diff时,可能会发现:
原本只准备改2个文件。
现在已经变成了8个文件。
不仅改了业务代码。
还动了:
公共函数。
类型定义。
测试。
配置。
甚至顺手重构了一小块旧逻辑。
这时候很多人的第一反应会是:
Codex是不是改太多了?
但更值得问的问题其实是:
为什么一个小Bug,会让Agent的修改范围一路扩大?
真正的原因很多时候不是AI“失控”。
而是它在试图完成一个完整的任务闭环时,不断发现:
这个Bug背后连接的东西,比最开始看到的更多。
一、最典型的场景:Bug很小,但根因不一定小
假设有一个很简单的问题:
用户更新昵称以后,页面偶尔还是显示旧昵称。
第一眼看,你可能会觉得:
改一下前端状态就行。
但Codex开始分析以后,可能发现:
前端显示的数据来自API。
API的数据来自Service。
Service读取数据库以后,还会经过缓存。
真正的问题是:
数据库更新了。
但缓存没有同步刷新。
于是它先修改缓存失效逻辑。
跑测试。
发现某个测试依赖旧缓存行为。
于是又改测试。
然后发现缓存工具函数在其他几个模块里也被调用。
为了避免重复问题,它可能继续修改公共逻辑。
最后一个“昵称显示不更新”的小Bug,变成了:
前端。
API。
Service。
Cache。
Test。
五六个位置同时修改。
这时候你会发现:
Bug表现很局部,不代表Bug根因也是局部的。
这是理解任务扩张的第一步。
二、为什么Agent特别容易把任务越做越大?
因为Agent和人修Bug时的行为方式不完全一样。
很多开发者修Bug时,会有一种很强的“最小修改”意识:
先把问题修掉。
不要动太多。
其他问题以后再说。
但Agent往往更倾向于建立完整闭环。
比如它发现:
A导致Bug。
但A的问题其实来自B。
B又依赖C。
如果只改A,测试可能还能通过,但根因没有完全消失。
于是Agent会继续往下追。
它的逻辑是:
既然这个问题是由更底层原因造成的,那就把底层原因一起解决。
从逻辑上说,这没有错。
但工程上会产生一个很重要的问题:
任务边界开始自然扩张。
最初任务是:
修Bug。
后来变成:
修根因。
再后来变成:
统一公共逻辑。
最后可能接近:
顺便重构。
这就是所谓的Scope Expansion。
三、Agent真正容易扩张的,不只是文件数量,而是“问题定义”
很多人看到Diff变大,只会盯着:
“怎么从2个文件变成8个文件了?”
但更深一层的问题其实是:
Agent已经悄悄改变了任务定义。
最开始的任务可能是:
修复用户昵称更新后偶尔不刷新的Bug。
后来可能变成:
修复缓存失效机制。
再后来变成:
统一项目里的缓存更新行为。
这三个任务明显不是一个级别。
但如果任务过程中没有明确的边界检查,Agent很容易一路向外扩张。
所以任务变大的真正过程往往是:
现象 → 根因 → 公共原因 → 系统性问题。
AI每往下多挖一层,修改范围就可能扩大一圈。
四、为什么人类开发者也会这么做,但Agent时代更明显?
因为过去人类有一个天然约束:
时间成本。
你本来只想修一个Bug。
结果发现背后牵涉一个老模块。
你很可能会想:
“这个先别动,风险太大。”
于是只做一个最小修复。
但Agent改变了这个成本结构。
过去重构一个公共模块可能要一两个小时。
现在Codex可能十几分钟就能生成修改。
于是一个很容易出现的心理变化是:
既然改起来不贵,那就顺便处理掉。
这时候AI降低了修改成本,却没有降低系统风险。
于是任务扩张会比过去更容易发生。
五、更深一层:代码修改成本下降以后,范围控制反而更重要
这是这个问题真正值得注意的地方。
以前开发者为什么会强调:
Keep the change small。
最小Diff。
一次只解决一个问题。
不是因为大改一定不好。
而是因为修改范围越大,验证复杂度越高。
一个Bug修复如果只改:
一个函数。
一个测试。
验证相对容易。
但如果一次改:
Service。
Controller。
Schema。
Cache。
Tests。
Config。
你需要确认的就不只是:
Bug有没有修掉。
还要确认:
旧行为有没有变化。
公共函数有没有影响其他模块。
接口契约有没有改变。
缓存语义有没有变化。
测试是不是仍然可信。
也就是说:
Agent把修改速度提高以后,验证成本并不会同步消失。
如果任务扩张得太快,很容易出现:
AI十分钟生成Diff。
开发者花四十分钟确认。
最后发现还是拆小了更省时间。
六、为什么这个问题在复杂项目里尤其明显?
因为真实项目存在大量隐藏依赖。
例如一个函数:
getUser()
看起来只服务于当前页面。
但实际上可能同时被:
登录流程。
管理后台。
通知系统。
权限模块。
定时任务。
共同调用。
Agent一旦修改这个公共函数,就会自然发现越来越多关联点。
项目越大:
公共模块越多。
共享状态越多。
历史兼容越多。
隐含规则越多。
于是一个局部Bug越容易一路追到系统边界。
这也是为什么:
同样一个Bug,在小Demo里可能只改20行,在真实项目里可能牵出200行Diff。
七、还有一种情况:Agent为了让测试通过,会继续扩张修改
这个特别容易被忽略。
假设Codex已经修复了目标Bug。
但运行测试以后发现:
还有两个旧测试失败。
这时候Agent会继续判断:
是不是自己的修改导致了测试失败?
如果是,它会继续修改。
然后又触发新的类型错误。
于是再改类型。
接着Build又失败。
再改配置。
从Agent视角看,这一系列动作都非常合理:
“我要把任务做到测试通过。”
但从开发者视角看:
原本只想修一个Bug。
最后已经变成整个小模块的兼容调整。
所以一个Agent任务能否及时停下来,非常重要。
否则:
任务闭环越完整,修改边界反而越容易扩大。
八、任务扩张一定是坏事吗?
不一定。
有些Bug确实暴露了更底层的问题。
如果只做表面修复,过一段时间还会再出问题。
这种情况下,扩大范围反而是正确的。
真正需要区分的是两种情况。
第一种:必要扩张
不扩大就无法真正修复。
比如:
Bug根因就是公共逻辑错误。
那就必须改公共模块。
第二种:机会性扩张
Bug已经修好。
但Agent发现:
这里代码可以重构。
那里测试可以顺便整理。
另一个函数也能顺手优化。
这种就不一定应该跟当前任务一起做。
问题不在于:
“改得多就是错。”
而是:
这些额外修改到底是不是当前任务完成所必需的。
九、怎么判断Agent是不是已经改过头了?
我会先问三个问题。
1. 如果不做这部分修改,原始Bug能不能修好?
如果答案是:
能。
那这部分很可能属于额外扩张。
2. 这些修改是不是引入了新的验证范围?
例如本来只是修接口。
现在却动了数据库Schema。
那验证成本已经明显提高。
3. 这些额外修改是否可以拆成单独任务?
如果可以单独做,而且不影响当前Bug修复,那通常更适合拆出去。
这样可以让:
Bug Fix保持Bug Fix。
Refactor保持Refactor。
不要把多个目标塞进同一个Diff。
十、怎么控制Agent的任务范围?
最有效的方法不是最后发现Diff太大再删。
而是在任务开始时就写边界。
比如不要只写:
修复这个Bug。
可以写成:
修复这个Bug,优先采用最小改动方案。
不要主动重构无关模块。
如果发现必须修改公共逻辑,请先说明原因和影响范围,再继续。
这种约束非常重要。
因为它告诉Agent:
完成任务的目标,不是“把周边都变得更好”。
而是:
在可控范围内解决当前问题。
十一、第二个办法:先让Agent给修复计划,再真正修改
如果Bug稍微复杂一点,我更建议先让Codex输出:
问题根因。
预计修改文件。
每个文件为什么要改。
哪些属于必须修改。
哪些属于可选优化。
然后再开始执行。
这一步非常有价值。
因为你在真正产生Diff之前,就能看到:
任务是不是已经开始膨胀。
如果最初预计:
改2个文件。
Agent计划却列出12个文件。
这时候就应该先问:
为什么?
而不是等它全部改完以后再判断。
十二、第三个办法:设置“扩张检查点”
例如可以规定:
如果任务需要:
修改超过5个文件。
跨越3个以上模块。
调整公共接口。
修改数据库Schema。
新增依赖。
那就先暂停,让人确认。
这其实类似给Agent设置一个风险阈值。
小范围任务:
自动继续。
一旦突破边界:
人工介入。
这样能够明显降低那种:
“小Bug一路变成大重构”
的情况。
十三、第四个办法:把“修Bug”和“顺手优化”拆开
如果Agent发现额外问题,可以让它记录下来。
比如输出:
当前Bug已经修复。
另外发现:
缓存工具函数可重构。
某个旧测试结构不合理。
类型定义存在重复。
但不要在当前任务里全部修改。
把这些变成新的Todo。
这样你既不会丢掉AI发现的问题,又可以保持当前Diff可控。
十四、给自己测一个指标:任务扩张率
这篇我建议只看一个指标:
Scope Expansion Rate——任务扩张率
计算方式可以很简单:
实际修改范围 ÷ 最初预计修改范围
例如一个任务开始时你预计:
修改3个文件。
最后Agent修改9个文件。
那么:
任务扩张率 = 9 ÷ 3 = 300%。
这个数字不要求绝对精确。
重点是看自己的任务是不是经常失控扩张。
低于150%
说明大多数任务都能维持在原定范围附近。
AI修改边界比较稳定。
150%—250%
说明Agent经常会发现额外依赖。
需要开始关注:
任务定义。
影响面。
边界约束。
经常超过250%
如果本来一个小任务,最后经常扩大两三倍甚至更多,就说明Workflow里可能缺少:
范围约束。
暂停条件。
任务拆分。
人工检查点。
这种情况下真正的问题不是AI能力不够。
而是:
AI太容易把一个问题扩大成一个更大的工程任务。
十五、Plus和Pro怎么判断?
如果你的任务扩张率还很高:
一个Bug经常从2个文件变成8个。
小任务频繁变成长任务。
很多时间都花在:
Review大Diff。
处理额外修改。
重新验证。
那当前真正应该优化的不是AI容量。
而是任务边界。
这种阶段Plus通常已经足够。
更值得做的是:
缩小Scope。
增加检查点。
拆分任务。
明确哪些修改必须先确认。
否则增加更多AI容量,很可能只是让Agent更快地产生更大的Diff。
如果你的任务扩张率已经比较低:
大多数任务范围稳定。
Agent知道什么时候该停。
项目约束也比较清楚。
但你开始长期遇到:
大量复杂Bug。
多个长任务连续执行。
大型代码库读取频繁。
任务数量明显超过当前AI容量。
这时候才真正进入:
容量问题。
这种情况下,Pro的更高使用强度才更容易转化成实际开发效率。
所以判断顺序应该是:
先把任务范围控制住,再考虑扩大AI产能。
最后
以后用ChatGPT、Codex处理真实项目,有一个习惯可能越来越重要:
不要只看Agent有没有把Bug修掉。
还要看:
它为了修这个Bug,究竟把任务扩大到了什么程度。
AI越来越强以后,真正危险的并不一定是:
它不会修改。
反而可能是:
它太有能力继续往下修改。
一个Bug背后发现一个根因。
一个根因背后又发现一个公共问题。
然后一路扩大。
如果没有明确边界,最后你得到的可能不是一个Bug Fix。
而是一个小型重构项目。
所以当下一次Codex告诉你:
“任务已完成。”
但你发现Diff已经从最开始的几十行变成几百行时,不妨再问一句:
这些修改里,哪些是真正为了修这个Bug必须做的?
这个问题,往往比单纯问“测试通过了吗”更重要。
持续分享 Codex、大模型开发与 AI 编程实战内容。
长期深度使用各类代码大模型,也整理了稳定的Plus/Pro会员订阅渠道,有需要可自取。
更多推荐




所有评论(0)