AI 改动了你不想动的关键部分:4 个原因 + 对症修复

让 AI 改一个参数名,它顺手把重试循环换成 map、timeout 从 30s 调成 10s——你的 prompt 一句都没提。本文讲清楚为什么 agent 爱过度编辑,以及怎么把它约束成手术级 diff。

你让模型把某个函数的一个参数改名。它改了。顺便”清理”了下面两个函数的重试循环,把 forEach 换成 map,还悄悄把默认 timeout 从 30s 调成 10s。你 prompt 里一句都没提。模型的总结里也只有一句”已按要求改名”。更糟:测试全过,因为那条重试路径只在生产里被触发。你会在凌晨 3 点的故障里发现这个改动,不是在 code review。AI agent 喜欢过度编辑,是因为大多数聊天式指令被它解读为”顺手把代码改得更好”——这是它从训练集里学到的”高级工程师”行为。

本文讲怎么把 AI 编辑约束到一个可审计的手术级 diff。

常见原因

1. 动词开放

“重构”、“改进”、“清理”、“现代化”——这些都允许模型动任何看起来不够好的代码,而按训练集标准,“不够好”几乎是所有遗留代码。

如何判断:你的动词没有宾语。光说”重构”=能改的都改。

2. 整文件粘进去,没标编辑区

模型收到整文件 + “改一处”的指令,重生成整个文件是阻力最小路径。重生成就漂。

如何判断:你贴了 400 行让它改一个名字,收回 400 行。

3. Agent 有大范围写入权限

Cursor Composer、Claude Code 自动 edit、Codex 宽 scope——模型能动任何文件。没显式收缩 scope 时,“scope”就等于”它能读到的全部”。

如何判断:改动出现在你从未提过的文件里。

4. 没要求 diff 格式

输出格式是”新文件”时模型得产出每一行,每一行都有机会漂。输出格式是”unified diff”时它被迫只产改动 hunk。

如何判断:你拿回的是整个新文件,不是 diff。

5. 模型总结说”改名”,实际做了更多

模型有时会在总结里少报改动。总结是自述,审计不能信。要审 diff 本身。

如何判断:模型说改了 1 处,diff 里有 5 处。

动手前先确认

  • 重新 prompt 之前先 git commit 当前状态(这样能干净地 git diff)。
  • 想清楚哪几行/哪些函数该改、哪些必须不动。
  • 保存过度编辑的版本,方便和手术级版本对照。
  • agent 运行时收缩 scope(Cursor 里只打开目标文件,Claude Code 限定工作目录)。
  • 决定:你要 diff、Edit 工具调用(精确旧字符串匹配)、还是带约束的完整新文件?

需要收集的信息

  • 你贴进去的文件 + 用的 prompt。
  • 模型声称的改动总结。
  • 实际 diff(git diff)。
  • 当时生效的模型、agent、工具权限。
  • 一份”被改但不该改”的行号清单。

最短修复路径

Step 1:开放动词换成手术级操作

差:  "重构这个模块让它更可读。"
好:  "在函数 `handlePayment` 里,把参数 `amt` 改名为 `amountCents`。
       只更新 `handlePayment` 内部的所有引用。
       不要动任何其他函数。不要碰 import。
       未改动的行不要重新格式化。"

Step 2:声明”禁动”清单

约束:
- 除 `handlePayment` 外不许动任何函数。
- 不许动 imports、exports、注释。
- 不许动常量 `MAX_RETRIES` 和 `TIMEOUT_MS`。
- 未改动的行格式保持。
- 如果你想"顺便清理"什么,写在另一节"建议跟进"里,不要直接动。

Step 3:要 unified diff,不要整文件

按此格式返回:

DIFF:
\`\`\`diff
--- path/to/file.ts
+++ path/to/file.ts
@@ ... @@
- 旧行
+ 新行
\`\`\`

建议跟进:
- <你注意到但没动的事>

diff 强制最小改动,因为重生成未动代码是浪费。

Step 4:能用精确匹配的 Edit 工具就用

Cursor / Claude Code / Codex 里优先用要求 old_string 精确匹配的 Edit 工具。模型对未标注区域物理上改不了——匹配会失败。这是最强的机械约束。

Step 5:审 diff,不要审总结

绝对不要把模型的散文总结当真。永远要:

git diff HEAD

每个 hunk 都翻一遍。看到意外的 hunk 就用 git checkout -p 或 IDE 的逐 hunk 回退。

Step 6:多区改动拆成多个 prompt

要改 3 个函数就发 3 个 prompt。合并 prompt 会模糊 scope,模型会跨区平均约束。

怎么确认已经修好

  • git diff 只在你点名的文件和函数里有改动。
  • import、export、常量都没移位。
  • 现有测试不需要改就能过。
  • 如果模型发现了其他问题,“建议跟进”列表非空——说明它尊重了 scope。
  • 用同一 prompt 第二次 AI 跑,diff 形态稳定。

如果还是没修好

  1. 收缩工具权限:不让动的目录设只读。
  2. 切到只用 Edit 工具的模式(不允许整文件重写)。
  3. 试更小/更便宜的模型——常常比旗舰模型更严守约束。
  4. 高风险代码绝不让 agent 直接 commit——必须 review diff 再合。

哪些情况不是你操作错了

你 prompt 里如果真写了”重构”、“润色”、“现代化”,模型只是听话。修法是换动词,不是骂模型。

预防建议

  • 默认动词:editrenamereplace——refactorimprovepolish 不加 scope 永不用。
  • 超 20 行的代码改动一律要 diff 输出。
  • 用注释把编辑区圈起来:// AI-EDIT-START / // AI-EDIT-END,告诉模型只改内部。
  • 手术级编辑用 Edit 工具的精确匹配模式。
  • 每次 AI 跑之前先 commit,方便 git diffgit restore
  • 每次都审 diff。不要信模型的自述总结。

相关阅读

标签: #排查 #Prompt #Prompt 质量 #误改重写