第五讲 — 科学方法调试
L01 > L02 > L03 > L04 > [ L05 ] L06 | L07 > L08 > L09 > L10 > L11 > L12
"否定假设也是进展。" — 知道"什么不是原因"和知道"什么是原因"同样有价值。
本讲核心:用
/autoresearch:debug把考古式翻查日志变成系统性假设驱动的 bug 调查。
代码示例:code/
配套项目:项目三 — 调试真实故障
问题
大多数调试是"考古式"的:翻查日志,同时改动多个变量,最终碰巧找到修复——却从未真正理解为什么有效。下次遇到类似 bug,重新来过。
问题在于没有系统性——每次调试都是从零开始,没有积累。
解决方案
收集症状
|
v
侦察(绘制错误范围)
|
v
+---+---+
| 形成 | → 一个具体、可证伪的假设
| 假设 | "错误发生在 X,因为 Y"
+---+---+
|
v
单一实验 → git commit(先提交)
|
v
分类结果:
确认 → 修复并记录
否定 → 记入 eliminated.md → 下一个假设
无结论 → 设计更好的实验
|
v
重复,直到根因确认或预算耗尽工作原理
1. 每次只测试一个假设
python
# ✓ 好的假设(单一、可证伪)
hypothesis = "用户 ID 为 None 时 validate_user() 抛出 KeyError"
experiment = "print(validate_user(None))" # 单一实验
# ✗ 坏的假设(多变量)
# "也许是数据库连接,或者参数验证,或者权限检查有问题"
# → 同时改三处 → 无法归因2. 从 7 种调查技术中选择
| 技术 | 适用场景 |
|---|---|
| 二分搜索 | 知道好的状态和坏的状态,找分界点 |
git bisect | bug 是某次提交引入的 |
| 最小复现 | 逐步缩小触发 bug 的最小条件 |
| 执行追踪 | 逐行追踪运行时状态 |
| 模式搜索 | 在代码库中搜索相似问题 |
| 反向追踪 | 从错误信息向上追溯调用栈 |
| 橡皮鸭调试 | 用自然语言解释代码逻辑,发现逻辑漏洞 |
agent 根据 bug 类型自动选择,3 次迭代未缩小范围时自动切换技术。
3. 三个输出文件
hypotheses.md → 所有假设:待测试 / 已确认 / 已否定
eliminated.md → 已排除原因的干净列表(最有价值!)
findings.md → 确认的 bug + 复现步骤 + 推荐修复eliminated.md 是最有价值的输出——它防止未来的开发者重新调查死路,是可积累的机构知识。
4. 链式到修复
bash
/autoresearch:debug # 找出根因
/autoresearch:debug --fix # 找出根因后自动切换到修复流程变更内容
| 方式 | 考古式调试 | 科学方法调试 |
|---|---|---|
| 假设数量 | 同时猜多个 | 每次一个 |
| 否定发现 | 丢弃 | 记入 eliminated.md |
| 跨会话 | 重新开始 | 从 hypotheses.md 继续 |
| 修复链接 | 手动 | --fix 自动链接 |
试一试
运行假设追踪器,模拟一次系统性调试会话:
sh
cd docs/zh/lectures/lecture-05-scientific-debugging/code
python hypothesis_tracker.py
python falsification_loop.py思考题:
- 在
falsification_loop.py的输出里,找到第一个被"否定"的假设。它排除了什么原因? - 如果你同时测试两个假设,结果是"确认",你能确定是哪一个有效吗?
- 你最近调试过的一个 bug——如果用科学方法,第一个假设会是什么?
- 为什么
eliminated.md比findings.md更有长期价值?
下一讲:第六讲 — 错误归零流水线