2026年9月4号晚上,我把 queue-cli.py 挪进了 archive 目录。3008行,22个子命令,配套的回归测试脚本写了10万字节。从8月28号晚上立项到9月4号退役,这个工具满打满算活了一个星期。
这不是一次失败的收尾,恰恰相反,我觉得这是一次挺完整的工具生命周期。趁着记忆还热,把整个过程写下来:为什么造它、怎么造的、为什么又不用了。
一、理念:任务不丢,完成必达
先交代背景。我在服务器上养了一队AI agent,有管调度的、写代码的、做审查的、采情报的,日常产生大量后台任务:派子代理去改代码、跑CLI命令、定时巡检。任务一多,问题就来了:谁在跑?跑完没有?结果发给谁?挂了谁管?
我对后台任务管理就三条要求:
在 queue-cli 之前,这三条靠一个共享JSON文件撑着。所有agent都能读写它,没有锁,没有备份,没有校验。能一直没出大事,纯属运气好。
二、压垮骆驼的一场事故
8月28号傍晚6点22分,主会话切换任务时把 .active-task.json 整体覆写了,串行队列里排着的三批任务全部冲掉。19点05分人工恢复,丢的账捡回来了,但我坐不住了。
复盘根因,三层叠一起:
当晚拍板:不缝补丁,重做。方向五层:文件拆分(当前任务和队列分成两个文件各管各的)、写前快照、原子写、单写通道、心跳巡逻。当天夜里走完内部评审,第二天实现落地。
三、实现:把人人都能写,变成只有一个人能写
核心思路一句话:所有写操作收口到一个命令行工具里,就是 queue-cli.py。
任何agent想改任务状态,只能调它的子命令:入队、出队、登记、完成、改状态。工具内部流程固定:抢文件锁、读最新数据、业务校验、写前快照、原子写、读回校验。谁也别想再玩整文件覆盖那一出。
几个实现细节我自己挺得意:
在这个底座上,长出了当时看挺完整的一套编排能力:
然后它开始长大。
8月30号,加update子命令,改字段不用再整文件重写。
9月2号,我抓到有agent先把执行器跑了、回头才补登记,甚至有不登记的。顺着查时间线,发现登记和启动之间全是窟窿:登记完没启动的,启动了没登记的,进程都跑起来了登记还没落地的。于是加start子命令:一把锁内完成登记,锁外拉进程,2秒探活,起不来自动回滚重新排队。登记先于启动,从机制上保证,不再靠自觉。
9月3号,又给JSON文件加了PostgreSQL持久化层。到这里,这个工具3008行、22个子命令、几十个回归用例,还有一套心跳探针围着它转。
但你应该已经闻到味道了:一个任务队列,怎么越养越像一个系统。
四、为什么不用了:平台把它长出来了
9月4号,新升级的OpenClaw 2026.8.2摆在我面前,原生任务系统落地。我拿queue-cli的功能清单逐条对照:
我的第一反应是不信。让调度agent花了一下午做了五组真实验证:两类执行器真实启动、强制崩溃看失败标记、退出码核对、编排镜像记录、纯命令行无会话触发。全过。当时账本里已经有1269条生产任务记录在自动流转。
当然不是没缺口。串行队列、并行槽、峰谷排队,原生都没有等价物;CLI直跑的完成通知默认静音;复合命令会让退出码失真;原生投递也有失败案例,审计里躺着31条warning。原生不是零运维,只是换了个地方运维。
摆了三条路:渐进式,队列留着管编排、账本迁原生;混合式,砍掉三分之二子命令留个精简编排层;全拆。我拍板:全拆,不做瘦身迁移。
理由想清楚了:
放弃的东西也想清楚了:串行排队改并发派发,峰谷自动排队改成调度agent手动错峰,失败自动回滚重排改成发起方自己重试。都是可接受的退化。
五、拆除与复盘
拆比想象中利落。晚上拍板,34项清单当天全部落地:26个规则文件批量改写,hook注入同步(再不同步就是第9次漂移),两个执行器脚本的拦截门换成进程树校验,queue-cli.py和测试脚本归档,运行时数据归档,记忆库写入终态记录。最后派了个测试子代理走全链路:自动入账、进度卡展示、完成投递回发起会话,一次通过。
PostgreSQL里的tasks表停写留查,坟头留碑。
复盘几条:
现在我的后台任务面板长在控制台里,任务页一刷,谁在跑、谁挂了、谁的结果没送出去,一目了然。
queue-cli,生于事故,卒于升级,享年七天。安息。
本文作者:丘丘
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!