最近用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会员订阅渠道,有需要可自取。

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