Claude Code 输出和 prompt 不符:修 plan,别修 diff

Diff 是漂亮代码,却解决了别的功能。用 Plan Mode(Shift+Tab)加 restate-before-execute 在 plan 阶段逮住偏差,而不是等代码落地后才发现。

你让 Claude Code「修 invoice 渲染里的时区 bug」。PR 漂亮地回来——测试全、文档全——却完全是 invoice 列表的缓存失效 bug。它读文件时确实发现了那个 bug,但不是你要的那个。原来的时区 bug 还在。

最快的修法:别让 Claude Code 一上来就动手改文件。按 Shift+Tab 直到状态栏显示 ⏸ plan mode on,再提问。在 Plan Mode 下(Claude Code v2.1.0 及以上),Claude 会先只读地看代码、写出一份计划,在你批准之前不能改任何文件。这样你在「计划」这个便宜的阶段就能看出误读,而不是在 diff 里才发现。喜欢用文字的话,输入 /plan;或者用 claude --permission-mode plan 启动,把它设成默认。

输出和 prompt 不符,几乎总发生在 plan 阶段,而不是代码生成阶段。Claude 读 prompt、读文件、读相关 context,然后 commit 了一个偏离你所写内容的心智任务。等代码落地时,plan 已经错了——而代码忠实地反映了那个错误的 plan。所以根治办法是结构性的:在任何改动之前,强制加一道「先重述、再批准」的关卡。

你属于哪一类?

你看到的症状最可能的原因跳到
你的 prompt 能读出两种合理意思prompt 歧义,Claude 挑了一种原因 1
第一条指令做了,后加的条件被忽略长 prompt 被压缩了原因 2
输出遵循了你没要求的某条惯例CLAUDE.md 默认值赢了原因 3
diff 在你从没点名的文件里从文件 context 推断 intent原因 4
修法小得可疑Claude 修了旁边更容易的问题原因 5
「修」完 bug 还能复现症状对、根因错原因 6

常见原因

按命中率从高到低排列。

1. Prompt 有歧义,Claude 挑了一种合理解读

「修时区 bug」可以是渲染 bug、存储 bug、解析 bug 或者比较 bug。Claude 撞上的第一个就动手了,而你想的是另一个。

如何判断:事后用新眼光看你的原 prompt。如果你能找出两种或更多合理解读,那就是歧义在作怪。

2. 长 prompt 被略读,关键句被略掉

prompt 超过约 200 字时,常被压缩成「主要诉求」——你后面加的次要约束、具体限定就丢了。Claude 按压缩后的版本干活。

如何判断:工作匹配你 prompt 里的第一个大指令,却忽略了后面加的条件(「但别影响 email 路径」)。

3. CLAUDE.md 注入了竞争性默认值

