等级制度与分权:Manager-Worker 架构的平衡术
What(本文讲什么)
Manager-Worker 不是“多开几个 agent 聊天”,而是一套可运行的编排系统:把一个长任务拆成工单(ticket),把工单派发给职责单一的 worker,并用门禁(超时、重试、幂等、权限、隔离、审计、观测、回滚、降级)保证它不会在长时间运行中失控。
这篇文章会把 manager-worker 讲成三个工程对象:
- 工单协议(Ticket Protocol):如何把模糊目标变成可执行步骤。
- 监督与恢复(Supervision):worker 会失败,系统要能自动隔离失败并恢复。
- 治理与审计(Governance):任何副作用都必须有提交记录(WAL)与幂等 key。
Problem(要解决的工程问题)
单体 agent 在复杂任务里会出现两类硬崩:
- 上下文崩:任务跨度大、文件多、错误多,context window 被撑爆。
- 控制回路崩:一个步骤失败后反复重试,最终进入 token 风暴与资源泄露(重试、超时、资源释放)。
manager-worker 的价值是把复杂度拆成可控的子问题,但它也会引入新的风险:
- 并发冲突:多个 worker 写同一资源(并发)。
- 重试副作用:worker 重试导致重复提交(幂等)。
- 归因困难:谁做了什么、为什么做、谁批准(审计、观测)。
所以本章的核心不是“分工”,而是“把分工做成可治理系统”。
Principle(系统结构:工单系统 + 监督树 + 门禁层)
1) 工单系统:把语言变成协议
manager 给 worker 的输入必须结构化。否则你得到的只是“把模糊问题转发给另一个模型”。
一个最小工单(ticket)建议包含:
task_id: 全局唯一,贯穿 trace 与日志(审计、观测)。role: worker 类型(coder/tester/reviewer/...)。context_pointers: 需要读取的文件/目录/日志位置(避免全量塞入)。success_criteria: 明确验收条件(例如测试必须通过)。constraints: 超时/重试上限/权限域(超时、重试、权限)。idempotency_key: 若可能产生副作用,必须提供(幂等)。
2) 监督树:失败隔离与自动恢复
worker 会失败是常态。工程系统要做的不是“让它不失败”,而是:
- 失败不会扩散到全局。
- 失败可以被自动恢复或降级。
- 失败会留下证据链(审计)。
Erlang 的 supervision tree 设计原则是经典参考:把失败当作正常路径,用监督策略隔离与恢复,并且需要 backoff/上限避免“自激重启风暴”。
参考: https://www.erlang.org/doc/design_principles/des_princ
3) durable execution:长任务必须可中断与可恢复
长任务一定会遇到中断(网络失败、模型限流、机器重启)。所以编排必须可检查点、可恢复。LangGraph 的 durable execution 文档给了很清晰的工程方向:把执行状态持久化,并支持恢复继续执行。
参考: https://docs.langchain.com/oss/python/langgraph/durable-execution
Usage(怎么用:一个可落地的 manager-worker 编排骨架)
1) Ticket Schema(示例)
{
"task_id": "t-20260421-001",
"role": "coder",
"context_pointers": ["file:src/foo.ts", "log:test-run-123"],
"success_criteria": ["unit_tests_pass", "no_lint_errors"],
"constraints": {"timeout_ms": 600000, "max_retries": 2},
"permission_scope": {"fs_write": ["src/"], "net": "deny"},
"idempotency_key": "idem-t-20260421-001-step-2"
}
2) Worker 输出必须结构化(否则不可审计)
worker 不应该把 1000 行日志甩回 manager。它应该返回:
- 变更清单:改了哪些文件,patch_id 是什么。
- 验证结果:跑了哪些命令,是否超时,失败原因标签。
- 风险点:是否触发重试、是否遇到权限拒绝(超时、重试、权限)。
3) 统一治理:把副作用收口到提交层
最容易翻车的一点是让 worker 自己随便写文件。更可靠的方式是:
- worker 只产出 patch。
- manager(或 orchestrator)作为唯一提交者,执行:
- schema 校验
- 权限检查
- 幂等 key 生成与 WAL 记录(幂等、审计)
- 提交与回滚策略(回滚、降级)
这样“多 worker 并发”也不会直接变成“多处自由写”。
4) 重试策略:重试不是免费午餐
重试必须被系统化:
- 每个 ticket 有
max_retries(重试)。 - 每次重试必须记录
retry_reason(观测)。 - 对有副作用的步骤必须使用
idempotency_key(幂等)。 - 触发阈值后降级为只读诊断或人工介入(降级)。
任务队列体系(例如 Celery)在确认/重试语义上提供了大量成熟经验:至少一次投递意味着你必须在业务层做幂等与去重。
参考: https://docs.celeryq.dev/en/stable/
Design(设计取舍:什么时候不用 manager-worker)
manager-worker 不是万能。你不应该在这两类场景硬上:
- 超小任务:分解成本大于收益。
- 需要强一致提交的场景:应该优先设计“提交协议与 WAL”,而不是靠对话协作。
它适合的场景是“任务跨度大,但步骤能被切成独立工单”的工程任务。
Pitfall(常见坑与防错)
- manager 过度微操:把上下文塞爆,worker 变成“复读机”(降级)。
- worker 输出不结构化:不可审计、不可复盘(审计)。
- 多 worker 并发写:冲突与回滚地狱(并发、回滚)。
- 无幂等 key:重试制造重复副作用(幂等、重试)。
- 无检查点:长任务中断后只能重跑(超时、资源释放)。
Debug(如何定位 manager-worker 的问题)
排查顺序建议:
- 先看工单:ticket 是否包含成功标准、超时、重试上限、权限域?
- 再看提交:是否有唯一提交者?是否写 WAL?是否有幂等 key?
- 再看观测:失败原因分布是什么?是超时、权限拒绝还是逻辑错误?
一个可运行的编排状态机(把“协作”落到可恢复执行)
把 manager-worker 变成系统,关键是给它一个状态机。最小状态机建议如下:
created:任务创建,生成 root trace。planned:生成初始工单列表(plan tickets)。dispatched:派发给 workers,写入任务队列。collecting:收集 worker 结构化结果。verifying:运行验证(测试/静态检查)。committing:唯一提交者写 WAL 并提交副作用(幂等、审计)。completed:成功结束。blocked:需要人工介入或权限提升(降级)。
每次从一个状态迁移到下一个状态,都应该写检查点(checkpoint),这样中断后可以恢复继续执行(超时、资源释放)。
最小 orchestrator 伪代码(强调超时/重试/幂等)
class Orchestrator:
"""
编排器:
负责派发工单、收集结果、验证、并在提交点写 WAL。
"""
async def run(self, task):
state = await self._load_or_create_state(task)
tickets = await self._plan(task)
results = []
for t in tickets:
r = await self._dispatch_with_retry(t, timeout_ms=t.constraints.timeout_ms, max_retries=t.constraints.max_retries)
results.append(r)
await self._verify(results)
# 唯一提交点:生成幂等 key,写 WAL,再提交
idem = self._make_idempotency_key(task)
wal_id = await self._wal.append(task_id=task.id, idempotency_key=idem, resources=self._resources(results))
await self._commit(results, wal_id=wal_id, idempotency_key=idem)
这段伪代码故意只强调三件事:
- 每个派发都有超时与重试上限(超时、重试)。
- 提交点只有一个(避免并发双写)(并发)。
- 提交点必写 WAL 并生成幂等 key(幂等、审计)。
Worker 的“汇报模板”(避免把 manager 塞爆)
Worker 最好的汇报是“结构化 + 可跳转”,而不是长文本:
changed_files:文件列表patch_id:patch 位置commands_run:跑过的命令与退出码failures:失败原因标签与关键行timing:耗时与是否超时
把汇报做成模板后,manager 的上下文装配就可以稳定复用,降低漂移风险(降级)。
Source(资料来源)
- LangGraph durable execution: https://docs.langchain.com/oss/python/langgraph/durable-execution
- Erlang supervision principles: https://www.erlang.org/doc/design_principles/des_princ
- Celery docs (retries/acks): https://docs.celeryq.dev/en/stable/
- AutoGen paper: https://arxiv.org/abs/2308.08155