| name | diagnose |
| description | 面向疑难 bug 和性能回归的纪律化诊断循环。复现 → 最小化 → 提出假设 → 插桩 → 修复 → 回归测试。当用户说 "diagnose this" / "debug this"、"诊断一下" / "排查一下"、报告 bug、说某处坏了/抛异常/失败,或描述性能回归时使用。 |
诊断(Diagnose)
一套面向疑难 bug 的纪律。除非有明确理由,否则不要跳过任一阶段。
探查代码库时,借助项目的领域术语表建立相关模块的清晰心智模型,并查看你要改动区域的 ADR。
阶段 1 — 建立反馈循环
这才是这套技能的核心。 其余都是机械步骤。只要你为这个 bug 拥有一个快速、确定、可由 agent 自行运行的通过/失败信号,你就一定能找到根因——二分、假设验证、插桩都只是消费这个信号。如果没有这个信号,盯着代码看再久也救不了你。
在这里投入超出比例的精力。要主动出击,要有创造力,绝不轻言放弃。
构造反馈循环的方式——大致按此顺序尝试
- 失败的测试:在能触达 bug 的任意接缝处——单元、集成、端到端。
- Curl / HTTP 脚本:打到运行中的开发服务器。
- CLI 调用:用固定输入跑 CLI,把 stdout 与已知正确的快照做 diff。
- 无头浏览器脚本(Playwright / Puppeteer):驱动 UI,对 DOM/console/network 做断言。
- 回放捕获的 trace:把真实的网络请求 / payload / 事件日志存盘,在隔离环境中沿同一代码路径回放。
- 一次性测试夹具(harness):搭起系统的最小子集(单个服务、依赖 mock 掉),用一次函数调用触达 bug 代码路径。
- 属性 / 模糊测试循环:如果 bug 是"有时输出错误",跑 1000 个随机输入找出失败模式。
- 二分夹具:如果 bug 出现在两个已知状态之间(提交、数据集、版本),把"在状态 X 启动 → 检查 → 重复"自动化,这样就能
git bisect run。
- 差分循环:把同一输入分别喂给旧版 vs 新版(或两套配置),对输出做 diff。
- HITL bash 脚本:最后手段。如果必须人来点击,用
scripts/hitl-loop.template.sh 驱动人,让循环仍然是结构化的。捕获的输出回流给你。
建好正确的反馈循环,bug 就解决了 90%。
持续打磨循环本身
把循环当作一个产品来对待。一旦有了某个循环,就追问:
- 能更快吗?(缓存初始化、跳过无关 init、收窄测试范围。)
- 信号能更锐利吗?(断言具体症状,而不是"没崩"。)
- 能更确定吗?(固定时间、播种 RNG、隔离文件系统、冻结网络。)
一个 30 秒且不稳定的循环,比没有循环强不了多少。一个 2 秒且确定的循环,是调试的超能力。
非确定性 bug
目标不是干净复现,而是更高的复现率。把触发条件循环 100 次、并行化、加压、收窄时序窗口、注入 sleep。50% 概率的偶发 bug 可调试;1% 的不行——持续抬高复现率,直到它可调试为止。
当你确实建不出循环时
停下来,明确说出来。列出你尝试过的方法。向用户索要:(a) 能复现问题的环境访问权,(b) 一份捕获产物(HAR 文件、日志转储、core dump、带时间戳的录屏),或 (c) 在生产环境加临时插桩的许可。不要在没有循环的情况下就去提假设。
在你拥有一个自己相信的循环之前,不要进入阶段 2。
阶段 2 — 复现
跑循环。亲眼看着 bug 出现。
确认:
复现出 bug 之前,不要继续往下走。
阶段 3 — 提出假设
在验证任何假设之前,先生成 3–5 个排好序的假设。只生成单一假设会让你锚定在第一个看似合理的念头上。
每个假设都必须可证伪:说清它做出的预测。
格式:"如果 是根因,那么 <改变 Y> 会让 bug 消失 / <改变 Z> 会让它更严重。"
如果你说不出这个预测,那这个假设只是一种感觉——丢弃它,或把它打磨锐利。
在验证前把排好序的列表给用户看。 他们往往有领域知识能瞬间重排("我们刚给 #3 上线了一个改动"),或知道哪些假设已经被排除。便宜的检查点,省下大量时间。不要为此阻塞——如果用户不在,就按你的排序继续。
阶段 4 — 插桩
每个探针都必须对应阶段 3 的某个具体预测。一次只改一个变量。
工具偏好:
- 如果环境支持,用调试器 / REPL 检查。一个断点胜过十条日志。
- 在区分各假设的边界处打定向日志。
- 绝不"全量打日志再 grep"。
给每条调试日志打标签,例如 [DEBUG-a4f2]。最后清理就变成一次 grep。没打标签的日志会残留;打了标签的日志会被清掉。
性能分支。 对性能回归,日志通常是错的方向。应改为:先建立基线测量(计时夹具、performance.now()、profiler、查询计划),再做二分。先测量,后修复。
阶段 5 — 修复 + 回归测试
在修复之前先写回归测试——但前提是存在一个正确的接缝。
正确的接缝是指:测试在调用点上触发的是真实的 bug 模式。如果唯一可用的接缝太浅(bug 需要多个调用方却只能写单调用方测试、单元测试无法复现触发 bug 的调用链),在那里写回归测试只会带来虚假的信心。
如果不存在正确的接缝,这本身就是一个发现。 记下它。是代码库架构在阻止这个 bug 被锁定。把它标记给下一阶段。
如果存在正确的接缝:
- 把最小化后的复现变成该接缝处一个失败的测试。
- 看着它失败。
- 应用修复。
- 看着它通过。
- 针对原始的(未最小化的)场景重跑阶段 1 的反馈循环。
阶段 6 — 清理 + 复盘
宣告完成前必须满足:
然后追问:什么本可以预防这个 bug? 如果答案涉及架构改动(缺好的测试接缝、调用方纠缠、隐藏耦合),把具体细节记为后续建议。在修复落地之后再提建议,而不是之前——此刻你掌握的信息比开始时多得多。