一个很常见的对话:
你:给用户列表加一个导出 CSV 的按钮。
Agent:好的,已完成。
你打开 diff:新增导出服务、重写表格组件、调整路由、加了分页、把 user store 拆了、顺手升级了一个依赖。
你只想要一个按钮。
这不是段子,而是 AI 编码里非常典型的一种失控:任务本身不大,但实现过程不断 膨胀,最后变成了一次没人要求过的重构。
更麻烦的是,它通常不是“写错了”,而是“写多了”。测试可能还过,功能也能用,但 你已经很难判断这次改动到底该不该接受。
一、先分清:必要涟漪和顺手重构
实现一个功能,不可能只改一行。真正的问题不是“有没有多改”,而是“多改的部分是 不是必要”。
我会把改动分成三类:
| 类型 | 例子 | 处理 |
|---|---|---|
| 必要涟漪 | 新增 API、类型、权限、测试、调用方适配 | 允许,但要能解释 |
| 可疑扩张 | 新抽象、重命名、移动文件、换依赖、重构无关模块 | 需要单独提案 |
| 明确越界 | 改与本次任务无关的支付/权限核心、破坏公开 API、数据迁移 | 默认拒绝 |
“加导出按钮”确实可能需要:
- 一个导出接口;
- 一个按钮;
- 一个类型;
- 一组测试;
- 一点权限判断。
这些是必要涟漪。但“把 user store 拆了”“重写表格组件”“升级依赖”不是。它们 可能让代码更“合理”,但不属于这次任务。
AI 的问题在于:它往往分不清这两者。它会把“让功能成立”理解成“把系统改到我认为 合理”。
二、为什么 AI 会写着写着就变样?
1. 它没有“非目标”
你告诉了它要做什么,但没告诉它不要做什么。
“加导出”在它看来可以包含:重构数据层、统一表格、优化分页、清理旧组件。每一 项都能讲出理由,但都不是你要求的。
2. 它在阅读中发现了“顺手能修”的东西
Agent 为了实现功能,会读很多相关代码。读得越多,越容易看到“这里可以抽一下” “这个命名不对”“这个依赖可以升级”。
人类工程师也会这样,但人通常知道“这次先不动”。Agent 没有这个刹车。
3. 它在做局部最优
为了让导出逻辑复用,它可能把表格逻辑抽出来;为了抽表格,又改了 store;为了改 store,又动了路由。
每一步都是局部合理,合起来就是范围爆炸。
4. 它没有明确的停止条件
它不知道什么时候该停下来问:“这一步已经超出你的要求了,要继续吗?”
于是它默认继续,直到“功能完成”。
三、范围失控的真正代价
不是多写了几行代码,而是:
- review 面变大。 你本来只想看一个按钮,现在要看十几个文件。
- 回滚变难。 功能和行为变更混在一起,出问题不知道回滚哪一块。
- 意图丢失。 一周后没人说得清哪些是需求,哪些是“顺手”。
- 风险混入。 一个无关的依赖升级,可能把整个构建带到另一个版本。
- 测试通过也不安心。 测试覆盖的是行为,不一定覆盖结构变化。
最坏的情况不是代码烂,而是你无法判断它到底做了什么。
四、先把“不要做什么”写出来
一个有用的提示词,不是只写目标,而是写四件事:
- 目标:这次要交付什么;
- 必要涟漪:允许动哪些相关部分;
- 非目标:明确不要做什么;
- 停止条件:超出范围时怎么办。
比如:
目标:用户列表支持导出 CSV。
允许:
- 新增导出接口和按钮;
- 修改 users 和 export 相关代码;
- 增加类型和测试。
不允许:
- 重命名或移动现有模块;
- 修改路由;
- 升级依赖;
- 重构 user store;
- 改支付和权限核心。
如果实现需要超出以上范围,先停下来创建 Jin 提案,
不要直接继续写代码。
这段话不能保证 Agent 100% 遵守,但它把“范围”从隐含变成了显式。review 时你也 有了一把尺子:不是凭感觉判断“是不是改多了”,而是对照清单。
五、把结构决策提前
提示词仍然只是提示词。要让它变成可审阅的东西,需要把结构决策提前。
Jin 的流程是:
- Agent 先读当前架构;
- 创建一个提案,写清楚目标、范围、验收标准;
- 只编辑提案的目标 L0/L1;
jin_validate校验架构引用、关系和约束;- 校验通过后,人审批;
- 批准后应用架构,Agent 自动继续。
这样做的好处不是“Agent 不会多改”,而是:
- 它想新增模块,提案里会出现;
- 它想加依赖,差异里会出现;
- 它想移动实体,位置变化会被标出来;
- 它想改公开实体,审阅时能看到;
- 你觉得方向不对,可以拒绝并写备注,Agent 带着反馈修订。
范围失控从“藏在 diff 里”变成了“在结构层先暴露”。
六、一个更实际的 review 顺序
即使有了提案,也不要一上来扎进代码。
可以按这个顺序:
- 看目标:这次到底要解决什么问题?
- 看范围:改了哪些模块,有没有出现非目标里的东西?
- 看结构 diff:有没有新模块、新依赖、公开接口变化?
- 看变更文件:有没有碰到不该碰的目录?
- 看关键代码:实现是否合理?
- 跑测试和 CI:行为有没有坏?
如果第 2、3 步就已经超出范围,后面的代码 review 可以不用开始了——先让 Agent 修订提案。
七、它不是代码沙箱
这里要说清楚:Jin 不是代码沙箱。
它不能阻止 Agent 在代码层写一个范围外的函数,也不能保证它没有改某个不该改的
文件。提案里的 scope 和 acceptance 是审阅信息,不是强制权限。
它做的是把结构变化变成显式提案,把“多改了什么”变得可见。代码层的 review、测试 和 CI 仍然必要。
但至少,你不用再靠“打开 diff 看看感觉”来判断范围。你可以先看结构,再看代码。
AI 可以写得多,但不该写着写着,把项目写成另一个样子。
项目地址:
v2.0.32-jin.1 下载:
中文完整指南:
Jin 是 OpenCode 的社区分支,不是 OpenCode 官方产品。