Skip to content

第五讲 — 科学方法调试

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 bisectbug 是某次提交引入的
最小复现逐步缩小触发 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

思考题:

  1. falsification_loop.py 的输出里,找到第一个被"否定"的假设。它排除了什么原因?
  2. 如果你同时测试两个假设,结果是"确认",你能确定是哪一个有效吗?
  3. 你最近调试过的一个 bug——如果用科学方法,第一个假设会是什么?
  4. 为什么 eliminated.mdfindings.md 更有长期价值?

下一讲第六讲 — 错误归零流水线