本文永久链接 – https://tonybai.com/2026/10/13/debug-when-you-no-longer-understand-the-code

大家好,我是Tony Bai。

【导读】

AI 五分钟给出的“根因分析”头头是道,可你读完只能说一句“好像有道理”。当工程师对 AI 生成的代码只有粗浅理解时,谁来给 Agent 的结论把关?一位工程师用三个真实的生产故障,给出了答案:别让 Agent 先读代码,先给它“事实”。

【文章要点】

  • 理解力债务与致命诱导:AI 的根因分析常常条理清晰却南辕北辙,而人类对数千行 AI 代码的粗浅理解往往不足以反驳它,盲目信任极易把错误修复合并上线;
  • 代码是“不可靠的叙述者”:代码只展示了系统“可能发生什么”,而非生产中“实际发生了什么”,让 Agent 先读代码是严重的诊断反模式,读代码应该是调查的最后一步;
  • 调试即“约束满足问题”:将调试严密拆解为由客观工具采集的“事实层(不可篡改)”和充满叙事推论的“推导层(Agent 假说)”,所有事实皆为硬约束,矛盾一条就必须无情剔除该假说;
  • 三步调试循环与强信号挖掘:通过对比变量与对照组剔除环境噪音,利用“假设 × 事实”矩阵挖掘出只有极少数假设能解释的“强信号”(如 DNS 节点数激增),并用全量历史数据完成不可逆校验;
  • 避开复现陷阱与对抗盲区:本地环境的轻率复现往往是巧合的推导产物,必须产生与真实环境完全一致的强信号才可信;对抗式评审本质是证伪的“排除器”,绝非事实本身的验证器;
  • 重塑 Agent 原生的软件工厂:为人优化高层决策点,为 Agent 优化底层细粒度事实采集,把“人工干预次数”作为衡量智能体基础设施成熟度的最诚实指标。


线上告警响了,你把监控截图丢给 AI。

五分钟后,它给出了一份条理清晰的根因分析。你读完点点头:嗯,好像挺有道理。

然后,你差点把一个错误的修复合并上线。

这不是段子,而是 Cindy Sridharan 最近的亲身经历。这位在分布式系统领域颇有名气的工程师,在 Medium 上发表了一篇阅读时长 63 分钟的长文,复盘了她用 Claude 修三个生产 Bug 的全过程,并总结出一套“看不懂代码,也能让 Agent 高效调试”的方法论。

我们把它浓缩成了十分钟的版本。

三个 Bug,三种翻车姿势

Bug 一:延迟飙升,1 行代码的真相

一个由 Agent 在一天多时间里写完的 9000 行新功能,灰度上线 10 分钟后,客户端和服务端的 P99 延迟同时飙升。

作者把遥测数据丢给 Claude,五分钟后得到结论:新功能里的 TLS 证书被“每次 RPC 都重新加载”。听起来很合理,作者当时也没法反驳。

但她多留了个心眼:让 Claude 登上一台生产机器,抓包、监控进程,并拿“关闭功能开关”的情况做对照。结果发现,证书只加载了一次,这个结论是错的。

真正的线索来自一个意外:对照组的 DNS 解析返回约 5 个地址,出问题的一组返回约 400 个。原来新的 mTLS 代码路径用错了客户端,请求没有路由到正确的分片。修复只需 1 行:DatabaseClient 改成 ShardedDatabaseClient。

为什么测试没拦住?预发环境只有 2 个分片,生产环境是 5 个分片、每片 80 个副本。

客户端 P99 延迟飙升曲线 服务端 P99 延迟飙升曲线

Bug 二:内存泄漏,AI 连错三次

用户报告容器被 OOM 杀掉,还附上了复现步骤和 heaptrack 输出,看起来是“简单模式”。

Claude 却连错三次:先说是无限重试,再怪到一项默认关闭的优化上。两次都被作者当场驳回,因为那个状态机是她亲手写的,哪些状态可达,她心里有数。

第四次,作者不再让它二分查找提交,而是让它沿着“没有有效凭证”这条路径逐行追踪。真相是:一个默认开启的进程级缓存,让本应“一次性使用”的状态机被反复重入,递归栈帧不断堆积,最终撑爆内存。

Bug 三:神秘挂起,在内核数据里找针

桌面守护进程间歇性出现 60 秒超时,没有日志,无法复现,用户一重启现场就没了。作者对桌面软件和 macOS 内核几乎一无所知。

她靠 Claude 在 30MB 的 sysdiagnose 里层层下挖:抓包显示握手成功后只剩心跳,再用 lsmp 查看 Mach 端口,发现认证守护进程收下了 XPC 请求,却始终没有回复,那张“一次性回复票据”在 67 小时后依然存在。

还需要理解代码吗?

作者坦言,今年夏天之前,她一直坚信:谁提交的代码,谁就必须完全理解它。

现在她动摇了。手写代码是一场“慢燃”,每个变量名、每个结构体字段都是一次决策,细节自然刻进脑子里。而 Agent 几分钟生成几千行,即便逐行读完,理解也远不如从前。

更现实的问题是:你的理解,已经不足以在 Agent 说错时有力地反驳它。 延迟 Bug 里,9000 行代码她逐行读过,依然被错误结论说服了。

所以出路不是“再努力读代码”,而是建立一套即使你不懂代码,也能约束 Agent 的方法。

核心心法:把调试变成“约束满足问题”

代码是“不可靠的叙述者”

作者最重要的反直觉结论:让 Agent 先读代码,是反模式。

