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. 自测

  1. 四种编排形态里,哪一种的可调试性最差,为什么?
  2. 全量转发在 5 步流水线里的成本增长是什么形状?
  3. "无限互相确认"的根因是模型笨还是缺终止判据?
  4. 三个终止条件里,哪一个最能拦住"看起来在干活"的空转?
  5. 为什么建议每个 Agent 只写自己名字对应的键?
  6. 列出两个"单 Agent 加好工具就够了"的信号。
S21-X10 · 多 Agent 协作:编排形态与失效模式
https://www.clxhxhhr.top/posts/5693/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。