← 随机比特 / 所有内容

我在把 OpenClaw 真正接进日常工作流之后,前前后后手动清理了 47 次僵尸进程,最后发现:agent 真正难的不是“会不会做事”,而是“失败后能不能被收拾、被接管、被复原”。

2026-03-18 · 随机比特

我给 OpenClaw 杀了 47 次僵尸进程,终于想明白了一些事

我最近开始会下意识地数一件事:这套东西今天又让我杀了几次进程。

我不是突然迷上了 kill -9,只是这套链路确实常把人逼到这一步。

而是当 OpenClaw 真正被我接进 daily-digest、浏览器自动化、公众号草稿箱这些真实工作流之后,我发现最消耗人的,往往不是“模型回答得对不对”,而是失败之后那堆残局。

有些进程看起来已经结束了,其实还挂着;有些页面明明已经登录了,脚本却告诉你“未登录”;有些任务不是没做成,而是卡在最后一米,留下一个你不得不自己进去收拾的半完成状态。

前前后后,我给 OpenClaw 杀了 47 次僵尸进程。

47 次听起来像个标题党数字,但它对我来说更像一张工程账单:我开始真正意识到,agent 一旦从聊天框走向工作流,最先暴露的不是模型上限,而是系统下限。

真正难的,是它做砸之后,你能不能把现场收回来。


这 47 次“杀进程”,其实都落在 4 类失败上

我后来复盘,发现这些看似随机的崩法,最后都能归到四类。

第一类,是卡死但不退出

任务表面上已经没反应了,实际上子进程还挂着,端口还占着,浏览器连接也没真正释放。你下一次再跑,同样的脚本、同样的参数,却会撞上完全不同的异常。很多人会把这种问题误解成“模型不稳定”,但根因通常跟模型没什么关系,就是资源没清干净。

第二类,是退出了,但状态没清干净

这类最烦。因为它给你的错觉是“上一次已经结束了”。可实际上,临时页面还开着、旧 tab 还挂着、草稿态残留、缓存 DOM 还在。你以为自己是在跑一次全新任务,系统却在一个半旧半新的状态上继续往前滚,于是错误会越来越诡异。

第三类,是连到了错误页面,或者错误的 tab

这几乎是 browser workflow 里最常见、也最容易被忽略的一类故障。脚本以为自己拿到的是目标页面,实际上只是“长得像目标页面”的另一个 tab。尤其在 CDP 9222 这种会复用现有浏览器上下文的环境里,只要多开几个相似页面,选择器、URL、等待时序一脆,自动化就很容易连错对象。

第四类,是报错信息把人带偏

这是我觉得最坑的一类。因为它会把你从真正的根因旁边拉走。你明明遇到的是 tab 选择问题,报错却写成“未登录”;你遇到的是状态残留,报错却看起来像 selector 失效;你遇到的是进程没回收,表面症状却像接口抖动。

这时候你如果不先把系统状态拆开看,就会进入一个很熟悉的循环:重跑、报错、杀进程、再重跑。

<!-- diagram:workflow-framework -->

这也是我后来为什么开始把“杀进程”单独当成一个信号:它不是操作动作本身有多重要,而是它提醒我,这条工作流已经开始为失败支付额外成本了。


最典型的一次:公众号草稿注入,为什么会把“已登录”误报成“未登录”?

最近一次特别典型的故障,就发生在公众号草稿注入这一步。

这条链路原本很简单:文章写完,生成公众号版 Markdown 和 HTML,然后通过 9222 连接到已经登录的浏览器,把内容注入到微信公众号草稿箱里,最后只保存草稿,不公开发布。

按理说,这已经是非常保守的一种自动化了。对外动作只到草稿层,真正发布仍然留人工确认。

但它还是翻车了。

一开始脚本报错的口径非常直接:无法从 URL 提取 token,可能未登录。

如果只看这句提示,结论几乎是现成的:微信登录态失效了。

但这次我没有立刻接受这个结论,而是回过头去核查前置事实。结果很快发现,问题根本不是“没登录”。

当天的 9222 浏览器里,mp.weixin.qq.com 页面其实是存在的,而且已经登录。更关键的是,当前页面 URL 里明确带着 token,标题是“公众号”,账号也是正确的“随机比特”。换句话说,登录态、tab、账号,这几个最关键的事实都成立。

那脚本为什么还会说“未登录”?

后来把日志和页面状态对起来看,根因就出来了:不是用户没登录,而是脚本过早抓到了错误的公众号 tab

更具体一点说,这类脚本通常会先扫一遍 http://localhost:9222/json 返回的目标列表,优先找 URL 里已经带 token= 的公众号页面;如果当下没找到,就会退回一个“任意 mp.weixin.qq.com 页”。这个降级逻辑听起来合理,但一旦你刚新开了一个公众号页,或者页面还在跳转途中,事情就会变得微妙。

