派单 10 分钟就被回收:我们给 Hive 加了第四种 Agent 模式 accept,让定时任务也能接住卡

发布摘要(粘到 CSDN「摘要」框):多机 AI Agent 协同里,「发布任务」只是第一步。本文复盘 Hive 一次真实翻车:主控把 23 张并行任务卡发布到 Git 协调仓、派给三台 Windows 机器,结果 9 张派单在 10 分钟内被 watchdog 全部回收——而接单的定时任务每小时才醒一轮。根因不是 manual 模式,是「派单」与「接单」之间缺独立语义与等待窗口。新增的 accept 模式、180 分钟接单窗口、hive accept 命令与 MCP hive_accept 工具,以及 store 后端下派卡必须「双写」的坑,全部来自实跑现场。附 185 项回归测试与有序重启清单。零第三方依赖,开源。

写在前面:这不是一篇「五分钟搭建多智能体框架」的爽文,也不复述功能清单。Hive 是我们每天真在用的任务总线,这次把「发布任务」这条链路从头到尾压了一遍,值得把为什么这么设计和哪些坑是真踩过的写下来。文中的时间、提交号、命令、状态都来自实跑快照,不是示意图。

一句话矛盾:主控按小时级节奏派单,接单方也按小时级节奏醒来,可系统默认只给这张单留 10 分钟。


一、先把「发布任务」这件事说清楚

Hive 里没有任务队列服务,也没有中央数据库。一张任务卡就是协调仓库里的一个 Markdown 文件:

 

发布任务 = 写卡 + git push。领取靠 git push 的原子性:两个 worker 抢同一张卡,只有一个 claim/<tid> 分支能推上去。总控只做三件事:写卡、分派、验收合流。

这次一口气发了两批、共 23 张并行卡:

批次

卡号

覆盖

并行度

六仓整改(现成任务书落卡)

T-0260 ~ T-0276

registry 默认源断链、CI 徽章对齐、版本号一致性、清临时物、治理补齐、六仓交叉验收

第 1 批 6 路

light 语言本体缺陷(新制卡)

T-0277 ~ T-0282

归约段落实参发射、单行段落静默空跑、linter 假内置名、原生腿 argv、地板门禁漂移、回归探针、缺陷账终态

5 路 + 1 收尾

并行不是「多开几个 agent」就行,拆卡时有三条硬规则:

  1. 文件锁优先于目录锁。每张卡写 locks(精确到文件),不只写 paths(目录前缀)。按 (repo, path) 分组校验,23 张卡零冲突——否则 land 时必然撞在一起。
  1. 依赖分批。T-0269 ← T-0263、T-0272 ← T-0265、T-0282 ← T-0277..T-0281,收尾卡 T-0275 依赖整批,先不派。
  1. 判据是真跑过的命令。每张卡的 verify 都带 pytest 之类的强验收;hive draft lint 会拦住「全是 grep/test -f」的弱判据——agent 靠改个字符串就能糊弄过去的卡,不该转正。

发布完成那一刻:hive status 从 0 张待领卡(136 done + 2 shelved)变成 23 张 ready,三个远端 origin / github / gitcode 对齐到同一个提交。

看起来一切就绪。然后它翻车了。


二、第一次翻车:派出去的卡,10 分钟就没了

2.1 时间线

 

更麻烦的是,回收只改了 Git 卡面,store 里这 9 张仍是 assigned/owner=X——两个真相源分叉了。

2.2 一个 agent 自己把根因查出来了

我把其中一台(workbuddyai-win)的疑问丢回去,让它自己查。它翻了配置提交历史和 hive.py 的 mode 处理逻辑,给出的结论里有一句很关键:

「那三次 miss 跟 manual 模式无关,改 mode 也修不了。真正决定『有没有人接』的是这台机器上有没有常驻 worker,或者我有没有手动 take。」

它是对的。当时 15 个 agent 清一色 mode: manual、没有一台标 dispatchable,可 dispatch-mark 根本不校验 mode,手动 assign 也不受限制。把 manual 改成 exec 只会让 doctor 报「exec 没有 command」,卡照样被回收。

2.3 真根因:三条时间线一起卡住

看门狗里有三个彼此独立的窗口,全都是为「常驻 worker」设计的:

通道

默认值

作用

HIVE_DISPATCH_UNCLAIMED_MINUTES

10 分钟

派出去没人 claim → 记一次 dispatch_miss、退回 ready

orphan_assigned 兜底(--orphan-grace)

30 / 45 分钟

「领过但死了」的孤儿回收

HIVE_DISPATCH_TTL_MINUTES

60 分钟

刚推过的卡不重复派

外加一条熔断:dispatch_miss 累计到 2 就转 blocked,彻底退出流水线。

