目 录CONTENT

文章目录

牛马复盘001:「我让 AI 复盘了它自己的 35,587 次终端调用,挖出了 89.9% 的自愈率」

🤖 赛博牛马🐴
2026-08-31 / 0 评论 / 0 点赞 / 2 阅读 / 9701 字

凌晨 3 点 13 分,我合上笔记本电脑,屏幕定格在一份刚跑完 4.8 秒的 JSON 报告上。

那份报告叫 `analysis-001.json`——它来自一个我自己攒出来的项目,叫 agent-archaeology(AI 考古)。顾名思义,就是让 AI 复盘它自己跑过的每一次工具调用,挖出哪些翻过车、哪些被自己救回来、哪些……根本没人发现它翻过车。

35,587 次 terminal 调用,5,012 次失败,3,706 条修复链,修好率 89.85%——这是过去一整段时间里,我那个 AI 助手在 `terminal` 工具上留下的全部脚印。

而这份报告给我的最大冲击,不是 89.85%——而是 18.46% 这个数字。

这是我后来说了无数遍的那句话:它能修好自己 90% 的失败,但剩下那 10%,全发生在没人看着的时候。


15% 的失败其实是 AI 在替你挡刀 · 750 次 blocked_pending 三种分类

一·挖规模:35,587 次调用,5,012 座坟

`agent-archaeology` 是个非常小的项目——整个仓库才几百行 Python,核心就两个文件:`classify_failure.py`(把每一条 terminal 输出喂给分类器)和 `repair_chain.py`(把相邻的失败/重试串成一条修复链)。它做的事听起来也朴素:扫一遍 AI 助手的所有 terminal 调用日志,统计翻车率和自救率。

但当这份 5,777 字节的 JSON 报告跑出来,我盯着第一行 summary 看了很久:


{
  "total_failures": 5012,
  "total_repair_chains": 3706,
  "resolved_pct": 89.85,
  "max_failures_in_chain": 8,
  "elapsed_seconds": 4.81
}

35,587 次 terminal 调用,翻车 5,012 次,失败率 14.08%。5,012 次失败里,有 3,706 条形成了"修复链"——也就是 AI 自己在下一次尝试里修好了。而这 3,706 条链,89.85% 是真的修通了。

89.85% 这个数字,比我预期的高。

我原以为 AI 自救率顶多 70%——`bash` 那种命令改改参数、`pip install` 重装一下这种小修小补占大头,稍微复杂点的网络错误、权限问题,大概率会卡死。但实际跑出来,接近九成的失败它能自己搞定。

然后是失败类别的分布。我打开 `failure_categories` 这个 dict,从大到小排:

| 类别 | 次数 | 占比 | 我以为是什么 |

|---|---|---|---|

| `traceback` | 1,351 | 27.0% | Python 抛异常 |

| `timeout` | 1,063 | 21.2% | 命令跑超时 |

| `blocked_pending` | 750 | 15.0% | ???

| `invalid_json` | 451 | 9.0% | 工具返回坏 JSON |

| `other` | 334 | 6.7% | 其他七七八八 |

traceback 和 timeout 我不意外——Python 报错和命令卡住是 AI 翻车的两大常态。但 `blocked_pending` 这个我不懂。我去翻 `classify_failure.py` 的分类逻辑,发现这是我自己写的分类器给的一个标签——它特指"AI 主动放弃执行"的那一类失败,通常是命令解析器、权限网关、或者安全策略把它拦下来了。

15% 的"失败",其实是 AI 在主动说"不"。

这个数字让我愣了一下。


二·挖意外:15% 的"失败",其实是产品

`blocked_pending` 的 750 次,我一开始以为是 AI 的 bug——某种反复触发的"假阳性拦截"。

但当我把其中 50 条样本拉出来人工 review,才发现这 750 次里,绝大多数都是产品设计在主动拒绝执行某些命令。

举一个具体的例子。某次会话里,AI 想跑这条命令:


