GBrain Minions — 源码深度分析

> 一句话版本:一个 Postgres 原生的后台任务队列系统,灵感来自 BullMQ,但完全不需要 Redis。确定性工作(shell 脚本、数据同步)走 Minions 零 token 消耗,判断性工作(LLM 循环)也可以用 subagent handler 在同一队列里跑。4,448 行 TypeScript。

项目信息
来源[github.com/garrytan/gbrain/src/core/minions/](https://github.com/garrytan/gbrain/tree/master/src/core/minions)
语言TypeScript
依赖PGLite(本地)/ Supabase(生产),Bun 运行时
灵感BullMQ(Redis 队列),Sidekiq(backoff 策略)

架构总览


┌─────────────────────────────────────────────┐
│              MinionQueue                     │
│  (Postgres-native job queue)                 │
│                                              │
│  add() → claim() → completeJob() / failJob() │
│  cancelJob() → recursive CTE                │
│  handleStalled() / handleTimeouts()         │
└──────────────┬──────────────────────────────┘
               │
     ┌─────────┼──────────┐
     ▼         ▼          ▼
  Worker    Worker     Worker
  (concurrency=1) (concurrency=4)
     │
     ├── shell handler      → /bin/sh -c "cmd"
     ├── subagent handler   → Anthropic Messages loop
     └── aggregator handler → fan-in child results

核心组件

1. MinionQueue(~1,500 行)— Postgres 原生任务队列

不需要 Redis,所有状态都在一张 minion_jobs 表里。

Job 生命周期(9 种状态):


waiting → active → completed
                 → failed → delayed → waiting (retry)
                          → dead (永久失败)
waiting → delayed (带延迟的任务)
active → paused → waiting (恢复)
waiting → waiting-children (父任务等子任务)
        → cancelled (递归取消所有后代)
active → dead (超时 / max_stall 超限)

关键设计

设计点实现方式
**乐观锁**`lock_token` + `lock_until`,worker 每半轮续约
**FOR UPDATE SKIP LOCKED**claim 时用 PG 行级锁,多 worker 安全
**幂等提交**`idempotency_key`,相同 key 返回已有 job
**事务原子性**`completeJob()` 在同一个事务里:更新状态 + token 汇总 + 写 child_done + 解析父任务
**递归取消**`WITH RECURSIVE` CTE 一次性取消整棵任务树(最深 100 层)
**级联失败策略**`on_child_fail`: `fail_parent` / `remove_dep` / `ignore` / `continue`

token 汇总:子任务完成后自动向上汇总 token 消耗到父任务,最终可以看到整棵树的总成本。

2. MinionWorker(~250 行)— 并发 worker


const worker = new MinionWorker(engine, {
  concurrency: 4,      // 同时跑 4 个 job
  lockDuration: 30000, // 30s 锁
  stalledInterval: 30000, // 30s 检测一次 stalled
});

worker.register('sync', syncHandler);
worker.register('shell', shellHandler);
worker.start(); // 阻塞直到 SIGTERM

每个 job 独立的 AbortController

Worker 循环

1. 推进 delayed 任务

2. Claim 新 job(不超过 concurrency)

3. 检测 stalled(lock 过期但 status 仍 active → 重新排队)

4. 检测 timeout(timeout_at 过期 → dead-letter)

3. Shell Handler(~311 行)— 执行 shell 命令

双重安全门

1. MinionQueue.add() 拒绝 name='shell',除非 allowProtectedSubmit=true(CLI 和本地 MCP 才有)

2. 环境变量 GBRAIN_ALLOW_SHELL_JOBS=1 才注册 handler

环境隔离


// 只传递白名单变量,防止 $OPENAI_API_KEY 泄漏
const SHELL_ENV_ALLOWLIST = ['PATH', 'HOME', 'USER', 'LANG', 'TZ', 'NODE_ENV'];

优雅终止


SIGTERM → 5 秒等待 → SIGKILL

stdout/stderr 用 UTF-8 安全的 TailBuffer 截断(64KB / 16KB)。

4. Subagent Handler(~710 行)— LLM 循环

不只是 shell,Minions 也能跑 LLM。完整的 Anthropic Messages API 循环:

5. 辅助系统

Backoff(Sidekiq 风格):

Stagger(确定性交错):

Quiet Hours(静默时段):

Rate Leases(限流):

父子任务 DAG


parent (waiting-children)
  ├── child_1 (completed → child_done → inbox)
  ├── child_2 (failed → child_done → on_child_fail policy)
  └── child_3 (active → timeout → dead → child_done)

Aggregator 模式:
parent waits for ALL children → reads inbox → aggregates results

关键机制

Token 汇总:每个子任务的 token 消耗自动累加到父任务。

安全模型

保护
Job name`shell` 是受保护名,MCP 调用者不能提交
环境变量白名单制,防止 API key 泄漏
Shell 路径硬编码 `/bin/sh`,防止 PATH 投毒
深度限制`maxSpawnDepth=5`,防止无限递归
子任务上限`max_children`,防止 fan-out 爆炸
AbortController超时/取消/锁丢失/进程终止,四种信号
Stall 检测`max_stalled`(默认 5),超过则 dead-letter

与 BullMQ 对比

维度BullMQ (Redis)Minions (Postgres)
依赖RedisPGLite / Supabase
持久性Redis AOF/RDBPostgres WAL
分布式天然支持单 worker(当前)
父子 DAG有限完整(inbox + child_done)
附件内置(5MB 限制,SHA256)
LLM 循环内置 subagent handler
限流外部内置 rate leases
静默时段内置 quiet hours

分析

优势

局限

与 Jay 的关联

评分

维度评分 (1-10)说明
设计质量9事务一致性、crash-safe、统一队列
代码质量94,448 行,注释详尽,edge case 处理完善
创新性8BullMQ 思路但 Postgres-native + 内置 LLM 循环
实用性8Garry 生产验证(19 cron、45K 页 brain)
通用性6Bun 专属、单 worker、Anthropic-only
与 Jay 的关联9直接解决 OpenClaw cron 痛点
**总分****8.5**Postgres 原生任务队列的标杆实现