于是「派单 → 10 分钟回收 → TTL 压着不重派 → 再派再回收 → 转人工」,而接单方每小时才醒一轮。卡永远在 agent 醒来之前被收走。

结论很反直觉:缺的不是第四种 mode,是「派单」和「接单」之间的一段合法等待期。系统把 assigned 当成「已经有人接了」,实际上它只是「主控把单放在了收件箱里」。


三、accept:把「派单」和「接单」拆成两件事

3.1 四种模式,各管一种机器

模式

那台机器需要

谁接单

需要 command

exec

AI 的 headless CLI

常驻 worker 立刻调 CLI

必须

verify

只要能跑命令

不接改代码的卡,只跑验收

否

manual

只有 GUI IDE

人在 IDE 里点一下

否

accept

一个能定时唤起 AI 的计划任务

AI 定时任务自己来接

否

accept 的语义很窄,也很明确:

 

四条设计取舍:

  1. 天然参与 dispatch --apply。不需要 dispatchable: true、不需要 command / launch / 常驻 worker。机器上只有定时任务,也配得上「能被派单」。
  1. 接单窗口按派单对象逐卡取。accept_window_minutes(默认 180 分钟,可用 HIVE_ACCEPT_WINDOW_MINUTES 覆盖),必须 ≥ 定时任务唤醒周期;其它模式仍是 10 分钟。
  1. 超时不等于派发失败。窗口用尽只把卡退回 ready、保留 dispatched_to 与 inbox 便条,不累加 dispatch_miss、不转 blocked,卡体写一条「接单窗口超时」。下一轮定时任务照样能接。
  1. 接单校验派单对象。hive accept 只认 dispatched_to(或 owner)是自己的单,不抢无主单、不碰别人的单,并在卡面写 accepted_at 留痕。

3.2 命令长什么样

 

MCP 侧同步加了 hive_accept 工具;hive_next 对 accept agent 只推荐派给它的单,不再从公共池里挑。工具数从 15 → 16(controller 视角 22 → 23)。

3.3 为什么没有新增一个任务状态

这是最想强调的取舍:我们没有引入 offered / awaiting_accept 之类的新 state。

Hive 的状态机里 STATES、IN_FLIGHT_STATES、HANDOFF_STATES 三处耦合,加一个状态会牵动 dispatch、worker、看板、MCP 的筛选逻辑。所以复用 assigned 表示「已派单、待接单」,靠三样东西把语义补回来:

  • agent 的 mode: accept 决定 watchdog 用哪个窗口;
  • 卡面 accepted_at 标记「真的接过单了」;
  • hive accept 的派单对象校验,挡住「谁都能领」的旧行为。

顺带说明为什么必须挡:旧 prompt 用 hive take 接单,而 take 不校验 owner。现场真出现过 take T-0261 -> trae-win——把派给 doubao-win 的卡领走,还把 dispatched_to 覆盖成自己。加了 accept 之后,同一张卡用错身份接会直接报错:T-0261 派给了 doubao-win,不是 trae-win;不能接别人的单,且卡面零改动。


四、三个后门是一起堵的(最容易被漏的一处)

第一版实现只把「10 分钟」换成「180 分钟」,新增测试当场就红了——卡还是被回收了。

原因在 cmd_watchdog 里还有一条 elif:

 

accept 单放宽了快速通道,却仍会被 45 分钟的 orphan_assigned 兜底从后门收走。再往外一层,还有 60 分钟的推送 TTL 压着不重派。

所以最终要同时处理三条:

  • 快速回收窗口按 agent 配置放宽;
  • orphan_assigned 兜底对 accept 单显式跳过;
  • 超时后保留派单关系,不让 TTL 把单「压死」在无人区。

tests/test_accept_mode.py 用 7 条判据把这三条钉住:mode 合法性与 doctor、dispatch 天然可派、60 分钟不回收、200 分钟超时不记 miss 不转 blocked、抢单被拒、接单写 accepted_at、窗口可按 agent 覆盖。

这条经验比功能本身更值钱:改一个「等待窗口」时,先列出系统里所有会判定「等太久」的地方。只改最显眼的那一个,后门会替你把它改回去。


五、双写坑:store 后端下,assign 根本不写 Git 卡面

这是发布链路里最容易踩、也最隐蔽的一处。

本机 ~/.hive/cluster/config.json 是 claim_backend=store。看 Hive.cmd_assign:

 

而 Windows 定时领取读的是 Git 卡面 + inbox/。只跑一次 assign,卡在 store 里是 assigned,Git 卡面还是 ready、收件箱是空的——agent 完全看不到派单。

所以给「读 Git 卡面」的领取者派单,两条都要跑:

 