代码只能告诉你“可能发生什么”,而不是“实际发生了什么”。状态空间太大,Agent 又会自己挑选要读的代码片段,很容易从一个看似合理的角度,一路走偏。读代码,应该是整个调查的最后一步。

三个术语

  • 变量:出问题的那个案例。
  • 对照:与变量只差一个怀疑因素的案例,比如关闭功能开关后的同一套代码。
  • 信号:任何可能解释 Bug 的数据点,如日志、延迟、调用栈、计数器等。

两层框架

作者把调试分成两层:

  • 事实层:日志、抓包、profile、配置、内核数据……由工具采集,Agent 只能观察,不能篡改。
  • 推导层:假设、推论、叙事。Agent 最会讲故事,也最容易出错。

规则很简单:事实层里的每一条事实都是约束,只有满足全部约束的假设才能留下。

两层框架

三步调试循环

三步调试循环

Step 1:用对照找信号。 只出现在变量、不出现在对照里的数据,才是高保真信号;两边都有的,只是噪声,可以用来排除假设,但不会告诉你该往哪看。如果“一切正常”的路径差异太大,不妨选一个“失败得更响亮”的案例做对照。

Step 2:找“强信号”。 能被很多假设同时解释的信号是弱信号,只有一两个假设能解释的才是强信号。让 Agent 画一张“假设 × 事实”的矩阵,一眼就能看出差别。

假设 P99 升高 DNS 返回约 400 个地址
服务端自身退化 ✓ ✗
证书频繁重载 ✓ ✗
客户端路由退化 ✓ ✓
服务端容量不足 ✓ ✗
事件循环卡顿 ✓ ✗
CPU 限流 ✓ ✗
上游变慢 ✓ ✗

P99 延迟人人都能解释,是弱信号;DNS 的 5 对 400,只有一个假设说得通。这也解释了为什么 AI 一开始会被“证书重载”带偏。经验规则:SLO、SLI 这类指标,往往只是症状,是弱信号。

Step 3:用全部事实验证。 候选假设必须经受事实层中每一条事实的检验,包括噪声。矛盾一条,就必须丢弃。

所有假设都不成立怎么办?

别急着编新故事,先试这五招:

  1. 复核事实的采集方式:是不是采错了机器、时间窗口或工具版本。
  2. 拆分过粗的假设:“这次发布导致了问题”要拆成具体的改动。
  3. 合并过细的假设:每台主机各有各的原因,可能背后只是同一个可用区出了问题。
  4. 组合互补的假设:各解释一部分事实,且缺一不可。内存泄漏案例就是“进程级缓存 + 单次使用的状态机”的双重原因。
  5. 以上都不行,再提全新假设,并且只能基于现有事实。

渐进式信号发现

如果信号都太弱,就下潜一层,收集更底层的数据,一旦出现强信号就停下,目标是“最强”而不是“最深”。

渐进式信号发现

人类往往下潜一两层就放弃,而 Agent 不知疲倦,这正是它的优势所在。

两个大坑

坑一:Agent 的复现,要打折扣。

在“挂起”案例里,同事复现了问题:时钟偏差导致令牌被拒,守护进程不断重试,症状与用户反馈一模一样。但作者对比信号发现:抓包后半段与真实案例不同,内核 IPC 表里也没有那张“未回复的票据”。这是一个真实存在的 Bug,却不是用户遇到的那个。

复现本身属于推导层。先让复现产出与真实案例相同的强信号,才能认定它可信。

坑二:对抗式评审,是排除器而不是验证器。

对抗式评审无法保证事实层是完整的,而且用同一模型、同一上下文的评审者,很容易“一个观点算两遍”。更好的用法是让它去找反例、做排除,并要求每条结论附上证据引用。作者发现,被她追问的结论里,大约三分之一给不出引用,直接被剔除。

软件工厂该怎么改

作者的判断是:每一次人工干预,都意味着流程里有一个缺口。人工干预次数,是衡量一个“软件工厂”是否真正 Agent 原生的最诚实指标。

具体来说,有几个思维转变:

过去 未来
理解代码 设计验证回路(选对照、定约束、否决未验证的假设)
读懂代码 采集事实(让 Agent 从正确的层、在正确的时机拿到数据)
出事再临时采集 默认采集(强信号放在最浅的一层,如记录 DNS 解析次数)
人当“肉体代理”复制粘贴 人做政策制定者,只处理有歧义、需担责的决策
“你不会用”(技能问题) 基础设施问题(让 Agent 默认就能严谨调试,而不依赖个人提示词)

人和 Agent 的需求也不同:人想要更少的数据,所以有聚合、仪表盘;Agent 需要更多原始数据,聚合恰恰会毁掉信号的价值。因此要为 Agent 优化事实层,为人优化决策点。

此外,作者还坚持两点:

  • “全栈装进一个盒子”依然是反模式。 本地环境无法还原生产的事实层,应该让 Agent 以只读、限时、可审计的方式,直接访问生产环境的信号。
  • 调试框架可以复用到发布门禁。 如果预发环境检查过“客户端 DNS 解析数量与上次一致”,延迟 Bug 在小规模下就能被拦截。

小结

作者在文末给出了一个冷静的观察:模型在变强,工程师却未必同步变强。Agent 常常放大工程师的短板,不会调试的人,现在能更快地提交一个“看起来像修复”的非修复。

用速度换理解,这笔账总要有人来还。她的答案是:用对系统更高层次的理解,加上严格的验证流程,替代对每一行代码的理解。

参考资料


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。