2239 字
约 7 分钟
17
S21-X10 · 多 Agent 协作:编排形态与失效模式
多 Agent 不是"更聪明的单 Agent",它是用通信换专精度。每加一条边,就多一次上下文传递、多一份延迟、多一个可能出错的地方。所以关键问题从来不是"要不要多 Agent",而是"这笔账划不划算"。
下面四种编排形态都能跑,但它们的成本和失效方式完全不同。
1. 四种编排形态
| 形态 | 结构 | 谁决定下一步 | 典型延迟成本 |
|---|---|---|---|
| 主管-工人(supervisor) | 一个调度者 + N 个专家 | 调度者 | 每轮一次路由推理 |
| 流水线(pipeline) | A → B → C,固定顺序 | 代码 | 一次串行链,无额外推理 |
| 黑板(blackboard) | 共享状态区 + 多个读写者 | 谁发现能处理谁上 | 并发写冲突与轮询开销 |
| 对等协商(peer) | 任意两个 Agent 直接对话 | 参与者自己 | 通信次数不可预测 |
| 形态 | 可预测性 | 可调试性 | 适合 |
|---|---|---|---|
| 主管-工人 | 中 | 中(看调度日志) | 任务多样、需要动态分工 |
| 流水线 | 高 | 高(每段独立) | 步骤固定、产出物明确 |
| 黑板 | 低 | 低(状态竞争) | 探索型、多人同时贡献 |
| 对等协商 | 很低 | 很低(无中心日志) | 研究原型、拟人化演示 |
生产上首选的从来是流水线和主管-工人。 黑板与对等协商的失败模式太多,除非业务本身就是"多方可疑地共同推理"。
2. 上下文传递:三种方式,成本差一个数量级
| 方式 | 传什么 | token 成本 | 信息损失 | 适合 |
|---|---|---|---|---|
| 全量转发 | 前序所有消息原文 | 最高,随步数超线性增长 | 无 | 只有 2–3 步的短链 |
| 摘要转发 | 前序内容的摘要 | 中 | 细节可能丢 | 多数生产流水线 |
| 只传结论 | 结构化结果(JSON 字段) | 最低 | 推理路径全丢 | 步骤独立、只需产出物 |
def forward(step_result, mode="conclusion"):
if mode == "full":
return {"messages": step_result["messages"]}
if mode == "summary":
return {"messages": [("ai", summarize(step_result["messages"]))]}
return {"payload": step_result["output"]} # 只传结构化结论
经验规则:下游只需要"结论"的时候,绝不要传"过程"。 一个 5 步流水线如果每步都全量转发,第 5 步的输入往往是第 1 步的十几倍,而这个增长没有任何收益——第 5 步不关心第 2 步纠结过什么。
3. 四种失效模式
| 失效 | 症状 | 成因 | 处理 |
|---|---|---|---|
| 角色重叠 | 两个 Agent 输出高度相似,或互相把活推给对方 | role 描述边界模糊 |
按"产出物"而非"职能"划分角色 |
| 无限互相确认 | "你确认一下""我确认了,你再确认" | 没有终止判据 | 规定确认只能单向一次,加步数上限 |
| 上下文膨胀 | 第 N 步 token 突然翻几倍 | 全量转发 + 历史不压缩 | 摘要/结论转发 + 滑动窗口 |
| 错误放大 | 上游一个小错,下游越写越离谱 | 下游无条件信任上游 | 关键节点加校验,失败即中断而不是继续 |
错误放大最值得警惕:上游把"QPS 1200"写错成"12000",下游基于它算容量、写方案、给出采购建议,最后交付一份看起来很专业的错文档。多 Agent 系统必须有一个"敢停下来"的节点,而不是每一步都假设输入正确。
4. 终止条件:三选三,都设
| 条件 | 判据 | 不设的后果 |
|---|---|---|
| 步数上限 | 总步数 ≥ N(常见 8–15) | 循环到天荒地老 |
| 预算上限 | token / 调用次数 / 金额 ≥ 阈值 | 一次跑飞烧掉整月额度 |
| 无新信息 | 连续 2 轮产出与上一轮相同或高度相似 | 原地打转,看起来在干活 |
def should_stop(state) -> bool:
if state["steps"] >= state["limits"]["max_steps"]:
return True
if state["budget"]["usd"] >= state["limits"]["max_usd"]:
return True
last, prev = state["outputs"][-1], state["outputs"][-2:-1]
if prev and similarity(last, prev[0]) > 0.95: # 两轮几乎一样
return True
return False
第三个条件最容易被忽略,也最实用:模型陷入循环时,产出通常不是"错",而是"重复"。检测重复比检测错误容易得多。
5. 主管-工人的骨架
from typing import Literal
def supervisor(state) -> dict:
decision = route_with_llm(state["task"], WORKERS) # 返回 worker 名或 "done"
return {"next": decision, "steps": 1,
"trace": state.get("trace", []) + [{"node": "supervisor", "next": decision}]}
def make_worker(name, fn):
def node(state):
out = fn(state["task"], state.get("context", {}))
return {"results": {name: out}, # 每人只写自己那一格
"trace": state["trace"] + [{"node": name, "ok": True}]}
return node
g.add_conditional_edges("supervisor", lambda s: s["next"],
{w: w for w in WORKERS} | {"done": END})
for w in WORKERS:
g.add_edge(w, "supervisor") # 干完回调度者
results 用 dict 按 worker 名分格,是避免并发写冲突最省事的写法——每个 Agent 只写自己的键,聚合时再统一读。
6. 可观测性:每步落盘比日志更重要
| 字段 | 用途 |
|---|---|
step / node |
定位是哪一步 |
input_digest |
输入摘要或 hash,能判断"下游到底看到了什么" |
output |
完整产出(大对象存 id) |
tokens / ms |
成本与耗时归因 |
decision |
路由决策及其理由 |
def trace(node, state, out, t0):
rec = {"node": node, "ms": int((time.time()-t0)*1000),
"input_digest": sha(state.get("messages", [])),
"output_id": store(out), "tokens": out.get("usage", {}).get("total")}
json.dump(rec, open(f"runs/{state['run_id']}.jsonl", "a"), ensure_ascii=False)
排障时最常问的问题是"下游为什么给出这个结论",答案只能从下游当时的输入里找。只记输出不记输入,等于只有结论没有证据。
一次运行的 run.jsonl 读起来应该像这样(示意格式):
{"node":"supervisor","next":"researcher","ms":812,"tokens":1204}
{"node":"researcher","input_digest":"a91f","output_id":"obj-77","ms":15320,"tokens":9821}
{"node":"supervisor","next":"writer","ms":640,"tokens":802}
{"node":"writer","input_digest":"c03e","output_id":"obj-81","ms":22110,"tokens":12470}
{"node":"supervisor","next":"done","ms":590,"tokens":410}
读这份记录能直接回答三个问题:钱花在哪个节点、哪一步输入变了、路由为什么这么走。
7. 什么时候单 Agent 加好工具就够了
| 信号 | 含义 |
|---|---|
| 任务能在 3 步内完成 | 拆成多 Agent 只增加通信成本 |
| 分工靠的是"知识"而不是"权限" | 一份系统提示加几个工具就够 |
| 各步骤输入输出高度同构 | 不需要不同的角色人格 |
| 延迟敏感 | 每次跨 Agent 都是一次完整推理 |
| 没有独立可验证的产出物 | 拆开也无法单独验收 |
反过来,值得上多 Agent 的信号是:不同步骤需要不同的工具权限、不同的模型规格、或不同的验收标准。 三条里有两条成立,再拆。
8. 排错
| 现象 | 原因 | 处理 |
|---|---|---|
| 两个 Agent 反复互问 | 终止条件缺失 | 加步数上限 + 单向确认 |
| 第 4 步开始明显变慢 | 全量转发导致上下文膨胀 | 改结论转发 |
| 最终文档错得离谱但每步都"成功" | 错误沿链放大 | 加校验节点,失败即中断 |
| 结果不可复现 | 路由由模型随机决定 | 记录 decision,必要时固定随机种子 |
| 并发时结果少一块 | 共享字段无 reducer | 每人写自己的键 |
| 找不到是哪一步坏的 | 只记输出不记输入 | 落盘每步输入摘要 |
9. 边界
| 场景 | 建议 | 理由 |
|---|---|---|
| 单步查询 + 生成 | 单 Agent + 工具 | 通信成本换不来质量 |
| 固定 5 步内容生产 | 流水线 | 可预测、可单独验收 |
| 任务类型差异大 | 主管-工人 | 需要动态选人 |
| 需要严格审计顺序 | 流水线 + 落盘 | 顺序由代码保证 |
| 探索型研究 | 单 Agent + 多次工具调用 | 多 Agent 只会放大噪声 |
10. 自测
- 四种编排形态里,哪一种的可调试性最差,为什么?
- 全量转发在 5 步流水线里的成本增长是什么形状?
- "无限互相确认"的根因是模型笨还是缺终止判据?
- 三个终止条件里,哪一个最能拦住"看起来在干活"的空转?
- 为什么建议每个 Agent 只写自己名字对应的键?
- 列出两个"单 Agent 加好工具就够了"的信号。
S21-X10 · 多 Agent 协作:编排形态与失效模式
https://www.clxhxhhr.top/posts/5693/ 评论
0 条
还没有评论,先写一条吧。