prompt 说「做 X」,CLAUDE.md 说「这块我们一直 Y」。Claude 调和成「按 Y 的风格做 X」——不是你要的。这种情况常见到 Claude Code 仓库里有专门的追踪 issue:Claude 读了 CLAUDE.md 里一条明确规则,嘴上说理解了,转头却做别的(anthropics/claude-code issue #15443)。

如何判断:输出匹配某条你忘了它还在的 CLAUDE.md 约定,尽管 prompt 写的不同。用 /memory 看看当前从各个 CLAUDE.md 加载进 context 的到底是什么。

4. Claude 从文件 context 推 intent,而不是从 prompt

你说「修 bug」,Claude 读了 invoice.ts,看到一处明显的 code smell,就判定那是 bug 并「修」了——真 bug 在另一个文件。同一个机制也会导致越界改动——你从没点名的文件被改了——这是反复出现的投诉(anthropics/claude-code issue #41707)。

如何判断:diff 在你没点名、也没预期会动的文件里。

5. Claude 解决了相关但更容易的问题

你要的是难的修法。Claude 看到一个相关但容易的问题,修了就当完事——真正的修法需要更深的查证,而模型没投入那个精力。

如何判断:一个你本来准备花数小时修的问题,改动却又小又干净——可疑。

6. Prompt 描述了症状,Claude 修了另一个原因

「用户看到错的时间戳」可能是时区、格式化、服务器时钟、浏览器 locale 或 DB 列类型。Claude 挑了一个原因修了,但真因是另一个。

如何判断:Claude「修」完 bug 还能复现。上报的症状没钉死是哪个根因。

最短修复路径

按收益从高到低。Step 1 在便宜的阶段(planning)逮住绝大多数 misalignment,而不是在昂贵的阶段(code)。

Step 1:每个非琐碎任务都先过一道你批准的计划

干这件事的原生工具就是 Plan Mode。按 Shift+Tab 循环权限模式,直到状态栏显示 ⏸ plan mode on(循环顺序是 normal → auto-accept edits → plan),或者输入 /plan。Claude 会只读地调查、写出计划,然后停下。在你接受计划之前不会改任何文件。把计划对照你的意图审一遍,然后批准;或者发回修正让它重新规划。

Plan Mode 帮你搭好了结构,但它不会逼 Claude 主动暴露它自己的疑虑。在 prompt 里加上这段,让歧义显形:

提出计划之前:
1. 用 3 条 bullet 重述任务:
   - 我要你做什么(一句话)
   - 成功是什么样(一个可观察的标准)
   - 什么不在 scope 里(你绝不会动的东西)
2. 列出 2 个你考虑过但拒绝的替代解读。

「替代解读」这一行是杠杆——逼 Claude 把歧义说出来,而不是闷头挑一种读法。

Step 2:修 bug 时,先要失败复现

开修之前:
1. 复现 bug。给我精确的触发输入 + 精确的错误输出。
2. 把失败 trace 到负责的代码,标 file:line。
3. 提出 fix。
4. 等我批准再写代码。

这能逮住「修错 bug」,因为你在任何修改之前就看到了复现。如果它的复现和看到的不一致,偏差当场就暴露了。

Step 3:多步任务,每步要 status 更新

每一步(改文件 / 加测试 / 跑 migration)之后:
- 贴一行总结
- 停下,等 "proceed"
- 我确认后才进入下一步

你能中途打断,而不是到最后才发现 misalign。总结一看不对就按 Esc 打断;已完成的部分会保留,你可以从那里改方向。

Step 4:偏差发生时,诊断 plan,而不是 diff

输出不符时,别只顾着改代码——先找出哪里误读了:

你的输出是 X 的正确代码,我要的是 Y。

走一遍:
1. 你理解的 task 是什么?
2. 我 prompt 里的什么导致了这个理解?
3. 什么信号会让你改成理解成 Y?

回答会告诉你撞上了六个原因里的哪一个,也会改善你下一次的 prompt。

Step 5:非琐碎工作用结构化 prompt 模板

Goal: [一句话]
Acceptance: [一个可观察的标准]
Scope: [允许动的文件]
Out of scope: [不要动的文件 / 关切]
Constraints: [必须 / 不允许]
Context: [Claude 推不出来的领域信息]

六行模板消掉绝大多数歧义驱动的误读。其中 Out of scope 那一行专门防原因 4。

Step 6:反复偏差,就把模板变成 slash command

如果类似 prompt 反复出错(「修 bug」→ 修错 bug),问题出在 prompt 的类别上,而不是单条 prompt。把这套结构化布局存成可复用的命令,别再重打。

到 2026 年 6 月,自定义命令已经并入了 skills。两种写法都还能用,都会生成一个 slash command:

  • 旧写法:.claude/commands/fix-bug.md 这个 Markdown 文件 → /fix-bug
  • 新写法(推荐):.claude/skills/fix-bug/SKILL.md/fix-bug

项目级 skill 放在 .claude/skills/,随仓库共享;个人级放在 ~/.claude/skills/,跨项目跟着你走。用户在命令后面打的内容会进到 $ARGUMENTS。示例 SKILL.md

---
description: Reproduce, locate, then propose a fix before writing any code
argument-hint: [bug description]
allowed-tools: Read, Grep, Glob, Bash
---
Bug to fix: $ARGUMENTS

Before editing any file:
1. Restate the bug in one sentence and state what is OUT of scope.
2. Reproduce it: exact input -> exact wrong output.
3. Trace to the responsible code (file:line).
4. Propose the fix and WAIT for my approval.

这样 /fix-bug timezone in invoice rendering 每次都跑完整道关卡。每个类别建一个——/fix-bug/add-feature/refactor——各自按那类工作校准。

如何确认真修好了

  1. 重跑最初的失败复现。你上报的那个症状必须消失——不是相关的另一个症状。
  2. 打开 diff,确认只动了你划进 Scope 的文件。Scope 之外出现改动,说明原因 4 漏过来了;拒掉、重新规划。
  3. 把 Claude 的计划总结对照你那行 Acceptance 标准读一遍。如果两者描述的不是同一个结果,那计划就是错的,代码也跟着错。

预防建议

  • 非琐碎工作保持 Plan Mode 开着(Shift+Tab⏸ plan mode on);任何改动前先批准计划。
  • 修 bug 必须先要失败复现,再要 fix 方案。
  • 把按类别的模板变成 /fix-bug/add-feature/refactor 这些 skill,让这道关卡只需一个按键。
  • 多步任务要每步 status,偏差可用 Esc 打断。
  • 偏差发生时,诊断 plan(不是 diff),并更新对应类别的 skill。
  • CLAUDE.md 保持精简,用 /memory 检查;里面一条过时的惯例会悄悄盖过你的 prompt。
  • 五行具体的 prompt 胜过五十行含糊的。具体 > 长。

常见问题

Plan Mode 会多花 token 吗? 它会多加一轮规划,但这一轮是只读代码、不改文件,比让 Claude 写错代码、再回滚、再重来要便宜得多——「回滚再重来」那个循环才是烧 token 的元凶。

我 prompt 写得很清楚,它还是跑偏了,怎么办? 跑 Step 4 的诊断。问它把 task 理解成了什么、prompt 里的什么把它带过去了。多半指向某条 CLAUDE.md 默认值(原因 3),或者它从某个打开的文件里捡到的 context(原因 4),而不是你的措辞。

从 Sonnet 4.6 换成 Claude Opus 4.7 能修好吗? 对真正难的推理偶尔有用,但 misalignment 主要是「规划关卡」的问题,不是「模型能力」的问题。一个较弱的模型加上「先重述、再批准」的关卡,胜过一个无人盯着、放任自流的更强模型。

怎么让它别去改我没点名的文件? 两层防护:在 prompt 里写明确的 Out of scope 一行;在 slash-command 的 skill 里用 allowed-tools / disallowed-tools 约束它能碰什么。Plan Mode 也会在任何写入前把文件清单亮出来,你可以直接拒掉越界的计划。

我批准了计划,代码还是偏离了计划。 那是本文不覆盖的另一种失败——计划对,执行没跟上。用 Step 3(每步 status),在第一个偏离的步骤就逮住,而不是拖到最后。

相关阅读

外部参考:Claude Code Plan Mode(官方文档)Slash commands 与 skills(官方文档)

标签: #排查 #Claude Code #排查 #输出不符