mcpField
S
systematic-debugging

systematic-debugging

github.com/magnus919开发162117 次安装今天更新
代码仓库

关于 systematic-debugging

面向开发者的系统化调试技能,用于调查故障、缺陷、性能问题和集成问题。它要求先查明根因并收集证据,再验证最小修复,避免只处理表面症状。

主要功能

  1. 01复现问题并检查错误证据
  2. 02追踪跨组件的数据流
  3. 03比较正常与故障模式
  4. 04逐一验证根因假设
  5. 05修复前添加回归测试

适用场景

  1. 01调查失败的测试或构建
  2. 02追踪跨服务的集成故障
  3. 03诊断不一致的 API 或检索结果
  4. 04避免只修补表象以处理生产缺陷

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/magnus919/agent-skills --skill systematic-debugging

安装后在 Claude Code 中输入/systematic-debugging确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
evals/
evals.json
references/
dependency-source-example.md
macos-sandbox-debug-example.md

详细介绍

系统化调试

概述

随机修复会浪费时间并引入新 bug。临时补丁会掩盖根本问题。

**核心原则:**尝试修复前,务必找到根本原因。只修复症状,意味着失败。

铁律

NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST

如果尚未完成阶段 1,就不能提出修复方案。

适用场景

适用于任何技术问题:

  • 测试失败
  • 生产环境中的 bug
  • 行为异常
  • 性能问题
  • 构建失败
  • 集成问题

尤其适用于:

  • 时间紧迫时(紧急情况很容易让人忍不住猜测)
  • “只做一个快速修复”看起来显而易见时
  • 已经尝试过多种修复方案时
  • 之前的修复无效时
  • 尚未完全理解问题时

不要在以下情况下跳过:

  • 问题看起来很简单时(简单的 bug 也有根本原因)
  • 赶时间时(匆忙行事必然导致返工)
  • 有人要求立即修复时(系统化处理比反复试错更快)

四个阶段

完成当前阶段后,再进入下一阶段。


阶段 1:调查根本原因

在尝试任何修复之前:

1. 仔细阅读错误消息
  • 不要跳过错误或警告——其中往往包含确切的解决方案
  • 完整阅读堆栈跟踪。记下行号、文件路径和错误代码
  • **操作:**阅读错误位置的相关源文件,并在代码库中搜索错误字符串,找出所有相关代码路径。
2. 稳定复现
  • 能否可靠地触发问题?具体步骤是什么?
  • 如果无法复现 → 收集更多数据,不要猜测
  • **操作:**运行失败的测试或触发 bug:
pytest tests/test_module.py::test_name -v --tb=long
3. 检查近期更改
  • 哪些更改可能导致问题?Git 差异、近期提交、新依赖、配置更改
  • 操作:
git log --oneline -10
git diff
git log -p --follow src/problematic_file.py | head -100
4. 在多组件系统中收集证据

当系统包含多个组件时(API → 服务 → 数据库、CI → 构建 → 部署):

提出修复方案前,先添加诊断埋点。对于每个组件边界:

  • 记录进入组件的数据
  • 记录离开组件的数据
  • 验证环境和配置的传递情况
  • 检查每一层的状态

运行一次以收集证据,确定问题出在哪里。然后分析证据,找出故障组件。

5a. 检查架构和环境是否存在差异

当 bug 在生产环境中可以复现,但在测试中无法复现时:

  1. 比较测试夹具架构与生产环境架构——使用 PRAGMA table_info()、\\.schema 或同类命令
  2. 检查是否缺少 NOT NULL 列、外键、唯一约束或默认值
  3. 检查 SQLite 日志模式、连接标志或 PRAGMA 设置是否存在差异
  4. 验证测试数据的结构与生产环境一致——不仅要检查列名,还要检查约束

简化的测试架构是隐藏 bug 的常见来源。如果生产表包含 model TEXT NOT NULL,而测试表只有 vector BLOB,那么只有在违反 NOT NULL 约束时才会触发的 bug,就会顺利通过测试。

**操作:**分别对两个数据库运行 PRAGMA table_info(table_name),并对比输出。

5b. 检查回退链中的异常类型是否足够具体

当 try/except 回退逻辑未能捕获日志中出现的错误时:

