关于 debug-hypothesis
面向编码 Agent 的假设驱动调试技能,按观察、假设、实验和结论四阶段推进。它要求先以证据验证原因再编写修复,并将调查过程记录在 DEBUG.md 中。
主要功能
- 01四阶段调试循环
- 02提出并排序根因假设
- 03实验改动最多五行
- 04在 DEBUG.md 记录调查
- 05确认原因后添加回归测试
适用场景
- 01调查原因不明的测试失败
- 02追查修复后复发的缺陷
- 03诊断不熟悉的崩溃或错误
- 04排查不同环境间的行为差异
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ lichamnesia/ lich-skills --skill debug-hypothesis安装后在 Claude Code 中输入/debug-hypothesis确认已加载。
详细介绍
假设驱动调试
一个四阶段循环,将调试从“随便试些修复,然后碰运气”转变为严谨的调查。每个阶段都有目标、硬性规则,以及一张合理化借口表,列出 Agent 为跳过该阶段会编造的理由。
核心原则:**在有证据证明假设正确之前,不得编写修复代码。**猜测不是调试。
适用场景
- 测试失败,且原因无法立即看出
- 同一个 Bug 已经“修复”过两次,却再次出现
- Agent 尝试的修复不起作用——阻止它再试另一个
- 遇到从未见过的崩溃或错误消息
- 性能退化,但没有明显的罪魁祸首
- 不同环境下的行为不一致(本地与 CI、开发与生产)
- Agent 陷入循环,反复应用同一个错误修复
不适用场景
- 拼写错误、缺少导入或语法错误——直接修复
- 原因明显且只有一行的构建失败
- 编译器或代码检查器的消息已准确指出问题及位置
- 你已经知道根本原因,只需编写修复代码
如果 Bug 在尝试修复一次后仍然存在,请立即切换到此技能。
调试循环
OBSERVE ──▶ HYPOTHESIZE ──▶ EXPERIMENT ──▶ CONCLUDE
│ │ │ │
▼ ▼ ▼ ▼
Gather List 3-5 One minimal Root cause
symptoms, possible test per confirmed
reproduce causes + hypothesis, or loop
reliably evidence max 5 lines back
│ │ │ │
└──────────────┴──────────────────┴───────────────┘
write everything to DEBUG.md
硬性规则:
- 所有内容都要写入
DEBUG.md。如果推理只存在于对话中,上下文压缩会将其吞掉。 - 在观察、假设或实验阶段,不得编写修复代码。
- 不得跳过假设阶段。“我想我知道原因了”就是一个假设——把它写下来,并像检验其他假设一样进行测试。
- 每次实验最多改动 5 行。如果实验需要更多改动,说明假设过于模糊——将其拆分。
阶段 1:观察
**目标。**收集原始事实。复现 Bug。区分你知道的内容与假设的内容。
步骤。
- 复现 Bug。获取准确的错误消息、堆栈跟踪或错误输出。如果无法复现,这就是你的第一个发现。
- 找到最小复现。移除无关代码,直到 Bug 仍然出现。
- 记录环境:操作系统、运行时版本、依赖项、配置。
- 记录哪些内容可以正常工作。正常与异常之间的边界,就是 Bug 所在之处。
- 将所有观察结果写入
DEBUG.md的## Observations小节。
退出标准。
- 已复现 Bug(或记录了无法复现的情况及条件)
- 已记录准确的错误消息或错误行为
- 已找到最小复现
- 已将观察结果写入
DEBUG.md
常见合理化借口
| 借口 | 实际情况 |
|---|---|
| “我已经知道问题出在哪了” | 那就把它写成假设并证明它。如果你是对的,只需 2 分钟。 |
| “让我先试试这个快速修复” | 这样你最终就会花 45 分钟,尝试 6 次都失败。 |
| “错误消息已经说得很清楚了” | 错误消息描述的是症状,而不是原因。NullPointerException 告诉你什么出了问题,却没有说明为什么。 |
| “我不需要复现,我能从代码里看出 Bug” | 你确定吗?那为什么它还没有被修复? |
阶段 2:提出假设
目标。 提出 3-5 个可能的根本原因。对于每个原因,列出阶段 1 中支持和相悖的证据,并按可能性排序。
步骤。
- 列出 3-5 个假设。不是 1 个,也不是“我觉得是 X”。至少 3 个。
从以下类别进行思考:
- 数据:输入错误、字段缺失、类型不匹配、编码问题
- 逻辑:条件错误、差一错误、竞态条件、顺序错误
- 环境:配置、版本、依赖、权限
- 状态:缓存过期、状态泄漏、初始化顺序
- 对于每个假设,写出:
- 支持证据: 支持该理论的观察证据
- 相悖证据: 不支持该理论的证据
- 测试: 能证实或推翻该理论的最小实验
- 标记 根本假设——有支持证据且没有相悖证据的那个假设。如果有多个符合条件,选择最容易测试的一个。
- 将所有内容写入
DEBUG.md中的## Hypotheses下。
DEBUG.md 中的格式示例:
## Hypotheses
### H1: Race condition in session middleware (ROOT HYPOTHESIS)
- Supports: only happens under concurrent requests, timing-dependent
- Conflicts: none yet
- Test: add mutex lock around session read, check if bug disappears
### H2: Stale cache returning expired token
- Supports: works after restart (cache cleared)
- Conflicts: cache TTL is 5min, bug appears within 30s
- Test: disable cache, reproduce
### H3: Wrong env variable in CI
- Supports: works locally, fails in CI
- Conflicts: env diff shows identical values
- Test: print actual runtime value in CI logs
退出条件。
- 至少写出 3 个假设
- 每个假设都有支持证据和相悖证据
- 每个假设都有具体且精简的测试
- 已标记根本假设
- 所有内容都已写入
DEBUG.md
常见借口
| 借口 | 事实 |
|---|---|
| “我只有一个理论” | 你只有一个最喜欢的理论。再仔细想想。万一不是它呢? |
| “写下来太慢了” | 不写下来调试更慢。压缩一旦把它吃掉,你就会忘记假设 2。 |
| “第一个假设显然是对的” | 那么证实它只需 2 分钟。如果跳过验证,等发现它是错的,就要花 30 分钟。 |
| “我没有相悖证据” | 这意味着你还没有足够深入地调查,或者它确实就是根本原因。无论哪种情况,都要测试。 |
阶段 3:实验
目标。 用尽可能小的改动测试根本假设。 你是科学家——你要尝试证伪,而不是证实。
步骤。
- 运行实验前先写下实验方案。你要改什么?什么结果能证实假设?什么结果能推翻假设?
- 做出改动。最多 5 行。 如果需要更多,说明你的假设太模糊。
- 运行阶段 1 中的复现步骤。
- 将结果记录在
DEBUG.md中的## Experiments下。
实验规则。
- 一次只改一个变量。不要为了“节省时间”而合并两个修复。
- 不要编写生产环境修复代码。编写诊断代码:日志语句、断言、简化逻辑、硬编码值。
- 记录结果后撤销实验。保持工作区整洁。
- 如果实验没有明确结论,那也是一种结果——记录下来并测试下一个假设。
退出条件。
- 已执行实验(一次改动、一个变量)
- 已记录结果:证实、推翻或无法得出结论
- 已撤销实验代码
- 结果已写入
DEBUG.md
常见借口
| 借口 | 事实 |
|---|---|
| “我直接修复,不测试了” | 不确认原因就修复,可能会提交错误的修复并导致其他问题。 |
| “我一次测试两件事,节省时间” | 两者都改了而 Bug 消失时,你怎么知道是哪一个修复了它?现在你还得重新测试。 |
| “5 行不够” | 5 行足以添加日志、断言、硬编码值或短路逻辑。如果不够,你的假设就只是“某处出了问题”,而不是一个假设。 |
| “不用撤销,修复基本上就是实验” | 实验用于诊断,修复是生产代码。两者的质量要求不同。 |
阶段 4:总结
目标。 确认根本原因,编写真正的修复,并添加回归测试。
步骤。
- 如果根本假设得到确认:
- 在
DEBUG.md中用一句话写明根本原因。 - 现在——且仅限现在——编写生产环境修复代码。
- 添加一个回归测试,确保没有修复时测试失败,有修复时测试通过。
- 将修复和测试一起提交。
- 在
- 如果根本假设被否定:
- 在
DEBUG.md中记录否定结论及证据。 - 将下一个假设提升为根本假设。返回阶段 3。
- 如果所有假设都被否定,则带着新的观察结果返回阶段 1。
- 在
- 在
DEBUG.md中更新最终的## Root Cause和## Fix部分。
退出标准。
- 已找出根本原因,并用一句话写明
- 已提交修复
- 已提交回归测试
-
DEBUG.md已补全完整的调查过程 - 原始复现用例现在可以通过
常见借口
| 借口 | 实际情况 |
|---|---|
| “我不需要回归测试,这只是个简单修复” | 简单的 bug 修复不需要这项技能。你之所以在这里,是因为问题并不简单。添加测试。 |
“DEBUG.md 只是用来调试的,我会把它删掉” | 保留它。将来你再次调试同一区域时,会感谢现在的自己。 |
| “所有假设都不成立,我卡住了” | 回到观察阶段。你漏掉了什么。bug 确实存在,因此一定有原因。 |
反蛮干规则
AI 调试中最常见的失败模式:Agent 形成一个理论,写出 150 行“修复”代码,发现不起作用,于是沿着同一个错误理论继续深入,又写出 150 行代码。
这项技能就是为防止这种情况而存在的。 如果你发现自己或 Agent:
- 在确认假设前写了超过 5 行代码 → 停下。回到阶段 2。
- 第二次尝试相同的方法 → 停下。该假设已被否定。尝试下一个。
- 忽视相互矛盾的证据 → 停下。把证据写下来,重新排列假设的优先级。
- 连续失败 3 次后觉得“快成功了” → 停下。你正在蛮干。
写下来。测试它。证明它。然后再修复。
评论
支持 Markdown