任务串行瓶颈
单 Agent 处理 700+ 任务耗时 29 小时,无法接受。需要多 Agent 并行。
Skill 能力一致性
多 Agent 并行执行同一 Skill,如何确保各 Agent 拥有完全相同的 Skill 能力?
调度与负载均衡
任务数量远大于单 Agent 处理上限,需要有效的任务分发机制。
性能目标
将 700+ 任务的执行时间从 29 小时 压缩到 30 分钟以内,通过 15 个并行 Worker 实现约 58 倍加速。
2.1 整体架构
用户 / 编排者 Agent
分析任务 → 创建任务队列 → 监控进度
↓ 创建 N 个任务
┌─────────────────────────────────────────────────────────────┐
│ Kanban 调度层 │
│ │
│ dispatch_interval_seconds: 5s max_spawn: 15 │
│ dispatch_in_gateway: true failure_limit: 5 │
│ │
│ Ready ──→ 调度器按 max_spawn 上限 Spawn Worker │
│ Running ─→ 跟踪正在执行的任务 │
│ Done ────→ 标记完成 │
│ Blocked ─→ 等待人工介入 │
└────────────────────────────┬────────────────────────────────┘
│ spawn_nowait()
↓
┌──────── Worker 1 ────────┐ ┌──────── Worker 2 ────────┐
│ hermes -p collector_a │ │ hermes -p collector_b │
│ ───────────────────── │ │ ───────────────────── │
│ 加载 Skill: │ │ 加载 Skill: │
│ policy-batch-collector │ │ policy-batch-collector │
│ ───────────────────── │ │ ───────────────────── │
│ kanban_read_task() │ │ kanban_read_task() │
│ → 执行采集逻辑 │ │ → 执行采集逻辑 │
│ kanban_complete() │ │ kanban_complete() │
└────────────────────────┘ └────────────────────────┘
↓
┌─────────────────────────┐
│ 共享结果存储 │
│ ~/七大政策/{城市}/... │
└─────────────────────────┘
2.2 核心设计原则
任务队列化
所有批量任务进入 Kanban 队列,调度器统一分发,任务不丢失、可追踪。
Worker 池化
固定数量 Worker 进程,按需复用。任务完成后立即接新任务,减少空窗期。
Skill 中心化
Skill 实体存于一处,所有 Profile 通过符号链接共享,更新一次全量生效。
进程隔离
每个 Worker 是独立 OS 进程,独立模型调用,互不阻塞,互不影响。
调度自主
调度器内嵌 Gateway,固定间隔 tick,主动补位,无需人工干预。
可观测性
任务进度、Worker 状态、成功率全程可控,调度器日志完整。
以固定间隔扫描 Ready 任务池,按 max_spawn 上限 Spawn 可用 Worker。每次 tick 间隔决定任务完成后的补位速度。
| 参数 | 默认值 | 说明 |
|---|---|---|
dispatch_interval_seconds |
60s | 轮询间隔。调到 5s 几乎无空窗期 |
max_spawn |
null | 每轮最大 Spawn 数,null = 不限 |
dispatch_in_gateway |
true | 调度器内嵌在 Gateway 进程内 |
failure_limit |
5 | 连续失败 N 次自动标记 Blocked |
调度逻辑(伪代码)
# kanban_db.py dispatch_once() def dispatch_once(): ready_rows = db.select("SELECT * FROM tasks WHERE status = 'ready'") for row in ready_rows: if spawned >= max_spawn: break claimed = claim_task(conn, row["id"], ttl_seconds=900) if claimed: pid = spawn_nowait( "hermes -p <profile> chat -q work kanban task <id>", cwd=workspace ) spawned += 1
Worker 生命周期
Profile = 独立 Hermes 配置单元,包含独立配置文件、SOUL.md、Skill 链接、API Key、session 存储。每个 Worker 进程由一个 Profile 驱动。
- 独立 ~/.hermes/profiles/collector_a/
- 独立 SOUL.md + Skill 链接
- 独立 API Key 配置
- 独立 session 存储
- 独立 ~/.hermes/profiles/collector_b/
- 独立 SOUL.md + Skill 链接
- 独立 API Key 配置
- 独立 session 存储
- 独立 ~/.hermes/profiles/collector_c/
- 独立 SOUL.md + Skill 链接
- 独立 API Key 配置
- 独立 session 存储
Worker Spawn 方式
subprocess.Popen("hermes -p <profile> chat -q work kanban task <id>")
每个 Worker 是独立 OS 进程,由调度器通过 Popen 启动,启动后自行读取任务、执行 Skill、标记完成。
多个 Worker 并行执行同一 Skill,必须保证各 Agent 拥有完全相同的 Skill 能力。以下是三种方案对比:
手动同步
每个 Profile 目录单独维护一份 Skill 副本。更新时需要逐个复制。
不推荐 更新成本高,容易版本漂移
符号链接
Skill 实体存于 ~/.hermes/skills/ 一处,各 Profile 目录通过 ln -s 引用。
推荐 更新一次全量生效,版本完全一致
共享 Skills 目录
所有 Profile 的 skills/ 配置指向同一个根目录,通过 hermes skills link 关联。
备选 需配置 skills.root_path
推荐:符号链接结构
~/.hermes/
skills/ ← Skill 单一来源(实体)
policy-batch-collector/
SKILL.md
kanban-batch-collection/
SKILL.md
...
profiles/
collector_a/
skills/ ← 符号链接 → ../../skills/
policy-batch-collector/ ──→ ../../skills/policy-batch-collector
kanban-batch-collection/ ──→ ../../skills/kanban-batch-collection
collector_b/
skills/ ← 符号链接 → ../../skills/
policy-batch-collector/ ──→ ../../skills/policy-batch-collector
kanban-batch-collection/ ──→ ../../skills/kanban-batch-collection
collector_c/
skills/ ← 同上
关键优势
更新 Skill → 改一处 → 所有 Worker 下次启动自动加载最新版本,无需手动同步,确保所有 Agent 能力始终完全一致。
Kanban 调度层实现了任务级别的负载均衡:任务统一入队,调度器按可用 Worker 数量轮流分发,保证所有 Worker 始终有任务执行,直到队列清空。
负载均衡策略
| 策略 | 描述 | 适用场景 |
|---|---|---|
| 轮询 | 调度器按顺序将 Ready 任务分配给空闲 Worker | 任务耗时相近时的首选策略 |
| 最短队列 | 优先分配给当前任务最少的 Worker | 任务耗时差异较大 |
| 加权 | 根据 Worker 性能历史分配不同比例任务 | 各 Worker 硬件性能不一致 |
并发控制参数
| 参数 | 值 | 效果 |
|---|---|---|
max_spawn: 15 |
15 | 最多同时运行 15 个 Worker 进程 |
dispatch_interval_seconds: 5 |
5s | 每 5 秒检查一次,Worker 完成后快速补位 |
failure_limit: 5 |
5 | 单任务失败 5 次后 Block,任务自动重新入队 |
性能估算
700 任务 / 15 Worker = 每 Worker 处理约 47 任务。
单任务平均耗时 2.5 分钟 → 每 Worker 约 117 分钟 → 整体完成时间 ≈ 30 分钟(含调度开销)。
Worker 进程隔离设计使得单点故障不会影响整体任务队列。以下是各层级的容错策略:
Worker 崩溃
进程异常退出 → 调度器下次 tick 发现任务 TTL 超期 → 任务自动重新入 Ready 队列
任务执行失败
连续失败 5 次(failure_limit)→ 标记 Blocked → 可人工介入或自动重置
结果幂等性
写入前先检查文件是否存在 → 已存在则跳过 → 可安全重跑不影响已成功的结果