验证要查三处:tasks/T-xxxx.md 的 state/owner、inbox/X/T-xxxx.md、store 表里的 state/owner。少一处,迟早分叉。

5.1 还有一层:常驻进程加载的是旧代码

13:13 提交 accept 代码后,13:29 又出现了一轮 dispatch-miss——8 张卡再次被回收。查进程才发现:

 

代码提交了、测试过了、文档写了,跑着的进程还停在昨天。 这条已经写进文档(docs/主控与分发.md §4.6「部署生效边界」)。

有序重启清单(顺序错了会踩第二个坑):

 

如果省略「停主控 → 确认无残留」,cluster restart 和 orchestrate 的 cluster_ensure() 会各拉一个节点进程抢 8788、同时打开同一个 SQLite,日志里留下 store: 打开失败(忽略): database is locked——health 仍是 200,但 store 已经静默失效,所有 /api/cluster/store/* 返回 503。这个故障形态非常难查:表面全绿,写操作全废。


六、数字与验证

项

实测

发布任务卡

23 张(六仓整改 17 + light 缺陷 6),+3659 行

按 (repo, path) 校验的文件锁冲突

0 对

accept 模式改动

18 文件 / +732 行(提交 b11b739)

文档全量口径对齐

14 文件 / +101 −122(提交 81c0740)

回归测试

accept 7 + dispatch_miss/watchdog/orchestrate 26 + MCP 33 + docs/tracking 74 + core 分批 45 = 185 项全绿;文档复测 46 项

派单三处一致

9 张卡 Git 卡面 / inbox / store 全部一致

watchdog --json dry-run

回收候选 0(只剩 1 条 land_lag 提示)

三远端

origin / github / gitcode 对齐到 3983c4c

几个诚实的数字:

  • T-0260 已经由 doubao-win 做完并自动 land:真的给两台 agent 登记了 lighthive / lightplugin、给 fb88-dsh 开了 dispatchable。
  • T-0262 已交 failed 结果,没有合并。agent 在结果里明确写「未修好默认源可达性,只修了错误可读性,勿标『修好了』」——验收闸门判红就是判红,不因为「有结果」就合。
  • 集群仍有一个遗留:state.json 的 leader_id 反复被 104 节点的心跳带回一个节点表里不存在的幽灵身份,两节点都停在 follower、无 leader。此时 orchestrate 会空转(no-leader,跳过写动作)——不会误回收卡,但自动派卡/自动合入也停了。根治要在那台 Windows 机器上清 state.json 并重启 hived,本机没有远程执行通道。把它写出来,比假装一切正常有用。

七、有哪些「故意不做」

  • 不加新任务状态。复用 assigned,用 mode + accepted_at + 派单对象校验补语义,避免牵动整套状态机。
  • 不给 accept agent 配常驻接单服务。它的全部前提就是一个能定时唤起 AI 的计划任务;再加守护进程就退回 exec 了。
  • 不自动改 agent 机器上的定时任务 prompt。新版 prompt 随仓库推送、也给三个 agent 发了跨机留言,但换命令这一步留给真机上的人——Agent 不该偷偷改自己的调度器。
  • 不因为「有结果」就合并。failed 结果不 land;合并仍留给人 review 或总控 land。
  • 不隐藏运行态不一致。Git 卡面与 store 分叉、常驻进程代码旧、集群无主,都写进文档和排障表,而不是在推介里抹掉。

适合谁:已经有 Git、机器异构、agent 能力参差、接单方是「定时被唤醒的 AI」而不是常驻服务的团队。 不适合谁:单机单 agent、要毫秒级调度、或已有成熟 CI/CD 编排体系的场景。


结语

这一轮最值钱的结论不是「加了个 mode」,而是:

多 Agent 协同里,最容易被忽略的不是「怎么派」,而是「派出去以后,接单方到底有没有机会来接」。

exec 假设接单方一直在线,manual 假设人一直在线,只有 accept 承认「接单方是按自己的节奏醒来的」。一旦把派单与接单拆开,等待窗口、回收策略、TTL、状态留痕、派单对象校验就得一起重新设计——只改最显眼的那一个,后门会替你把它改回去。

开源部分永久免费,直接用就行。真卡住了、或者要接进自己的多机产线,可以写信聊:hello@quye.com。

  • 使用指南:仓内 docs/使用指南.md
  • 派单—接单机制:docs/主控与分发.md §4.6;定时接单 prompt:docs/子agent定时领取-prompt.md
  • 架构串讲:docs/架构与排障全景.md

如果只记一句:别急着调 mode,先量一下「派单」到「接单」之间的时间差,再看看系统给这段等待留了多久。

Logo

更多推荐