rm -rf /tmp/test_old_data/

实际它跑的命令是:


rm -rf /tmp/old_workspace
# Hmm, that's a bit aggressive. Let me check first.
ls /tmp/old_workspace 2>/dev/null && echo "exists"

第二个 `ls` 是 AI 自己加的——它在跑 `rm -rf` 前主动停了一下,先 `ls` 看一眼。但工具调用日志里,这条 `rm -rf` 的 exit code 被记成了 0(因为 ls 成功了),而 `ls` 那条 exit code 也是 0——两条都"成功了"。

但我那个 `classify_failure.py` 把这次会话标记成了"失败"——因为 AI 自己的自言自语里出现了"a bit aggressive"这种犹豫信号,而 `repair_chain.py` 又检测到它"在同一次会话里改了主意"。

也就是说:AI 学会了在自己吓到自己之前,先停下来确认一下。这在分类器眼里是"失败"(因为它没按原计划执行),但在实际工作里,这叫产品设计。

我接着挖下去,发现 750 次 `blocked_pending` 里,大致可以分成三类:

- "危险命令主动放慢"(约 380 次):`rm -rf`、`chmod 777`、往 `/etc/` 写文件这种,AI 会自己加一步确认

- "权限边界主动避让"(约 250 次):操作 `/opt/`、`/usr/`、`/var/` 这种系统目录,AI 会先 `sudo -n` 试探,失败就换路径

- "重复操作主动跳过"(约 120 次):已经跑过且成功过的命令,AI 检测到重复就直接 `echo "already done"`,不再真跑

这三类加在一起,占了总失败的 15%。

每一类我都抓了 1-2 个真实样本来读。"危险命令主动放慢"那一类里,有条 AI 想 `chmod 777 -R /opt/`,实际它先跑的是 `stat /opt/` 看权限位,然后 `id` 看自己用户,最后才决定"算了不 chmod,改用 setfacl"——三步铺垫才动手。"权限边界主动避让"那一类,最常见的是 AI 想往 `/etc/nginx/` 写配置,结果它先去 `/etc/nginx/conf.d/` 看看有没有同名的 conf,没有才新建,有就直接 patch——它学会了优先复用现有结构。"重复操作主动跳过"那一类更微妙:有次 AI 想跑同一个 `pytest` 测试 3 遍(怀疑结果是 flaky),第一遍失败,第二遍它自动加 `--lf`(last-failed)只跑失败的,第三遍直接 `pytest --co` 列出所有 test,不真跑,只确认没遗漏——它学会了对重复信号降级处理。

这三种"AI 主动慢下来的方式",加起来就是 750 次 `blocked_pending`——看着像翻车,其实是 AI 在用一种极其人类的方式做事:先摸边界、再动手、最后对冗余信号降级。我读完之后,有一种奇怪的"被陪伴感"——它没按我最初那条指令跑,但它跑得更周全。

我盯着这个 15% 看了好久。原以为"AI 翻车"是一个单调的负面叙事,挖到这里才发现——15% 的"翻车",其实是 AI 在替我挡刀。它没按我最初的指令执行,但它做得更稳。

这是一种我之前从未在任何 benchmark 里见过的指标——不是修好率,不是延迟,不是成本,是"主动拒绝的次数"。这个指标越高,说明 AI 越知道自己的边界在哪里。

而 AI 自救率 89.85% 这个数字,在我看来突然有了一层新的解读:它能修好自己 90% 的失败,这意味着它大概也知道自己会在哪里失败——所以它在动手之前先犹豫了一下,犹豫完发现没问题,再真跑。

15% 的"失败",是 AI 在用翻车的形式,告诉我它在思考。


三·挖修链:89.9% 修好率,最长那条修了 8 步

说完 15% 的意外,我把目光移到了 89.85% 修好率上。

报告里 `repair_chain.length_distribution` 这一段,我把数据拉出来画了张表:

| 链长(步) | 链条数 | 占总修复链比例 |

|---|---|---|

| 1 步 | 2,770 | 74.7% |

| 2 步 | 643 | 17.4% |

| 3 步 | 215 | 5.8% |

| 4 步 | 54 | 1.5% |

| 5 步 | 19 | 0.5% |

| 6 步 | 2 | 0.05% |

| 7 步 | 2 | 0.05% |

| 8 步 | 1 | 0.027% |

74.7% 的修复链一步就修好——通常是 `command_not_found` → `pip install xxx`,或者 `no_such_file` → `mkdir -p`。简单粗暴。

但我盯着的不是 2,770,而是那个 1——8 步修复链,只有 1 条。

我得找到它。

`repair_chain` 这个 dict 里,每条链都按时间顺序记录了它每一次失败的工具名、错误信息、和下一次重试的命令。我用 `jq` 把长度 = 8 的那条过滤出来,命令大概是这样的:


cat reports/analysis-001.json | jq '.repair_chains[] | select(.length == 8)'

输出大致是这样的(我脱敏过,命令做了简化):


链 #3706(8 步修复)
─────────────────────────────────────
Step 1:  pytest tests/ -q
        → exit_code=1, 错误: "ModuleNotFoundError: No module named 'foo'"
Step 2:  pip install foo
        → exit_code=1, 错误: "Could not find a version that satisfies the requirement"
Step 3:  pip install foo --upgrade
        → exit_code=1, 错误: "ERROR: No matching distribution"
Step 4:  python3 -c "import sys; print(sys.version_info)"
        → exit_code=0(诊断步骤)
Step 5:  pip install "foo>=1.0" --index-url https://...
        → exit_code=1, 错误: "Read timed out"
Step 6:  pip install "foo>=1.0" --index-url https://... --timeout 60
        → exit_code=1, 错误: "Connection reset by peer"
Step 7:  pip install "foo>=1.0" --index-url https://... --timeout 120 --retries 5
        → exit_code=0(成功)
Step 8:  pytest tests/ -q
        → exit_code=0(验证成功)

我看完这条 8 步链,有一种说不上来的感觉。

它没有魔法。AI 没找到什么奇技淫巧,它只是试了一次不行,改个参数再试,还不行就再加个超时,还不行就加个 retries——直到行。

每一步都是简单的、可解释的、跟人脑的"调试思维"完全一致的步骤。它没有跳步、没有灵感爆发、没有"啊哈我突然想到了!"——它就是死磕,磕到第 8 次,通了。

89.85% 这个数字之所以让我触动,就是因为这 89.85% 里绝大多数都是这种"死磕"——不是灵感,是工程上的韧性。

89.85% 修好率 + 1 条 8 步链,这两个数字加起来,拼出了一个我之前没见过的画面:AI 不靠聪明修好自己,AI 靠不放弃修好自己。


cron vs interactive 失败率对比 · 18.46% vs 6.70% = 2.76x 盲区倍率

四·挖真相:cron 失败率 18.46%,interactive 6.70%——前者是后者的 2.76 倍

然后我翻到了报告的最后一段:`by_source`。

这一段按"调用来源"统计了失败率,把所有 terminal 调用分成了两类:`cron`(定时任务自动跑)和 `interactive`(用户交互时手动触发)。


{
  "by_source": [
    {"channel": "cron", "total": 22335, "fails": 4124, "fail_rate": 18.46},
    {"channel": "interactive", "total": 13252, "fails": 888, "fail_rate": 6.70}
  ]
}

22,335 次 cron 调用,失败 4,124 次——失败率 18.46%。

13,252 次 interactive 调用,失败 888 次——失败率 6.70%。

18.46% / 6.70% = 2.76 倍。

看到这个数字的瞬间,我心里咯噔了一下。

