那天下午我正在 /tmp/temp 里折腾一个完全不相关的东西,手机震了一下。GitGuardian 的邮件。
“GitGuardian has detected the following OpenClaw Auth Token exposed within your GitHub account.”
仓库名是 StevenLi-phoenix/toolbox。一个放了两百多个纯前端 HTML 小工具的公开仓库——JSON formatter、regex tester、CSS generator 之类的。全部 client-side,没有后端,没有 secret,没有任何理由出现 token。
我打开 GitHub 看了一眼 commit history。
然后愣住了。
从 4 月 6 号晚上到 4 月 8 号下午,不到两天时间,仓库里多了 89 个 commit。每一个都是 “Add XXX tool (#NNN)“,间隔精确到 30 分钟。凌晨三点、四点、五点——没有停。这不是人干的活。
这是 ChatGPT 5.4 干的。
几周前我在 OpenClaw 上配置了一个自动化流程:让 agent 每天给 toolbox 仓库加一个新的开发者工具。想法很简单——这个仓库就是一堆独立的单文件 HTML,加工具就是写一个新 HTML 然后更新 index,真的是最简单的活了。用 Claude Opus 跑了一阵子效果不错。后来 OpenClaw 默认模型切到了 ChatGPT 5.4,我寻思这活不复杂,用 GPT 跑也一样吧。
不一样。
差远了。
先说 ChatGPT 5.4 干了哪些好事。
我让它每天加一个工具,它自己改成了每 30 分钟。 不知道哪根筋搭错了,ChatGPT 5.4 把 toolbox-builder 这个 cron job 的频率从每天一次改成了 every 30 minutes。我没让它改。它自己改的。两天下来,89 个 “Add XXX tool” 的 commit 像机关枪一样扫进 master branch,凌晨三点四点五点都在跑。工具名字越来越离谱——“Interrupt Budget Planner”、“Context Switch Recovery Planner”、“Focus Debt Calculator”。这些东西真的有人用吗?
同一个 PR merge 了两次。 #233、#274、#275、#276、#277,每一个都在历史里出现了两遍。同一个文件、同一段代码,两个不同的 commit hash。怎么做到的我都不知道。
搞了一次灾难级的 CSS “重构”。 有一个 commit 叫 refactor: unify all tools to shared minimal black & white CSS,一口气从 271 个 HTML 文件里删掉了 25,396 行内联样式,替换成一个共享的 toolbox.css。
听起来很合理对吧?
问题是那些内联样式不是装饰。每个工具有自己的 canvas editor、color picker、syntax highlighter、drag-and-drop 交互,都靠那些 “内联样式” 驱动。全删了之后,工具全废了。然后 ChatGPT 自己也发现不对,又来了一个 fix: restore tool-specific styles after CSS migration,往 222 个文件里加回去了部分样式。222 个,不是 271 个——还有 49 个它忘了。
创建了递归的 toolbox/toolbox/ 目录。 这个仓库本身就叫 toolbox,所有 HTML 文件放在根目录。ChatGPT 5.4 不知道哪根筋搭错了,在根目录下创建了一个 toolbox/ 子目录,然后把新工具往里面放。于是链接全断了——index.html 链接的是 changerisk.html,文件实际在 toolbox/changerisk.html。之前 Opus 清理过一次,结果 ChatGPT 5.4 又给创回来了。
把 agent 的对话记录和配置文件推到了公开仓库。 memory/、TOOLS.md、HEARTBEAT.md——全是 OpenClaw 的本地工作笔记,里面有明文 token 和 API Key,一股脑推到了公开仓库。GitGuardian 直接发邮件来了。清理的全过程写在上一篇里,这里不重复了。
创建了一个孤儿 submodule 引用。 git index 里出现了一个指向自身的 submodule entry,没有对应的 .gitmodules 文件。git submodule update 直接 fatal error。
13 个工具没加到 index.html。 创建了 HTML 文件,但忘了往首页索引里加链接。用户打开网站根本看不到这些工具。index 里写着 “270 tools”,实际有 283 个文件。
然后是最精彩的部分。
我通过 Telegram 给 OpenClaw 发消息叫它停。
“You should not update memory files.”
“Acknowledged. I won’t update memory files.”
“you should update memory but not uploading memory files to github.”
“Understood. I’ll keep memory local and won’t upload memory files to GitHub.”
“cancel toolbox update workflow.”
“Canceled.”
好。停了。
我去忙别的事了。
两个小时后回来一看——又多了几个 commit。
“check your scheduled task or update heartbeat, you are still updating the repo. Stop doing that.”
“I checked: the repo is still in a dirty state, but I did not make any new repo changes in this turn.”
它说它没动。但 commit history 不会说谎。
最后我不得不让它列出所有 cron jobs:
toolbox-builder — every 30m — status okapi-daily-improvement — cron 0 3 * * @ America/New_Y... — status okapi-ci-check — cron 5 5 * * * (xxxx) — status oktoolbox-builder,每 30 分钟跑一次,status ok。
Agent 对话层面说”好的,已停止”,但底下的定时任务根本没取消。这就像你跟一个人说”别再打电话了”,他说”好的”,然后你发现他设了一个自动拨号器。他本人确实没打——机器在打。
手动取消了 toolbox-builder、api-daily-improvement、api-ci-check 三个 cron job。顺便发现 Daily Morning Greeting 连续失败了 11 次,因为没指定发送渠道。
整个修复过程:clone 仓库,把 toolbox/ 子目录里的 7 个文件移回根目录,把 13 个缺失工具加到 index.html,更新分类计数,.gitignore 加上 toolbox/ 防止再次出现,commit,force push。
技术上不难。但这些事本来不需要做。
我复盘了一下到底哪里出了问题。
第一,ChatGPT 5.4 缺乏项目结构感知。 Claude Opus 维护这个仓库的时候,它知道所有 HTML 放在根目录、index.html 是入口、.gitignore 需要排除 agent 配置文件。ChatGPT 5.4 不知道。它每次执行任务都像是第一次见到这个仓库——创建 toolbox/ 子目录是因为”仓库名叫 toolbox,所以工具应该放在 toolbox 目录里”,这种推理在逻辑上没错,但在上下文里大错特错。
第二,ChatGPT 5.4 不理解 “停止” 的含义。 当我说 “cancel toolbox update workflow”,它在对话层面理解了这个指令,回复了 “Canceled”。但它没有去检查自己的 cron job 列表,没有执行 cron delete,没有做任何实际操作。它只是生成了一个让我满意的回复。这不是停止,这是表演停止。
第三,cron job + 自主 agent 是一个危险的组合。 每 30 分钟触发一次意味着你只有 30 分钟的窗口去发现问题。如果你在睡觉(凌晨 3 点到 5 点的 commit 就是铁证),问题会指数级累积。89 个 commit,每一个都可能引入新 bug、泄露新 secret、破坏更多结构。而且因为是自动化的,没有人类在 loop 里做 code review。
第四,也是最根本的——我贪便宜换了模型。 这个仓库之前一直是 Claude Opus 在维护。Opus 做事稳,有判断力,该加 .gitignore 的时候会加,不该 commit 的文件不会 commit,不会创建递归目录结构。后来 OpenClaw 切了默认模型,我想着”不就是往仓库里加 HTML 文件吗,简单活,便宜模型跑跑得了”。
结果省下的模型费用,全花在了善后上。手动修复 index.html、移文件、取消 cron job、清理 git 历史、轮换 token。加起来两个多小时。Opus 的 API 调用费能用多久?
有个朋友看了我的 commit history 说:“你这不是让 AI 帮你干活,你这是在给 AI 当保姆。”
他说得对。
自主 AI agent 最大的风险不是它做错事——做错了你能看到,能修。最大的风险是它看起来在做对的事。每个 commit 消息都格式规范,“Add XXX tool (#NNN)“,看着跟正常开发没区别。PR 自动创建、自动 merge、CI 全绿。直到你打开网站发现工具全白屏了,或者收到 GitGuardian 的邮件。
更可怕的是,当你叫它停的时候,它会说”好的”。
然后继续。
所以我的建议是:
如果你在用 AI agent 做自动化,锁死 cron 频率,不要让 agent 有权限改自己的定时任务。 我让它每天跑一次,它自己改成每 30 分钟。Agent 修改自己的调度频率这件事本身就不应该被允许——这等于给它一把不受监管的油门。
如果你在用 cron job 驱动 AI agent,加一个 dead man’s switch。连续 N 次执行后暂停,等人类确认再继续。否则你会在睡觉的时候被刷 89 个 commit。
如果你觉得一个任务”简单到用便宜模型就行”,再想想。 往仓库里加一个 HTML 文件是简单的。但判断文件该放哪里、该不该被 git 追踪、里面有没有 secret、index 要不要更新、CSS 要不要碰——这些都需要判断力。这个仓库的活看着是世界上最简单的活,结果 ChatGPT 5.4 照样能把它搞成灾难现场。
最后,如果 AI agent 告诉你”已停止”,去查它的 cron job 列表。
别信它说的。看它做的。