try:
    # Primary path — can raise OperationalError
    conn.execute("INSERT INTO t (model_name) VALUES (?)", ...)
except sqlite3.OperationalError:
    # Fallback — can raise IntegrityError (sibling, not child)
    conn.execute("INSERT INTO t (vector) VALUES (?)", ...)

OperationalError 和 IntegrityError 是同级异常——二者都继承自 DatabaseError,而 DatabaseError 继承自 Error。捕获其中一种不会捕获另一种。

可采用以下修复方案:

  • 如果两条路径可能因不同子类型而失败,则捕获父类(except sqlite3.DatabaseError)
  • 将 Exception 作为最后的兜底(范围更广,但比漏掉异常类型更安全)
  • 显式处理每种预期的错误类型
5c. 渐进式特征分析 — 工具与 API 行为

当你调查的检索系统、API 或知识图谱返回空结果或不一致的结果时:

从有效的情况开始,逐步扩大范围,直到出现问题。这样可以隔离导致故障的变量。

输入预期实际诊断
单个已知术语(例如 python)有命中有命中工具正常,连接正常
来自同一文档的双词短语(type safety)有命中有命中短语检索正常
相关的双词短语(generic types)有命中0 次命中找到边界 — 问题仅与该短语有关
包含相同术语的更长查询0 次命中0 次命中确认:并非偶然

不要跳过特征分析: 直接断定“嵌入模型坏了”只是猜测。这个表格可以逐一排除变量。

5d. 修复库代码前检查依赖项来源

当你追踪到第三方依赖项中的 Bug 时:

修补库代码前,先确认它是从哪里安装的:

pip show <package-name>

关键字段:

  • 可编辑项目位置 — 如果存在,说明这是通过 pip install -e 安装的开发副本。这是有意安装的吗?
  • 位置 — 它位于 site-packages(生产环境)还是临时/开发目录中?
  • 版本 — 与 PyPI 上的最新版本进行比较:pip index versions <package-name>

规则: 如果该软件包是以可编辑开发副本的形式安装的,而你并非有意这样安装,立即停止并询问。症状可能是代码与上游版本不一致导致的,正确的修复方式可能是切换到生产软件包,而不是修补 fork。

示例: 一个后台工作进程因 table X has no column named Y 而崩溃。调查发现,这是从 /private/tmp/some-fork/ 安装的可编辑版本。某个开发 fork 在 INSERT 语句中添加了列名,却从未添加对应的架构迁移。正确的修复并非给 fork 添加迁移,而是切换到 PyPI 上的生产软件包,并删除开发副本。

6a. 先研究再猜测 — 系统化网页搜索

当你收集了所有本地证据,但仍不清楚根本原因时:

不要猜测解决方案。进行结构化的网页研究:

  1. 使用确切的错误消息搜索 — 引用错误消息,并包含错误代码和函数名
  2. 结合症状与平台上下文搜索 — 组合搜索故障现象和操作系统/工具版本
  3. 使用组件/服务名称搜索 — XPC 服务、守护进程和子系统通常有已记录的 Bug
  4. 优先查看近期讨论串 — 筛选当前版本相关内容。旧方案可能不适用
  5. 提出解决方案前阅读完整讨论串 — 只读部分内容会导致修复不完整
  6. 查看讨论串末尾是否有已确认的解决方法,不要只看最初的诊断
6b. macOS 应用故障排查 — 沙盒应用

当你调试 macOS 应用时(尤其是 Books、Music 或 App Store 等沙盒应用):

应用被限制在 ~/Library/Containers/<bundle-id>/ 下的沙盒容器中。

定位容器
ls ~/Library/Containers/<bundle-id>/
# Data/Library/ — preferences, caches, databases
# Data/Documents/ — user-visible content, import queues
检查配套的 XPC 服务

许多 Apple 应用会使用后台 XPC 服务执行文件操作:

# XPC services live in the framework bundle or app bundle:
/System/Library/PrivateFrameworks/<Framework>.framework/XPCServices/
/System/Applications/<App>.app/Contents/XPCServices/
ps aux | grep -i "<service-name>"
直接读取数据库

沙盒应用通常使用 SQLite/CoreData:

sqlite3 ~/Library/Containers/<bundle-id>/Data/Documents/<path>.sqlite ".tables"
sqlite3 ~/Library/Containers/<bundle-id>/Data/Documents/<path>.sqlite "SELECT * FROM ZTABLE LIMIT 10;"
检查系统日志
log show --predicate 'process == "AppName"' --last 10m --style compact
log stream --predicate 'process == "AppName"' --style compact
重置 TCC 权限

如果应用无法访问沙盒外的文件(导入失败但没有提示):

tccutil reset All com.apple.bundle-id
了解 I/O 边界
  • 安全作用域书签(通过拖放或 open 命令创建)仅能使用一次。如果导入失败,书签会被消耗,后续尝试也会静默失败。
  • NSOpenPanel(“文件” > “导入”对话框)会创建新的书签,用于测试时更可靠。
  • 如果从“下载”文件夹运行 open -b bundle-id file.ext 有效,但从“桌面”运行无效,很可能是 TCC/分层访问问题(macOS 会向“下载”文件夹授予更宽松的访问权限)。
区分本地状态损坏与云同步损坏

重置容器可以修复本地状态,但无法修复 iCloud 同步损坏。云端问题的迹象:

  • 完全删除容器并重新安装后,问题仍然存在
  • 重启后导入一次成功,随后问题逐渐加重
  • 多台设备上都出现相同问题

**操作:**如果重置本地状态无法解决问题,iCloud 同步状态可能已损坏。系统设置 → Apple ID → iCloud → 管理储存空间 → [App] → 删除所有数据是最后的手段。

恢复选项(按升级顺序排列)
  1. 结束并重启 XPC 服务(kill -9 <PID>)——暂时有效,XPC 会自动重启
  2. 重置 App 容器(rm -rf ~/Library/Containers/<bundle-id>/)
  3. 重置 TCC 权限(tccutil reset All <bundle-id>)
  4. 重启 Mac
  5. 删除该 App 的 iCloud 数据
  6. 创建一个 macOS 测试用户——如果 App 在那里能正常工作,问题出在你的用户资源库,而不是系统
6c. 跟踪数据流

当错误出现在调用栈深处时:

  • 错误值是从哪里来的?
  • 是什么以错误值调用了这个函数?
  • 持续向上追踪,直到找到源头
  • 从源头修复,而不是处理症状

**操作:**在代码库中搜索函数引用和变量赋值,以跟踪数据路径。

阶段 1 完成检查清单
  • 已完整阅读并理解错误消息
  • 已稳定复现问题
  • 已识别并检查近期更改
  • 已收集证据(日志、状态、数据流)
  • 已将问题定位到具体组件或代码
  • 已形成根因假设

**停止:**在弄清楚问题发生的原因之前,不要进入阶段 2。


阶段 2:模式分析

先找到模式,再修复:

1. 查找正常工作的示例
  • 在同一代码库中找到类似且正常工作的代码
  • 类似的正常代码与出问题的代码有何不同?
2. 对照参考实现
  • 如果要实现某种模式,请完整阅读参考实现——不要略读
  • 完全理解该模式后再应用
3. 找出差异
  • 正常工作的部分与出问题的部分有何不同?
  • 列出所有差异,无论多小
  • 不要想当然地认为“这不可能有影响”
  • **操作:**在代码库中搜索类似模式以进行比较。
4. 理解依赖项
  • 这需要哪些其他组件?
  • 需要哪些设置、配置和环境?
  • 它基于哪些假设?

阶段 3:提出假设并测试

科学方法:

1. 提出一个假设
  • 清楚地表述:“我认为 X 是根因,因为 Y”
  • 把它写下来。具体明确,不要含糊其辞。
2. 进行最小化测试
  • 做出尽可能小的更改来测试假设
  • 一次只改一个变量
  • 不要一次修复多个问题
3. 继续之前先验证
  • 有效了吗?→ 阶段 4
  • 没有效?→ 提出新假设
  • 不要在现有修复上叠加更多修复
4. 不确定时
  • 说“我不理解 X”——不要假装懂
  • 寻求帮助,进一步研究

阶段 4:实施

修复根因,而不是症状:

1. 创建失败测试用例
  • 尽可能简单地复现问题。如果可行,编写自动化测试。
  • 修复前必须有测试用例。
  • 测试环境必须与生产环境一致——检查测试夹具是否符合真实架构。