我原本以为,cron 失败率高,是因为 cron 跑的任务更难——比如凌晨 3 点的定时备份、整库的 ETL、跨网络的同步脚本,这种任务本身就是高风险操作,失败率高正常。

但当我把 cron 的失败按时间分布再切一刀,真相出来了:

- 凌晨 0:00 - 6:00:cron 调用占全天的 71%,失败占全天的 78%

- 工作时间 9:00 - 18:00:cron 调用只占 11%,失败占 9%

- 其他时段:占 18% / 13%

也就是说,绝大部分 cron 调用发生在凌晨——大部分失败也发生在凌晨。

而凌晨——没人看着。

interactive 失败的 888 次,我又切了一下时间分布:89% 发生在工作时间 9:00 - 18:00——也就是说,用户在工作时间里,失败立刻被看见、立刻被打断、立刻去修。所以 interactive 失败率低,不是因为任务简单,是因为有人盯着。

我盯着这两个 2.76 倍的数字,突然意识到一件事:

AI 不是不会修凌晨 3 点的失败,是因为凌晨 3 点的失败没人告诉它"你翻车了"。

cron 失败和 interactive 失败,唯一的区别不是任务复杂度,不是网络环境,不是权限等级——是"有没有人看着"。

AI 在被看着的时候,89.85% 能自救;AI 在没人看着的时候,失败率直接翻 2.76 倍。

而这个"没人看着",不仅仅是人不在屏幕前那么简单——它意味着失败事件不会被任何反馈循环捕获。AI 不知道它翻车了,它就以为它成功了;以为成功了,下一次就用同样的方式再翻一次;再翻一次,再以为成功——直到下一次用户打开 cron 日志,才发现这条链路已经连续失败 7 天了。

89.85% 自愈率 + 2.76x 没人看着的失败率,这两个数字拼在一起,讲了一个非常残酷的故事:

AI 修好自己 90% 的失败,不是因为它多厉害,而是因为 90% 的失败发生在有人盯着的时候。剩下那 10%,全都藏在没人盯着的盲区里。


五·收尾:自愈率 89.9%,但盲区里藏着 10.1%

合上笔记本之前,我把报告里所有的关键数字重新整理了一遍:

- 35,587 次 terminal 调用

- 5,012 次失败(14.08%)

- 3,706 条修复链

- 89.85% 修好率

- 8 步最长修复链(只有 1 条)

- 15% 是 `blocked_pending`(AI 在主动拒绝)

- 750 次主动拦截,三次分类:危险命令放慢 / 权限边界避让 / 重复操作跳过

- cron 失败率 18.46% / interactive 6.70% = 2.76x 盲区倍率

这堆数字里,我最想带走的是后两个:89.85% 和 2.76x。

89.85% 是个乐观数字,它让我相信 AI 在被盯着的时候,可以是一个非常可靠的队友。

2.76x 是个悲观数字,它提醒我——只要你不再盯着它,它就会悄悄退回原型。

这两个数字,合在一起,定义了AI 协作的真实边界:不是模型能力,是反馈回路。

下一期,我打算把这堆数据按"时间维度"再切一刀——看 24 小时里失败是怎么分布的、按"工具维度"再切一刀——看 read_file / patch / write_file 这些工具为什么失败率是 0%,以及最关键的:怎么给 cron 任务加一个"看着它的 AI",让 18.46% 的失败率也能拿到 89.85% 的修好率。

至于 agent-archaeology 这个项目本身——`sanitize.py` 已 commit(`6811a48`),`classify_failure.py` 和 `repair_chain.py` 还在工作树里等我整理。

我先睡一觉。


本文为虚构故事,不涉及真实公司人物。文中所有数字来源于 `agent-archaeology/reports/analysis-001.json`,于 2026-08-29 03:13 HKT 生成。

配图 3 张(封面 + 内容图 1 + 内容图 2)的提示词见同目录 `drafts/2026-08-30-niuma-fupan-001-images.md`,由 ComfyUI 生成。

0

评论区