你让模型修一个函数。它修了。还”顺便”重格式化了相邻两个函数、改了一个常量名、在文件顶部加了一段”应该说明模块用途”的注释。你 prompt 里一句没提,但也没明令禁止。模型扩大 scope,是因为它被训练成”留下比拿到时更好的东西”,而”更好”在你不画边界时是无界的。
最快的修法: 别去修输出,去修 prompt。把开放动词(“改进”)换成受限动词加明确对象(“只编辑函数 validate_email”),再加一份逐条列出的 out-of-scope 清单,并要求它输出 diff。对 agent 来说,边界必须是机械的、不能只是一句话:用 Cursor 的编辑范围、Claude Code 的 permissions.deny 规则,或 Codex 的 --sandbox read-only,把模型锁死在目标文件或目录上。本文同时讲 prompt 级和工具级两层边界。
先看你属于哪一类
| 症状 | 最可能的原因 | 跳到 |
|---|---|---|
| 你点名的那个文件里多出了别的改动 | 开放动词 + 没有 out-of-scope 清单 | Step 1-2 |
| 你贴进去的相邻函数 / 段落被重写 | 长上下文,没有边界 | Step 1-3 |
| 多步对话里 scope 一步比一步大 | 步与步之间边界丢了 | Step 5 |
| 你从没点名的文件被动了 | agent 有大范围写入权限 | Step 6 |
| 模型嘴上同意边界,转头还是破了 | 只有 prompt 级边界,没有强制 | ”如果还是没修好” |
常见原因
1. 开放动词授权扩展
“改进”、“清理”、“润色”、“重构”全都暗示”凡是能改的地方都改”。模型会照字面执行。
如何判断:你的动词是开放的。受限动词是”编辑函数 X”、“替换第 Y 行”、“重命名变量 Z”。
2. 没有显式的 out-of-scope 清单
你说了要做什么,但没说要留下什么不动。模型就把视野里的一切当成可下手的对象。
如何判断:你的 prompt 有 in-scope 集合,却没有 out-of-scope 集合。
3. 长上下文里有看起来可修的相邻问题
你把整个文件贴了进去。模型看到的不只是目标,还有另外五处看着可以顺手修的地方。没有边界,它就全修了。
如何判断:scope 扩展落在 prompt 里能看到的相邻代码或内容上。
4. 多步任务每步没有边界
第 1 步限定了函数 X。第 2 步说”现在改进这个模块”,边界就蒸发了。到第 3 步,模型已经把整个文件重写了。
如何判断:scope 漂移和步数相关(出现在多步工作流里)。
5. Agent 有大范围写入权限
在 Cursor 的 Agent mode、Claude Code,或 workspace 可写的 Codex 里,模型能动 scope 内的任何文件。“scope”在机械层面就变成了”它能读、能写的全部”。
如何判断:改动出现在你从未点名的文件里。
动手前先确认
- 标清楚什么该改(in-scope)。
- 标清楚什么不该改(out-of-scope)。
- 决定相邻问题是让它 flag 出来,还是直接忽略。
- 多步工作每一步都规划好边界。
- agent 运行前,先把工具权限收缩到目标文件或目录。
需要收集的信息
- 当前的 prompt。
- 越界的输出(代码用
git diff)。 - 不该发生的改动清单。
- in-scope 和 out-of-scope 两个集合。
- 当时生效的模型、agent 和工具权限。
最短修复路径
Step 1:开放动词换成受限动词
差: 改进 user.py 文件。
好: 只编辑 user.py 里的函数 `validate_email`。其他函数 byte 级
保持原样。未改动的行不要重新格式化。
受限动词加上明确对象,消掉了”改进”留下的那块歧义。
Step 2:声明 out-of-scope 清单
Out of scope(不许动):
- validate_email 以外的任何函数。
- import 和 export。
- MAX_RETRIES 常量。
- 现有注释。
- 文件级 docstring。
正常情况下你会"顺手清理"的内容,请写到 SUGGESTED_FOLLOWUPS
区块里,不要直接动。
SUGGESTED_FOLLOWUPS 模式让模型把它的观察浮出来却不动手——你发现越界编辑时,想要的其实通常就是这个。
Step 3:内容类任务用标记编辑区
对散文、markdown 或配置这类不方便用行级指令的场景,把可编辑区域围起来:
只编辑下方标记之间的文本。标记之外的内容原样保留。
# AI-EDIT-START
def validate_email(email: str) -> bool:
return "@" in email
# AI-EDIT-END
物理标记比一句话更经得起 scope 漂移——句子模型可以重新解读,标记不行。
Step 4:要 diff,不要整文件重写
按 unified diff 输出。只包含编辑区内改动的 hunk。不要输出整个文件。
diff 让越界改动在机械层面无所遁形,而且模型知道你会去读 diff 时,行为会更保守。用 agent 时这一条是白送的:Cursor 的 Agent 在应用前会把每一处改动都以可审阅的 diff 形式展示出来——去读它,别直接点 Accept All。
Step 5:多步工作,每一步都重申边界
Step 1:只编辑 validate_email。Out of scope:其他全部。
Step 2:第 1 步审过之后,只编辑 send_email。Out of scope:
validate_email(已冻结)和其他全部。
把”已冻结”清单一路往后传,让 scope 单调收紧而不是越积越多。一条只在长对话开头说过一次的边界活不下来,要重申。
Step 6:对 agent,在工具层收缩,而不是在 prompt 里
prompt 里的边界是一个请求;权限边界是工具强制执行的,不管模型自己想怎么做。截至 2026 年 6 月:
- Cursor。 改动是局部的时候,用 Edit mode(单文件、外科手术式),别用 Agent mode。要彻底把某个文件挡在 agent 触及范围之外,把它加进
.cursorignore(gitignore 语法)——但要注意这是 best-effort 的,目前会同时挡掉读和写,而且 agent 有时仍能通过 shell 命令摸到被忽略的文件,所以还是要审 diff。见 Cursor 的 agent 安全文档。 - Claude Code。
.claude/settings.json里的权限规则由 Claude Code 强制执行,不是由模型执行。用 gitignore 风格的路径规则拒掉目标之外的编辑,例如"deny": ["Edit(/src/legacy/**)"];或者把整个会话切到只读探索,把/permissions设成 plan mode。只有--add-dir加进来的目录可写,所以从目标子目录里启动本身就是一道边界。CLI 等价写法是--disallowedTools "Edit"。见 Claude Code 权限文档。 - Codex。 用
--sandbox read-only做只规划不写入,或--sandbox workspace-write把编辑限制在工作目录内;即便如此,.git、.codex、.agents仍保持只读。会话中途可用/permissions降级到只读。见 Codex 的 sandboxing 文档。
机械权限每次都胜过 prompt 级指令。
怎么确认已经修好
- diff 限定在 in-scope 集合内。
- out-of-scope 的文件、函数、段落是 byte 级一致——用
git diff --stat确认(只该出现你预期的那些文件),再对每个文件跑git diff。 SUGGESTED_FOLLOWUPS(如果有)列出了模型注意到但没动的相邻问题。- 重跑 prompt,diff 的形态相近。
- 同事审 diff 时找不出一处意外的改动。
如果还是没修好
- 模型嘴上同意了边界、转头又破了:别再靠散文。换成工具强制的边界(Step 6)——Claude Code 的
Edit(...)deny 规则和 Codex 的read-onlysandbox 由工具检查,不由模型。 - agent 的权限比你 prompt 暗示的更宽:去确认实际可写的范围(Cursor 是 Edit 还是 Agent、Claude Code 的工作目录、Codex 的 sandbox mode),而不是去确认 prompt。
- 重复性工作,把边界锁进 system prompt 或项目指令(
CLAUDE.md、.cursor/rules、AGENTS.md),不要写在 user message 里,这样它对每一轮都生效。 - 换一个模型。不同模型行为不一样,在某个任务上过于积极的模型,在另一个任务上可能反而守得住 scope。
常见问题
我明明没让它扩,模型为什么要扩 scope? 前沿模型被调成乐于助人、并且倾向于”留下比拿到时更好的代码”。没有明说边界时,“助人”就包含修你没让它修的东西。这个扩展是一项没加约束的功能在起作用,不是 bug——所以修法是把约束加上。
只说一句”别的都不要改”够吗? 有帮助,但单条否定指令在一个塞满”看着可修”代码的长上下文面前很弱。把它和一份具体的 out-of-scope 清单(Step 2)以及一个 diff 要求(Step 4)配在一起,让越界改动可见。对 agent,再用一条权限规则(Step 6)兜底。
我的 agent 改了我从没提过的文件,专门怎么治这个?
这是权限问题,不是 prompt 问题。在 Cursor 里切到 Edit mode,或把禁区路径加进 .cursorignore。在 Claude Code 里,往 .claude/settings.json 加一条 Edit(...) deny 规则,或从那个窄子目录里启动。在 Codex 里跑 --sandbox read-only 或 workspace-write。工具不让它写的东西,模型就碰不到。
怎么在多步对话里限定 scope,又不用每步都重贴一遍? 维护一份很短的”已冻结”清单,每步开头重申一次:“Frozen(不要动):validate_email、send_email。只编辑:parse_headers。“每步多花几个 token,就能挡住边界随着对话变长而被侵蚀。
更小的模型真的更守 scope 吗? 有时是,但这取决于任务,不是一条规律。别把换模型当成第一步——一份清晰的 out-of-scope 清单加上一个 diff 要求,在你现在用的那个模型上就能解决大多数情况。
预防建议
- 默认动词:“edit”、“rename”、“replace”——不加 scope 的”improve”、“refactor”、“polish”永不用。
- 永远声明 out-of-scope。如果什么都不设禁区,就明说。
- 内容类任务用标记编辑区;agent 用工具强制的边界。
- agent 工作流里,开工前就机械收缩工具 scope,而不是越界之后再补。
- 用
SUGGESTED_FOLLOWUPS模式:让模型报告观察而不动手。 - 每次都审 diff。不要信模型对自己改了什么的自述总结。