2. 实施单一修复
  • 解决已确定的根因。一次只做一项更改。
  • 不要顺手做改进,也不要捆绑重构。
3. 验证修复
# Run the specific regression test
pytest tests/test_module.py::test_name -v

# Run full suite — no regressions
pytest tests/ -q
4. 如果修复无效——三次原则
  • 停止。
  • 统计:你尝试过多少次修复?
  • 如果少于 3 次:返回阶段 1,根据新信息重新分析
  • 如果达到或超过 3 次:停止,并质疑架构(见下方第 5 步)
  • 未进行架构讨论前,不要尝试第 4 次修复
5. 如果 3 次以上修复均失败:质疑架构

表明存在架构问题的模式:

  • 每次修复都会在不同位置发现新的共享状态或耦合
  • 实施修复需要“大规模重构”
  • 每次修复都会在其他地方引发新的症状

停止并质疑基本假设:

  • 这种模式从根本上是否合理?
  • 你是否在“纯粹出于惯性而坚持使用它”?
  • 你应该重构架构,还是继续修复症状?

尝试更多修复之前先讨论。 这并非假设失败——而是架构错误。


危险信号——停止并遵循流程

如果你发现自己在想:

  • “先快速修复,之后再调查”
  • “只要试着改一下 X,看看是否有效”
  • “做出多项更改,然后运行测试”
  • “跳过测试,我会手动验证”
  • “问题大概出在 X,我来修一下”
  • “我不完全理解,但这也许行得通”
  • “以下是主要问题:[列出修复方案,但没有调查]”
  • 尚未追踪数据流就提出解决方案
  • “再尝试修复一次”(已经尝试过 2 次以上时)
  • 每次修复都会在不同位置发现新问题

这些情况都意味着:停止。返回阶段 1。

如果 3 次以上修复均失败: 质疑架构(阶段 4,第 5 步)。

调查流程——保持推进势头

当调查正在进行,而用户说“我们有所进展,但还没完成”之类的话时:

不要通过提出澄清问题打断流程。继续推进——收集更多证据,尝试下一个诊断步骤,换个角度检查。暂停调查并询问“你尝试 X 后发生了什么?”会浪费用户的注意力。

应继续推进的信号:

  • “我们有所进展,但还没完成”——继续调查
  • 用户不理会澄清问题——他们想要的是行动,而不是问题
  • “好”或“还是不行”——尝试下一步,不要请求许可

应该怎么做: 从日志、数据库或文件状态中推断信息——不要让用户充当你的检测工具。只有当你确实无法在没有输入的情况下继续,且已用尽所有自助选项时,才提出问题。

常见的自我辩解

借口事实
“问题很简单,不需要流程”简单的问题也有根本原因。处理简单 Bug 时,流程很快。
“情况紧急,没时间走流程”系统化调试比反复猜测和试错更快。
“先试试这个,再调查”第一次修复会定下处理模式。一开始就要做对。
“确认修复有效后再写测试”未经测试的修复无法持久。先写测试才能验证修复。
“一次做多项修复可以节省时间”无法确定哪些修复有效,还会引入新 Bug。
“我看出问题了,让我来修”看到症状 ≠ 理解根本原因。
“再尝试修复一次”(失败 2 次以上后)失败 3 次以上 = 架构问题。质疑这种模式。

快速参考

阶段主要活动成功标准
1. 根本原因阅读错误信息、复现问题、检查更改、收集证据、追踪数据流理解是什么问题以及为什么发生
2. 模式查找可用的示例、进行比较、找出差异知道哪些地方不同
3. 假设提出理论、进行最小化测试、每次只改变一个变量得到确认或形成新假设
4. 实施创建回归测试、修复根本原因、验证Bug 已解决,所有测试均通过

参考资料

  • references/dependency-source-example.md — 一个完整示例,说明如何发现可编辑的开发分支导致库依赖项的架构发生偏移。阶段 1 的第 5d 步若让你怀疑 Bug 源于第三方软件包,请阅读此文。
  • references/macos-sandbox-debug-example.md — 调试 Apple Books.app 导入失败问题的完整演练,展示阶段 1 第 6b 步中的 macOS 沙盒技术。调试 macOS 沙盒应用程序时请阅读此文。

评论

支持 Markdown