多 Agent 协作的 Kanban 架构——任务分派、状态同步与死锁预防

0 阅读2分钟

def atomic_claim_task(lane_id: str, agent_id: str) -> Optional[Task]: """只有一个 Agent 能拿到同一任务""" # Lua 脚本保证原子性 script = """ local tasks = redis.call('ZRANGE', KEYS[1], 0, 0) if #tasks == 0 then return nil end local task_id = tasks[1] redis.call('ZREM', KEYS[1], task_id) redis.call('HSET', 'task:'..task_id, 'owner', ARGV[1], 'status', 'IN_PROGRESS') return task_id """ return redis.eval(script, 1, f"lane:{lane_id}:queue", agent_id)


## 三、死锁预防——四个铁律

### 3.1 单向流转

任务只能从左向右移动:Planner → Coder → Reviewer → Deployer。

**禁止回退**。如果一个任务需要返工(比如 Code Review 不通过),不是把任务退回去,而是:
1. 当前任务标记为 `FAILED`
2. 创建新任务,放入正确的泳道
3. 新任务携带旧任务的上下文和 Review 意见

这样保证了状态机的 DAG(有向无环图)属性,从根本上避免了循环等待。

### 3.2 Agent 不等待 Agent

```python
# ❌ 错误:Agent A 等 Agent B
result = agent_b.execute(subtask)  # 死锁高发区
do_next_step(result)

# ✅ 正确:通过 Board 流转
# Agent A 只负责提交子任务到下一个泳道
board.push(subtask, lane="Coder")
# 然后继续处理自己的下一个任务
next_task = board.pull("Planner")

3.3 超时释放

每个任务必须带 TTL。如果 Agent 崩溃或卡死,超时后自动释放回队列:

# 定时扫描僵尸任务
def scan_zombie_tasks():
    for task in redis.scan_iter("task:*"):
        if task.status == "IN_PROGRESS" and task.elapsed > task.ttl:
            task.status = "READY"
            task.owner = None
            redis.zadd(f"lane:{task.current_lane}:queue", {task.id: task.priority})
            alert(f"Task {task.id} timed out, requeued")

3.4 Dead Letter Queue

不可恢复的错误不进主流程。每个泳道有对应的 DLQ:

def handle_failure(task: Task, error: Exception, lane: Lane):
    if isinstance(error, RecoverableError):
        task.retry_count += 1
        if task.retry_count <= task.max_retries:
            lane.requeue(task)  # 重试
            return
    # 不可恢复 → DLQ
    task.status = TaskStatus.DLQ
    task.error_context = error.traceback
    dlq.push(task)
    notify_human(f"Task {task.id} moved to DLQ: {error}")

四、安全边界——Agent 隔离的第三条腿

安全专题的前两篇(Prompt Injection 防御、Tool Sandbox)解决了「外部不可信输入」的问题。多 Agent 场景下还有一个同样致命的问题:Agent 之间的互信不该是无条件的

4.1 最小权限——每个泳道只给必要的工具

泳道允许的工具禁止的工具
Planner读文件、搜索代码写文件、执行命令
Coder读/写 ~/projects/、run test操作 ~/.hermes/、网络出站
Reviewer读文件、git diff写文件、执行命令
Deployerscp、systemctl restart任意读写(只跑部署脚本)

4.2 互不信任——Agent 的输出永远是「数据」而非「指令」

Agent A (Coder): "代码写好了,Reviewer 你去审查吧"
Agent B (Reviewer): 收到的是 {task_id, diff_url}
                  → 不会直接执行 A 的任何建议
                  → 只从 Board 拉任务,不看 A 的私人消息

禁止 Agent 之间直接传消息。所有交互必须通过 Board(带状态验证)。

4.3 审计日志——不可抵赖

[2026-07-14 10:23:01] Task #1421 | PlannerCoder | Agent: plan-01
[2026-07-14 10:23:15] Task #1421 | CLAIMED by Coder agent: code-03
[2026-07-14 10:25:42] Task #1421 | DONEReviewer | 3 files changed
[2026-07-14 10:26:10] Task #1421 | CLAIMED by Reviewer agent: review-02  
[2026-07-14 10:28:33] Task #1421 | FAILEDDLQ | Reason: 安全漏洞

每条记录不可篡改。出问题时,谁接的任务、做了什么、结果如何,一清二楚。

五、完整架构图

┌─────────────────────────────────────────────────────────────────┐
│                         Kanban Board                             │
│                                                                  │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐             │
│  │ Planner │→│  Coder  │→│ Reviewer│→│ Deployer│             │
│  │  WIP:3  │  │  WIP:2  │  │  WIP:2  │  │  WIP:1  │             │
│  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘             │
│       │            │            │            │                    │
│       ▼            ▼            ▼            ▼                    │
│  ┌─────────────────────────────────────────────────┐            │
│  │              Redis (状态存储 + 队列)              │            │
│  │  lane:{name}:queue  (有序集合, 按优先级)         │            │
│  │  task:{id}          (哈希, 状态+owner+上下文)    │            │
│  │  lane:{name}:dlq    (DLQ 队列)                   │            │
│  └─────────────────────────────────────────────────┘            │
│                                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐        │
│  │  Agent   │  │  Agent   │  │  Agent   │  │  Agent   │        │
│  │  Planner │  │   Coder  │  │ Reviewer │  │ Deployer │        │
│  │  #1-3    │  │   #4-5   │  │   #6-7   │  │   #8     │        │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘        │
│                                                                  │
│  ┌─────────────────────────────────────────────────┐            │
│  │          Dead Letter Queue (人工接管)             │            │
│  └─────────────────────────────────────────────────┘            │
│                                                                  │
│  ┌─────────────────────────────────────────────────┐            │
│  │              审计日志 (不可变追加)                │            │
│  └─────────────────────────────────────────────────┘            │
└─────────────────────────────────────────────────────────────────┘

六、跟「并行 Agent 一把梭」的对比

很多框架的做法是:把 10 个 Agent 同时启动,各自领活干,用一个「协调器」汇总结果。

维度并行一把梭Kanban 流水线
文件冲突Agent A/B 同时改同一文件,互相覆盖泳道隔离,同时只有 1 个 Coder 持有任务
死锁A 等 B,B 等 A单向流转,无循环依赖
故障恢复一个 Agent 挂了,它的任务石沉大海TTL 超时释放 + DLQ 兜底
状态可见性不知道谁在做什么Board 实时显示每个任务的状态
安全Agent 之间直连,输出即指令隔离+审计,不可抵赖
吞吐量高(10 个并行)但乱中等,但可控、可预测

Kanban 方案牺牲了「理论最高吞吐量」,换来了「可预测性和安全」。对生产环境来说,后者远比前者重要。

七、实现建议:从最小可行开始

不需要一上来就建 Redis、写 Lua 脚本。最小可行方案 30 分钟就能跑:

# 最小 Kanban:文件系统 + 目录结构
board/
├── lane-1-planner/
│   ├── queue/          # 任务文件 = READY
│   ├── in_progress/    # Agent 把这个文件移到这里 = IN_PROGRESS
│   └── done/           # 移到这里 = DONE
├── lane-2-coder/
│   ├── queue/
│   ├── in_progress/
│   └── done/
├── lane-3-reviewer/
└── lane-4-deployer/

# 原子性用 os.rename() 保证(同文件系统 rename 是原子的)
def claim_task(lane: str, agent_id: str) -> Optional[str]:
    queue_dir = f"board/{lane}/queue"
    progress_dir = f"board/{lane}/in_progress"
    tasks = sorted(os.listdir(queue_dir))
    if not tasks:
        return None
    task = tasks[0]
    src = os.path.join(queue_dir, task)
    dst = os.path.join(progress_dir, task)
    try:
        os.rename(src, dst)  # 原子操作!
        return task
    except FileNotFoundError:
        return None  # 被别的 Agent 抢走了

八、什么时候不需要 Kanban?

场景结论
单 Agent,顺序执行任务不需要。一个 Agent 串行执行就够了
多 Agent,但任务完全独立(互不修改共享资源)不需要。并行一把梭更高效
多 Agent,共享文件/数据库/配置需要 Kanban。WIP 限制 + 泳道隔离是刚需
生产级 Agent 编排平台需要的就不只是 Kanban 了——还需要 Kafka 消息队列、分布式锁、Service Mesh

总结

三层理解:

层级理解
初级Kanban 就是个任务板,Agent 从上面领任务
中级WIP 限制是防止 Agent 互相踩踏的关键机制,状态机用原子操作保证一致性
终极多 Agent 协作的本质是分布式共识问题。Kanban 用「单向流转+泳道隔离+WIP 限制」这个约束集,把分布式共识降级为顺序一致性——代价是牺牲最高吞吐,换来确定性和安全

记忆锚点:一个泳道、一个 Agent、一个方向、一个 WIP 上限。