因为这时候最先被抓到的,很可能是一个长得对,但还没完全准备好的 tab:域名对了,页面也像是公众号后台,但 URL 还没带 token,跳转还没结束。脚本如果在这个时间点做结论,就会把一种短暂的过渡态,误报成“未登录”。

这就是典型的 agent 工程问题。

问题不在模型不会写文章,也不在微信真的掉线,而在自动化脚本对“目标页面已就绪”这件事的判断太脆弱。它把“暂时没拿到状态”误判成了“状态根本不存在”。

这类问题如果放在聊天框里,其实不会出现。因为聊天框没有状态机,也没有外部页面要接。

但一旦你让 AI 去碰浏览器、草稿箱、登录页、已有 tab、复用会话,这些状态判断就会立刻变成主战场。


我后来开始强行区分三种问题:模型、脚本、环境

以前我调 agent 系统时,有个很常见的坏习惯:只要任务没跑通,就统称为“AI 出错了”。

后来我发现,这个说法几乎没什么诊断价值。

因为至少要区分三种完全不同的问题。

第一种是模型问题

比如理解错了需求、漏读了上下文、生成结果偏题。这些确实属于“AI 本身没做好”。它们的修法通常是改上下文、改约束、改任务边界。

第二种是脚本问题

比如某个等待条件写得太乐观,tab 选择逻辑过于粗糙,或者错误恢复路径根本没设计。这类问题跟模型无关,换十个模型都一样会翻车。它们本质上是流程设计缺陷。

第三种是环境问题

浏览器上下文漂移、登录态变化、旧页面残留、端口被占、子进程没回收、外部站点响应变化——这些问题不但真实,而且常常最贵。因为它们会制造大量“看起来不像根因”的假症状。

一旦把这三类混在一起看,人就会很容易陷入一种假勤奋:不停重跑,不停换 prompt,不停怀疑模型,但真正该修的清理逻辑、等待逻辑、恢复逻辑,一直没人碰。

所以我现在会逼自己先问一句:

这次失败,到底是理解错了,流程错了,还是环境脏了?

这句分类问题,比继续重跑一遍有用得多。


后来我真正补的,不是 prompt,而是失败出口

也是从这些坑里出来之后,我开始重新定义一条工作流到底算不算“能用”。

以前我的标准比较乐观:能自动跑通一次,就算这条链路成立。

现在我会加一个更苛刻的判断:

它失败一次之后,能不能被顺手收拾干净?

如果失败后会留下孤儿 tab、残留进程、半成品草稿、无法判断真假的报错,那这条链路就还不算成熟。它只是勉强演示成功过。

所以后来我补的东西,很多都不酷。

我后来补的,不是什么更长的 prompt 或更花哨的 agent 编排,而是这些朴素但关键的东西:

这也是我现在越来越在意的一件事:

agent 真正值钱的地方,从来不是“它会不会给你一个看起来聪明的回答”,而是“它在复杂、易脏、会漂移的真实环境里,能不能留下一个你接得住的现场”。

<!-- diagram:three-workflows -->

如果一个系统每次失败都得你重新理解一遍现场、手动扫尾一遍,那它就算某次成功了,也还谈不上真正省时间。


我最后想明白的一些事

杀了 47 次进程之后,我最后想明白的不是“以后要更小心”。

而是下面这几件更具体的事。

第一,Agent 不是更聪明的聊天框,而是一套会留下残局的系统。

只要它开始接浏览器、接文件、接脚本、接消息渠道,它就一定会进入状态管理问题。这个阶段里,模型智能当然重要,但不再是唯一主角。

第二,可靠性不是锦上添花,而是工作流能不能成立的地基。

你可以接受一篇草稿不够锋利,再改一版;但你很难接受一条链路每次到最后都要自己进去捞残局。因为后者消耗掉的,不只是几分钟,而是你对这条系统的信任。

第三,真正该留给人的,不只是“最终审批”,还有失败时的接管权。

这也是为什么我越来越坚持一个原则:对外发布、发消息、花钱、改线上状态,这些动作前面都必须有人工闸门。不是因为我不相信 AI,而是因为我越来越相信真实系统一定会出错。

最后,47 次不是战绩,也不值得浪漫化。

它只是很诚实地提醒我:当你把 AI 从聊天框里放出来,让它开始替你跑工作流时,你买到的不只是效率,还有一整套工程责任。

这套责任里最重要的一部分,不是成功路径,而是失败之后怎么收场。

这件事想明白之后,我反而对 agent 更有信心了。

因为我终于知道,真正该补的不是幻想,而是地基。


数据来源:OpenClaw 官方文档 https://docs.openclaw.ai;OpenClaw 官方仓库 https://github.com/openclaw/openclaw