--- name: debug-expert description: "Use when 程序出现错误、异常、崩溃,或行为与预期不符,或测试失败,或无法定位问题根因时。触发场景:调试、debug、报错、错误、异常、bug、问题排查、故障排查、不工作、崩溃、无法运行、出错了、为什么不生效、运行报错、跑不起来、程序挂了。" --- # 调试专家 铁律:**先理解,再修改。** 禁止在没有复现和定位根因之前就修改代码(猜测式修改往往掩盖真实问题)。 在完成**阶段二(假设清单)**和**阶段三(最小复现)**之前,禁止对任何代码做出修改。 即便你"知道"原因,也必须先建立假设清单并尝试复现,再动手修改。 ## 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,因为 [原因]。 ``` **思考方向**: - 最近的改动(最可能的原因) - 环境差异(本地可以,线上不行 → 看配置、依赖、权限) - 数据问题(特定数据触发 → 看边界条件) - 并发/时序问题(随机出现 → 看竞态条件) - 外部依赖(网络/数据库/第三方服务) --- ### 阶段三:最小复现 在验证假设之前,先建立最小可复现的测试案例: ```python # 目标:用最少的代码稳定复现问题 # 好处: # 1. 确认问题确实存在(而非环境问题) # 2. 排除无关因素 # 3. 修复后可用作回归测试 # 最小复现示例 def test_bug_reproduction(): # 最简单的触发路径 result = problematic_function(minimal_input) assert result == expected # 这一行会失败 ``` **如果无法复现**: - 说明是环境问题 → 系统对比两个环境的差异 - 说明是特定数据问题 → 询问触发数据的特征 加载 `references/root-cause-analysis.md` 获取系统化分析工具。 --- ### 阶段四:定位根因 使用二分法逐步缩小问题范围: ``` 定位策略: 1. 确认问题的边界(从哪里开始出错,到哪里结束) 2. 在中间点添加检查点,判断问题在前半段还是后半段 3. 重复,直到定位到具体的函数/行 ``` **常用调试工具**: ```python # Python:pdb 调试 import pdb; pdb.set_trace() # 设置断点 # 或者使用 print 调试(快速但临时) print(f"DEBUG: variable={variable!r}, type={type(variable)}") # 日志记录 import logging logging.debug("状态: %s", state) ``` ```bash # 查看进程状态 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` 全局搜索) - [ ] 修复是否引入了新问题?(运行完整测试套件) - [ ] 是否需要添加回归测试防止将来重现? - [ ] 根因是否真正解决,而非只是绕过症状? 4. **记录**(如果是重要的 Bug): ``` 根因:[什么导致的] 触发条件:[什么情况下会触发] 修复方式:[如何修复] 预防措施:[如何防止再次发生] ``` --- ## 特殊问题处理 ### 随机/偶发性问题 - 极大概率是**竞态条件**或**内存问题** - 增加日志详细度,在生产环境收集更多信息 - 检查并发访问共享资源的代码 - 使用压测工具提高触发频率 ### 只在特定环境出现 系统对比两个环境: ```bash # 对比环境变量 diff <(env | sort) <(ssh prod 'env | sort') # 对比依赖版本 pip freeze vs pip freeze(生产) # 对比配置文件 diff local.env prod.env ``` ### 性能问题 先测量,再优化,不要靠直觉: ```python # 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` — 常见问题类型的调试模式