范围失控不是模型不听话,而是任务没有边界

2 阅读6分钟

一个很常见的对话:

你:给用户列表加一个导出 CSV 的按钮。

Agent:好的,已完成。

你打开 diff:新增导出服务、重写表格组件、调整路由、加了分页、把 user store 拆了、顺手升级了一个依赖。

你只想要一个按钮。

这不是段子,而是 AI 编码里非常典型的一种失控:任务本身不大,但实现过程不断 膨胀,最后变成了一次没人要求过的重构。

更麻烦的是,它通常不是“写错了”,而是“写多了”。测试可能还过,功能也能用,但 你已经很难判断这次改动到底该不该接受。

一、先分清:必要涟漪和顺手重构

实现一个功能,不可能只改一行。真正的问题不是“有没有多改”,而是“多改的部分是 不是必要”。

我会把改动分成三类:

类型例子处理
必要涟漪新增 API、类型、权限、测试、调用方适配允许,但要能解释
可疑扩张新抽象、重命名、移动文件、换依赖、重构无关模块需要单独提案
明确越界改与本次任务无关的支付/权限核心、破坏公开 API、数据迁移默认拒绝

“加导出按钮”确实可能需要:

  • 一个导出接口;
  • 一个按钮;
  • 一个类型;
  • 一组测试;
  • 一点权限判断。

这些是必要涟漪。但“把 user store 拆了”“重写表格组件”“升级依赖”不是。它们 可能让代码更“合理”,但不属于这次任务。

AI 的问题在于:它往往分不清这两者。它会把“让功能成立”理解成“把系统改到我认为 合理”。

二、为什么 AI 会写着写着就变样?

1. 它没有“非目标”

你告诉了它要做什么,但没告诉它不要做什么。

“加导出”在它看来可以包含:重构数据层、统一表格、优化分页、清理旧组件。每一 项都能讲出理由,但都不是你要求的。

2. 它在阅读中发现了“顺手能修”的东西

Agent 为了实现功能,会读很多相关代码。读得越多,越容易看到“这里可以抽一下” “这个命名不对”“这个依赖可以升级”。

人类工程师也会这样,但人通常知道“这次先不动”。Agent 没有这个刹车。

3. 它在做局部最优

为了让导出逻辑复用,它可能把表格逻辑抽出来;为了抽表格,又改了 store;为了改 store,又动了路由。

每一步都是局部合理,合起来就是范围爆炸。

4. 它没有明确的停止条件

它不知道什么时候该停下来问:“这一步已经超出你的要求了,要继续吗?”

于是它默认继续,直到“功能完成”。

三、范围失控的真正代价

不是多写了几行代码,而是:

  • review 面变大。 你本来只想看一个按钮,现在要看十几个文件。
  • 回滚变难。 功能和行为变更混在一起,出问题不知道回滚哪一块。
  • 意图丢失。 一周后没人说得清哪些是需求,哪些是“顺手”。
  • 风险混入。 一个无关的依赖升级,可能把整个构建带到另一个版本。
  • 测试通过也不安心。 测试覆盖的是行为,不一定覆盖结构变化。

最坏的情况不是代码烂,而是你无法判断它到底做了什么。

四、先把“不要做什么”写出来

一个有用的提示词,不是只写目标,而是写四件事:

  1. 目标:这次要交付什么;
  2. 必要涟漪:允许动哪些相关部分;
  3. 非目标:明确不要做什么;
  4. 停止条件:超出范围时怎么办。

比如:

目标:用户列表支持导出 CSV。

允许:
- 新增导出接口和按钮;
- 修改 users 和 export 相关代码;
- 增加类型和测试。

不允许:
- 重命名或移动现有模块;
- 修改路由;
- 升级依赖;
- 重构 user store;
- 改支付和权限核心。

如果实现需要超出以上范围,先停下来创建 Jin 提案,
不要直接继续写代码。

这段话不能保证 Agent 100% 遵守,但它把“范围”从隐含变成了显式。review 时你也 有了一把尺子:不是凭感觉判断“是不是改多了”,而是对照清单。

五、把结构决策提前

提示词仍然只是提示词。要让它变成可审阅的东西,需要把结构决策提前。

Jin 的流程是:

  1. Agent 先读当前架构;
  2. 创建一个提案,写清楚目标、范围、验收标准;
  3. 只编辑提案的目标 L0/L1;
  4. jin_validate 校验架构引用、关系和约束;
  5. 校验通过后,人审批;
  6. 批准后应用架构,Agent 自动继续。

这样做的好处不是“Agent 不会多改”,而是:

  • 它想新增模块,提案里会出现;
  • 它想加依赖,差异里会出现;
  • 它想移动实体,位置变化会被标出来;
  • 它想改公开实体,审阅时能看到;
  • 你觉得方向不对,可以拒绝并写备注,Agent 带着反馈修订。

范围失控从“藏在 diff 里”变成了“在结构层先暴露”。

六、一个更实际的 review 顺序

即使有了提案,也不要一上来扎进代码。

可以按这个顺序:

  1. 看目标:这次到底要解决什么问题?
  2. 看范围:改了哪些模块,有没有出现非目标里的东西?
  3. 看结构 diff:有没有新模块、新依赖、公开接口变化?
  4. 看变更文件:有没有碰到不该碰的目录?
  5. 看关键代码:实现是否合理?
  6. 跑测试和 CI:行为有没有坏?

如果第 2、3 步就已经超出范围,后面的代码 review 可以不用开始了——先让 Agent 修订提案。

七、它不是代码沙箱

这里要说清楚:Jin 不是代码沙箱。

它不能阻止 Agent 在代码层写一个范围外的函数,也不能保证它没有改某个不该改的 文件。提案里的 scope 和 acceptance 是审阅信息,不是强制权限。

它做的是把结构变化变成显式提案,把“多改了什么”变得可见。代码层的 review、测试 和 CI 仍然必要。

但至少,你不用再靠“打开 diff 看看感觉”来判断范围。你可以先看结构,再看代码。

AI 可以写得多,但不该写着写着,把项目写成另一个样子。

项目地址:

github.com/ZYF93/openc…

v2.0.32-jin.1 下载:

github.com/ZYF93/openc…

中文完整指南:

github.com/ZYF93/openc…

Jin 是 OpenCode 的社区分支,不是 OpenCode 官方产品。