SuperAPI AI 工具集

diagnose

测试与质量

查看项目

更新于 10-16 04:11

技能介绍

diagnose 是一个用于诊断疑难 Bug 和性能回退的技能,强调纪律化的诊断循环,只有在有明确理由时才可跳过阶段。在探索代码库时,建议使用项目的领域术语表建立相关模块的清晰心智模型,并检查所涉及区域的 ADR(架构决策记录)。

第一阶段——构建反馈回路

这是整个技能的核心,其余都是机械性工作。如果你能针对该 Bug 获得快速、确定、可由智能体运行的通过/失败信号,你就能找到原因——二分、假设验证和插桩都只是消费这个信号。如果没有这样的信号,再怎么看代码也无济于事。应在此投入不成比例的精力,要激进、要有创造力、拒绝放弃。

构建反馈回路的方式(大致按此顺序尝试):

  • 在能触达 Bug 的接缝处编写失败测试——单元、集成或端到端。
  • 针对运行中的开发服务器编写 Curl / HTTP 脚本。
  • 使用固定输入调用 CLI,将 stdout 与已知正确的快照做 diff。
  • 无头浏览器脚本(Playwright / Puppeteer)——驱动 UI,对 DOM、控制台或网络进行断言。
  • 重放捕获的 trace:把真实网络请求、载荷或事件日志保存到磁盘,隔离地通过代码路径重放。
  • 一次性脚手架:启动系统的最小子集(单个服务、mock 依赖),用一次函数调用走一遍 Bug 所在的代码路径。
  • 属性 / 模糊测试循环:如果 Bug 表现为“有时输出错误”,运行 1000 个随机输入,寻找失败模式。
  • 二分脚手架:如果 Bug 出现在两个已知状态(提交、数据集、版本)之间,自动化“在状态 X 启动、检查、重复”,以便用 git bisect run 处理。
  • 差分循环:把同一输入分别通过旧版本与新版本(或两种配置)运行,diff 输出。
  • 人在回路(HITL)bash 脚本:最后手段。如果必须由人点击,用 scripts/hitl-loop.template.sh 驱动,使循环仍保持结构化,捕获的输出回传给你。

构建出正确的反馈回路,Bug 就解决了 90%。

迭代回路本身

把回路当作产品。一旦有了回路,就自问:能让它更快吗(缓存设置、跳过无关初始化、缩小测试范围)?能让信号更锐利吗(针对具体症状断言,而不是“没有崩溃”)?能让它更确定吗(固定时间、设定随机数种子、隔离文件系统、冻结网络)?一个 30 秒的不稳定回路几乎不比没有强;一个 2 秒的确定性回路则是调试超能力。

非确定性 Bug

目标不是干净的复现,而是更高的复现率。将触发循环 100 次、并行化、加压、缩小时间窗口、注入 sleep。50% 抖动的 Bug 可调试,1% 则不行——持续提高复现率直到可调试。

当确实无法构建回路时

停下来并明确说明。列出你尝试过的内容。向用户请求:(a) 访问能复现该问题的环境,(b) 捕获的产物(HAR 文件、日志转储、核心转储、带时间戳的屏幕录制),或 (c) 允许添加临时生产插桩。没有回路就不要进入假设阶段。在你信任某个回路之前,不要进入第二阶段。

第二阶段——复现

运行回路,观察 Bug 出现。确认:回路产生的失败模式与用户描述一致,而不是附近碰巧发生的另一种失败(错误的 Bug 等于错误的修复);该失败在多次运行中可复现(对于非确定性 Bug,复现率足够高以便调试);已捕获确切症状(错误信息、错误输出、缓慢的耗时),以便后续阶段验证修复是否真正解决了它。在复现 Bug 之前不要继续。

第三阶段——提出假设

在测试任何假设之前,先产生 3–5 个有排序的假设。单一假设会锚定在第一个看似合理的想法上。每个假设必须可证伪:说明它做出的预测。格式:“如果 是原因,那么 会使 Bug 消失 / 会使 Bug 更严重。”如果无法说出预测,这个假设就只是一种感觉——丢弃或细化它。在测试前把排序列表展示给用户,他们往往拥有领域知识,能立即重新排序(“我们刚给第 3 项部署了一个变更”),或者知道已被排除的假设。这是低成本的检查点,能节省大量时间。不要因此阻塞——如果用户不在,就按你的排序继续。

第四阶段——插桩

每个探针都必须映射到第三阶段的一个具体预测。一次只改变一个变量。工具偏好:如果环境支持,使用调试器 / REPL 检查(一个断点胜过十条日志);在能区分假设的边界处使用有针对性的日志;绝不“记录一切然后 grep”。给每条调试日志打上唯一前缀,例如 [DEBUG-a4f2],最后清理时只需一次 grep。无标记的日志会留存,有标记的日志会被清除。

性能分支:对于性能回退,日志通常是错误的方法。应改为先建立基线测量(计时脚手架、performance.now()、profiler、查询计划),然后二分。先测量,后修复。

第五阶段——修复 + 回归测试

在修复之前先写回归测试——但只有在存在正确接缝时才这样做。正确的接缝是指测试能演练在调用点真实发生的 Bug 模式。如果唯一可用的接缝太浅(Bug 需要多个调用方时只做单调用方测试,或单元测试无法复现触发 Bug 的调用链),在该处的回归测试会给出虚假信心。如果不存在正确接缝,这本身就是发现,记录下来:代码库架构正在阻止把 Bug 锁定住,将其标记给下一阶段。

如果存在正确接缝:把最小化复现转成该接缝处的失败测试;看着它失败;应用修复;看着它通过;针对原始(未最小化的)场景重新运行第一阶段的反馈回路。

第六阶段——清理 + 复盘

声明完成前必须满足:原始复现不再复现(重新运行第一阶段回路);回归测试通过(或缺席接缝已被记录);所有 [DEBUG-...] 插桩已移除(grep 该前缀);一次性原型已删除(或移动到明确标记的调试位置);被证明正确的假设写在提交 / PR 信息中——让下一位调试者从中学习。

然后自问:什么本可以预防这个 Bug?如果答案涉及架构变更(没有好的测试接缝、调用方纠缠、隐藏耦合),把具体情况交给 /improve-codebase-architecture 技能。在修复落地之后再提出建议,而不是之前——因为现在你比开始时拥有更多信息。