debug-expert

SKILL.md

调试专家

铁律:先理解,再修改。 禁止在没有复现和定位根因之前就修改代码(猜测式修改往往掩盖真实问题)。

Inputs / Outputs / Gates / Handoffs(统一契约)

  • Inputs(最小输入):期望 vs 实际;完整错误信息/日志/堆栈;可复现步骤(如有);最近改动(如有);运行环境信息(OS/版本/命令)。
  • Outputs(产物形态):一份可交接的调试记录(结构参考 references/debug-log-template.md),包含假设清单、最小复现、证据链、验证命令与回归建议。
  • Gates(继续前必须满足)
    • 未完成“假设清单 + 最小复现”前禁止修改代码(保持与本文件 HARD-GATE 一致)。
    • 宣称“已修复”前必须运行验证命令并贴出绿色输出或关键结果(保持与本文件后续 HARD-GATE 一致)。
    • 通用门控清单可复制使用:../code-review-expert/references/quality-gates-checklist.md
  • Handoffs(推荐下游)
    • tdd-master(TDD 开发大师):先写能复现问题的测试,再修复
    • writing-plans(实施计划编写):把修复拆成可执行步骤(适合复杂问题)
    • code-review-expert(代码审查专家):变更后做质量门禁

调试工作流

阶段一:问题理解

收集足够的背景信息(每次最多问 2-3 个问题):

必须了解

  • "期望行为是什么?实际发生了什么?"
  • "错误信息或日志是什么?"(要求贴出完整错误,不要省略)
  • "最后一次正常工作是什么时候?中间做了什么改动?"

按需追问

  • "是否能稳定复现?还是随机出现?"
  • "在什么环境发生的?(本地/测试/生产,什么 OS/版本)"
  • "是否有完整的调用堆栈?"

明确禁止:在没有完整错误信息时就开始猜测原因。


阶段二:建立假设

根据现有信息,生成 2-5 个可能的假设(从最可能到最不可能排序):

假设清单:
1. [假设 A]:可能性 高/中/低,理由:[...]
2. [假设 B]:可能性 高/中/低,理由:[...]
3. [假设 C]:可能性 高/中/低,理由:[...]

验证计划:先验证假设 1,因为 [原因]。

思考方向

  • 最近的改动(最可能的原因)
  • 环境差异(本地可以,线上不行 → 看配置、依赖、权限)
  • 数据问题(特定数据触发 → 看边界条件)
  • 并发/时序问题(随机出现 → 看竞态条件)
  • 外部依赖(网络/数据库/第三方服务)

阶段三:最小复现

在验证假设之前,先建立最小可复现的测试案例:

# 目标:用最少的代码稳定复现问题
# 好处:
# 1. 确认问题确实存在(而非环境问题)
# 2. 排除无关因素
# 3. 修复后可用作回归测试

# 最小复现示例
def test_bug_reproduction():
    # 最简单的触发路径
    result = problematic_function(minimal_input)
    assert result == expected  # 这一行会失败

如果无法复现

  • 说明是环境问题 → 系统对比两个环境的差异
  • 说明是特定数据问题 → 询问触发数据的特征

加载 references/root-cause-analysis.md 获取系统化分析工具。


阶段四:定位根因

使用二分法逐步缩小问题范围:

定位策略:
1. 确认问题的边界(从哪里开始出错,到哪里结束)
2. 在中间点添加检查点,判断问题在前半段还是后半段
3. 重复,直到定位到具体的函数/行

常用调试工具

# Python:pdb 调试
import pdb; pdb.set_trace()  # 设置断点

# 或者使用 print 调试(快速但临时)
print(f"DEBUG: variable={variable!r}, type={type(variable)}")

# 日志记录
import logging
logging.debug("状态: %s", state)
# 查看进程状态
ps aux | grep process_name
# 查看端口占用
lsof -i :8080
# 查看系统日志
journalctl -u service_name -n 100 --no-pager
# 查看 Docker 容器日志
docker logs container_name --tail 100

加载 references/debugging-patterns.md 获取特定类型问题的调试模式。


阶段五:修复与验证

定位根因后:

  1. 制定修复方案(不要第一个想到的方案就是最好的):

    • 方案 A:[描述],优点/缺点
    • 方案 B:[描述],优点/缺点
    • 推荐:[哪个方案,为什么]
  2. 实施修复

  3. 验证清单(完成前强制检查,不得跳过):

  • 原始问题是否已解决?(运行最小复现案例)
  • 是否有其他类似的代码也存在相同问题?(用 rg 全局搜索)
  • 修复是否引入了新问题?(运行完整测试套件)
  • 是否需要添加回归测试防止将来重现?
  • 根因是否真正解决,而非只是绕过症状?
  1. 记录(如果是重要的 Bug):
    根因:[什么导致的]
    触发条件:[什么情况下会触发]
    修复方式:[如何修复]
    预防措施:[如何防止再次发生]
    

特殊问题处理

随机/偶发性问题

  • 极大概率是竞态条件内存问题
  • 增加日志详细度,在生产环境收集更多信息
  • 检查并发访问共享资源的代码
  • 使用压测工具提高触发频率

只在特定环境出现

系统对比两个环境:

# 对比环境变量
diff <(env | sort) <(ssh prod 'env | sort')
# 对比依赖版本
pip freeze vs pip freeze(生产)
# 对比配置文件
diff local.env prod.env

性能问题

先测量,再优化,不要靠直觉:

# Python 性能分析
import cProfile
cProfile.run('main_function()', sort='cumulative')

# 简单计时
import time
start = time.perf_counter()
# ...代码...
print(f"耗时:{time.perf_counter() - start:.3f}秒")

红旗警告:当你想跳过流程时

遇到以下想法,立刻停下,回到当前应该所在的阶段:

借口 现实
"错误很明显,我知道原因,直接改就好" "明显原因"往往是症状不是根因。30 秒建个假设清单不会耽误你。
"问题太紧急,没时间走流程" 猜测式修改引入新 bug 比走流程耗时更长。紧急时更要冷静。
"已经修了类似的 bug,这次一样" 表面相似的 bug 根因可能完全不同。相似性是陷阱。
"最小复现太麻烦,能复现就行" 无法最小复现 = 无法确认修复 = 留下定时炸弹。
"测试过了,差不多能用" "差不多"不是通过标准。必须运行验证清单中的每一项。
"这个问题太随机,复现不了" 随机问题 = 竞态条件/内存问题。不复现不代表可以跳过假设阶段。

参考资源

  • references/root-cause-analysis.md — 根因分析工具和框架
  • references/debugging-patterns.md — 常见问题类型的调试模式
Weekly Installs
3
GitHub Stars
122
First Seen
10 days ago
Installed on
opencode3
gemini-cli3
deepagents3
antigravity3
github-copilot3
codex3