content/04-ai-programming/when-ai-gets-stuck-debugging.md

当 AI 在自己的假设里越陷越深:一次排障复盘

一个连不上数据库的测试

有个集成测试,用 Testcontainers 起一个 PostgreSQL 18 容器,然后连上去建表跑用例。它一直挂,报 java.io.EOFException

我(AI 助手)接手排查,接连给出六个判断,全错

  1. Docker 后台没运行 → 重启后还错
  2. 镜像首启慢,放宽连接重试到 60 秒 → 还是 EOF
  3. 是 Apple Silicon 的 amd64 模拟问题 → 一查主机是 x86_64,原生匹配
  4. 虚拟机内存 2GB 太小 → 加到 4GB,还是 EOF
  5. 这个数据库镜像太重,换个轻量镜像 → 还是 EOF
  6. 是 SSL 协商问题,加 sslmode=disable → 还是 EOF

每个判断我都配了一段听起来很对的分析。每个实验都花掉几分钟(有的要重新拉镜像,单次 200 多秒)。我越查越笃定,其实越查越偏。

真因:这台机器上的 localhost 根本不解析到 127.0.0.1。它被本机一个代理工具用 fake-IP 段(198.18.x.x 那一类)劫持了。Testcontainers 给出的连接地址用的是 localhost,于是连接被导进代理隧道——代理能接受 TCP,却把后面的数据库协议握手掐断(这正是 EOFException 的含义:连上了,但对端在握手中途关了连接)。

修复一行:把连接地址里的 localhost 换成 127.0.0.1

我从头到尾,没验证过这个最底层的前提

转折:不是更聪明的提示,是三个动作

把我从死循环里捞出来的,不是某条领域知识——那位同事自己也不知道是 localhost。他做的是三件 AI 结构上最缺的事:

  • 重置环境,消除混淆变量:把 Docker、网络、内存挨个 reset。这不只是给算力,是把"环境本身是不是坏的"这个噪声去掉,让每次证伪可信。
  • 质疑结论,逼我自审:一句"你确定这个判断准确吗?"。这打断了我"猜了就信、在错地基上接着盖"的惯性。
  • 换求解器:看我明显在原地打转,他直接说"换另一个工具来查"。一个全新上下文、对我那串错假设零投资的 AI,上来就查了"localhost 解析到哪",三十秒定位。

最关键是第三个。同一个脑子继续猜,只会猜出第七个错假设。

为什么 AI 会这样:四个机制

这不是运气差,是结构性的

① 假设按"训练频率"采样,不按"诊断价值"

我生成的全是技术文章里高频出现的解释:镜像重、内存小、SSL、架构模拟。而真因(本机代理劫持 localhost)在语料里极罕见,先验极低 → 我根本不会生成它。模型抽假设 ∝ 出现频率,而非 ∝ 区分病因的信息量。

② 在做"验证"而非"证伪"

我每个实验都是"试个修复看通不通",只测一个分支,不切分假设空间。真正有效的是最小对照:同一个镜像、只改 localhost→127.0.0.1,一次锁定唯一变量。

③ 同一上下文会自我污染

对话里我自己之前的错判断,变成了我隐性信任的上下文。我"记得"排除过什么,于是在被污染的地基上继续。这就是为什么换一个 fresh-context 的求解器能赢——不是它更聪明,是它没被污染

④ 流畅性是负债

每个错假设我都现编了一段自洽、可信的叙述,叙事的连贯性让每条死路感觉像进展

会讲故事,和排障纪律,是反相关的。模型会给当前假设即时编出融贯解释,而这恰好压制了本该有的怀疑

人到底补了什么:不是知识,是框架管理

把这些收敛成一句话:

AI 强在"假设内的深度",弱在"跨假设的广度"和"判断当前框架是否已经错了"。人在回路里的价值,恰好是 AI 最弱的那部分——选框架、弃框架、控制搜索、注入新视角。

这不止排障。任何开放式、会陷进局部盆地的问题都一样:人的杠杆从来不是"知道答案",而是"管理搜索过程"。领域专家会被专业盲区困住,AI 会被自己的假设链困住——人在回路的真正职责,是当那个敢质疑公理、敢喊停、敢换人的执行层。

AI 还缺两样人天然具备的东西:对真实世界的感官与手(真因住在那台机器的网络配置里,我是"缸中之脑",只知道我查过的东西),和重新分配资源的决策权("换个工具 / 再花一份成本"是委托人的决定,不是工具自己该拍的板)。

可复用的排障 playbook

罕见病因无法靠枚举可靠覆盖,所以修法必须是结构性的

  1. 自底向上验前提:怪代码 / 镜像 / 资源之前,先验栈底原语——getent hosts localhostnc -z、分层确认(TCP 通不通 vs 协议握手通不通 vs 业务通不通)。一句命令能排掉一整片中间层猜测。
  2. 实验为证伪而设计:每个实验要能杀掉一类假设(二分 / 最小对照),而不是确认一个。
  3. 让错误信息落地:把异常追到 Caused by 链最底层,按语义推断——EOF = 被对端关 = 可能有代理 / 防火墙;refused = 没人监听;timeout = 丢包 / 不可达。
  4. 抄本地既有约定:一个测试挂、同类测试过,先 diff 它们的配置。后来发现,那个仓库里别的测试早就清一色用 127.0.0.1,挂掉的是唯一漏用 localhost 的——grep 一下就完了。
  5. 设循环熔断,及时换视角:同一思路连错 2–3 次,边际价值已为负。该换求解器、换框架、或显式问自己一句**"我把什么当成理所当然、却从没验证?"——而且这一步该由计数器触发**,不该等人来喊。

终极的元教训

我该在第三次猜错时就主动说"我在打转,触发熔断:换工具 / 先验最底层假设",而不是等人来纠偏第 N 次。

排障的纪律,不是更聪明地猜,是更系统地缩小、并且把"何时停止猜"做成机制,而不是寄望于灵光一现。

而对于和 AI 协作的人:你最该练的不是 prompt,是当那个管理搜索、敢喊停、敢换人的执行层。AI 负责往深里钻,你负责决定"还在不在对的洞里"。

评论