Convoy 是 theplenkov/bro 下的一个技能,本质上是 bro CLI 的薄封装,自身只定义策略,不包含调度实现——所有机制都位于 bro CLI 中,由 bro flow convoy next 根据 beads 状态计算下一步。
前置要求:bro 在 PATH 中可用,或通过 npx -y @broject/bro@0(主版本固定)调用;同时需要 bd 命令和一个已初始化的 .beads/ 目录。Gas City 不是必需组件,bro 本身就是执行器。
什么是 convoy:一个 convoy 就是一个 beads 分子,是公式(通过 bd mol pour)的持久化实例。每个步骤是子问题,blocks 边构成 DAG。所有状态都保存在 beads 中,因此 convoy 能经受会话重启、上下文压缩和多个并行智能体的影响。
核心循环:
- bro flow convoy pour <formula> [--var K=V]… 打印分子根 id(每次运行一次)。
- bro flow convoy status [mol] 显示 DAG,标记完成(✓)、就绪(▸)、阻塞(·)。
- bro flow convoy next [mol] 输出 JSON,说明当前该做什么。
- bro flow convoy claim <step-id> 原子认领步骤,开始前必须先执行。
- bro flow convoy done <step-id> --result "…" 关闭步骤并输出新的 next。
- 循环执行 next → claim → 工作 → done,直到 next 报告 "state": "complete"。
- 全部完成后,关闭分子根:bd close <mol-id> --reason "convoy complete"。
next 返回的状态:
- step:执行该步骤(id、title、description 即工作指令),然后用 done 提交结果。
- gate:下一步是人类步骤。必须询问用户,不能代替用户执行或标记完成;用户的答复就是结果,之后用其决定调用 done。
- blocked:存在未完成步骤但没有就绪的(可能是依赖循环,或剩余步骤都在等待其他未完成工作)。此时运行 status 并汇报。
- complete:所有步骤完成,关闭分子根。
next 还会报告 gates[](所有就绪的人类关卡,即使智能体步骤仍在运行也要及时向用户呈现)、inProgress[](已被认领的步骤,包括你自己认领的;别人认领的步骤要避开)以及 inputs[](每个已关闭的直接依赖的 --result,即交接信息,开始步骤前应阅读)。
如果有多个打开的分子,必须显式传入 --mol <id>;无参数解析在存在歧义时会报错而不是猜测。
队列模式——bro flow convoy run: run 把上述会话内循环变成分离工作进程:每个分子启动一个 convoy-runner 智能体,通过 agents facade 生成(注册表条目、固定提示/日志/退出码、在分子根上共享 beads 认领,bro fleet agents status/bro fleet/bro review watch 可见)。像它替代的 mol-queue 脚本一样串行执行;生成时 fleet.maxConcurrent 仍然生效。
- --attempts N(默认 4)限制每个分子的生成尝试次数;崩溃、直接拒绝、工作进程退出但分子未完成都计入;超过后分子失败,队列继续。
- rate_limited 且提供方有 resetAt 时等待重置(不消耗尝试);配额/无重置墙和操作员停机属于 parked/stopped 判定——只报告,永不重试。
- 已被活跃工作进程认领的分子显示为 occupied——跳过,绝不双跑。以人类关卡结束的分子显示为 gated。 对于真实队列,应以分离方式运行(nohup/systemd-run/tmux)——它是像 bro review drive --every 一样的监督循环,退出码 1 表示某些分子耗尽了尝试次数。
等待器:bro flow convoy wait <mol>… [--every SEC] [--timeout SEC] 是有限监视器,当每个分子都落定后退出(0=完成,2=卡住,3=被关卡拦截,4=超时)。退出本身就是唤醒事件——应将其作为后台 shell 生成,而不是在循环中轮询 status。
策略要点:
- 永远不要跳过 next。不要凭标题猜测步骤顺序——DAG 决定顺序。独立的就绪步骤会同时出现在 ready[] 中;跨智能体会话并行执行是允许的,但顺序选择步骤是安全默认。
- --result 是交接信息。后续步骤会读取它——写入下一步需要的内容(路径、PR 链接、判定结果),而不是日记。
- 关卡不是可选的。人类步骤的存在就是为了阻止机器;把它当作智能体步骤会破坏公式。
- 开始前先认领。claim 是原子租约(assignee + in_progress,若其他智能体持有则拒绝)。同一仓库有并行智能体时,未认领的步骤是共享工作。
- 恢复是免费的。上下文压缩或新会话后,status 能重建一切——没有内存状态会丢失。
- 失败保持可见。如果步骤无法完成,让它保持打开并告知用户——不要强行关闭你没做的步骤。