Agent 系列(9):多 Agent 架构设计模式——Supervisor 与 Pipeline

为什么一个 Agent 不够用?

前面八篇文章里,我们构建的都是单 Agent:一个 LLM,一组工具,一条对话历史。这套架构能解决大多数问题。

但有些任务天然是"多专家"的:

  • 写一篇技术文章,需要研究员收集资料、写手起草、编辑润色------三个角色,三种思维方式
  • 处理用户工单,需要意图识别、知识库查询、回复生成------三个阶段,独立可测
  • 代码评审,需要静态分析、安全扫描、可读性审查------三个维度,互不干扰

用单 Agent 处理这些任务并非不可以,但你会发现 System Prompt 越写越长,输出质量越来越不稳定------因为你在强迫一个角色扮演所有人。

多 Agent 的核心价值:职责分离,每个 Agent 只做一件事,做好它


两种主流架构模式

多 Agent 系统有多种拓扑结构,其中两种最常见:

sql 复制代码
Supervisor 模式(动态路由):
  classify → supervisor → researcher
                       ↘ writer
                       ↘ reviewer
                       ↘ FINISH

  特点:有一个"指挥中心",决定下一步调哪个 Agent

Pipeline 模式(固定顺序):
  outline_agent → draft_agent → polish_agent → END

  特点:执行路径硬编码,每个 Agent 只知道自己的上下文和下一个节点

两种模式不是竞争关系,而是适用场景不同。


Demo 1:Supervisor 模式

设计思路

Supervisor 模式的挑战在于路由可靠性。如果让 LLM 每一步都决定"下一个调谁",它会出现:

  • 重复调用同一个 Worker
  • 忘记记录已调用过的 Worker
  • 不知道何时该终止

更好的设计是两阶段混合

yaml 复制代码
Phase 1: LLM 做一次任务分类(simple_fact vs full_article)
Phase 2: Python 根据分类 + 已调用列表做确定性路由

LLM 负责"看清楚这是什么任务",Python 负责"按规则执行"。

LangGraph 实现

python 复制代码
class SupervisorState(TypedDict):
    messages: Annotated[list, add_messages]
    task: str
    task_type: str   # "simple_fact" or "full_article"
    called: list[str]
    next: str


def classify_node(state: SupervisorState) -> SupervisorState:
    """LLM 做一次分类,结果写入 state,后续路由全程可用"""
    decision = _ask(
        "Classify this task:\n"
        "  simple_fact  --- a factual question with a direct short answer\n"
        "  full_article --- needs research, writing, and editorial review\n"
        "Output one word only: simple_fact / full_article",
        f"Task: {state['task']}",
    ).strip().lower()
    task_type = "full_article" if "full_article" in decision else "simple_fact"
    return {**state, "task_type": task_type}


def supervisor_node(state: SupervisorState) -> SupervisorState:
    """纯 Python 路由,不调 LLM,不会循环"""
    called = state["called"]
    task_type = state["task_type"]

    if "researcher" not in called:
        next_worker = "researcher"
    elif task_type == "simple_fact":
        next_worker = "FINISH"          # 简单问题:研究完就结束
    elif "writer" not in called:
        next_worker = "writer"
    elif "reviewer" not in called:
        next_worker = "reviewer"
    else:
        next_worker = "FINISH"

    return {**state, "next": next_worker}

图的拓扑结构

python 复制代码
g = StateGraph(SupervisorState)
g.set_entry_point("classify")
g.add_edge("classify", "supervisor")
g.add_conditional_edges(
    "supervisor",
    route_supervisor,
    {"researcher": "researcher", "writer": "writer",
     "reviewer": "reviewer", "FINISH": END},
)
g.add_edge("researcher", "supervisor")
g.add_edge("writer", "supervisor")
g.add_edge("reviewer", "supervisor")

classify → supervisor → [workers] → supervisor → ... → FINISH 形成一个可控的循环。

实测执行结果

任务:"Write a short article about Python list comprehensions"

less 复制代码
[classify] task_type = full_article
[supervisor] → researcher
[researcher] working...
[supervisor] → writer
[writer] working...
[supervisor] → reviewer
[reviewer] working...
[supervisor] → FINISH

Workers called: ['researcher', 'writer', 'reviewer']
Task type     : full_article

classify 只调用一次,后续路由全程由 Python 掌控,执行链干净清晰:researcher → writer → reviewer → FINISH


Demo 2:Pipeline 模式

Pipeline 的代码量比 Supervisor 少很多------因为不需要路由逻辑:

python 复制代码
class PipelineState(TypedDict):
    topic: str
    outline: str
    draft: str
    polished: str
    stage_log: list[str]


def outline_agent(state: PipelineState) -> PipelineState:
    outline = _ask("创建一个 5 点大纲...", state["topic"])
    return {**state, "outline": outline, "stage_log": [...]}

def draft_agent(state: PipelineState) -> PipelineState:
    draft = _ask("基于大纲写 200 字草稿...", state["outline"])
    return {**state, "draft": draft, "stage_log": [...]}

def polish_agent(state: PipelineState) -> PipelineState:
    polished = _ask("润色...", state["draft"])
    return {**state, "polished": polished, "stage_log": [...]}

# 拓扑:outline → draft → polish → END
g.add_edge("outline_agent", "draft_agent")
g.add_edge("draft_agent", "polish_agent")
g.add_edge("polish_agent", END)

实测执行结果

ini 复制代码
[outline_agent] 957 chars
[draft_agent] 1846 chars
[polish_agent] 2168 chars

最终输出(前 300 字):
"### Unveiling the Power of List Comprehensions in Python
Python's lists are dynamic and powerful data containers...

每个阶段的输出都是下一阶段的输入,内容逐步丰富:大纲 957 字符 → 草稿 1846 → 润色 2168。

整个流程没有任何 LLM 路由决策------路径在写代码时就确定了。


Demo 3:同一张图,不同执行路径

Supervisor 模式最直观的优势:不改代码,不同任务自动走不同路径

用同一个 Supervisor 图跑一道简单的事实问题:

ini 复制代码
任务:"What year was Python created?"

[classify] task_type = simple_fact
[supervisor] → researcher
[researcher] working...
[supervisor] → FINISH

Workers called : ['researcher']
Task type      : simple_fact
结果:researcher → FINISH(writer + reviewer 被跳过)

对比两次运行:

java 复制代码
任务                              Workers 调用链                 步骤数
──────────────────────────────────────────────────────────────────────
Write article (full_article)      researcher→writer→reviewer     3
Factual question (simple_fact)    researcher                     1

同一个 Supervisor 图,根据任务分类自动选择执行路径。

Pipeline 做不到这件事------它的路径在代码里已经写死了。


模式选择矩阵

复制代码
维度              Pipeline                   Supervisor
──────────────────────────────────────────────────────────────────────
执行路径          固定,写死在代码里          动态,分类驱动
最适合场景        ETL 流水线、文档处理        研究、开放问答、混合任务
可调试性          高(线性 trace)            中(路径随任务变化)
LLM 调用次数      N(每阶段一次)             N + 1(多一次分类调用)
灵活性            低                          高
可预测性          高                          较低
实现复杂度        简单                        中等

经验法则:

  • 明确知道执行哪几步 → Pipeline
  • 需要根据任务决定步骤 → Supervisor

多 Agent 设计 Checklist

架构选型

  • 任务步骤固定且已知 → Pipeline;需要动态决策 → Supervisor
  • Worker 数量 ≤ 3 且职责清晰时,两种模式都可行
  • Worker 数量 > 5 时,考虑分层 Supervisor(Supervisor of Supervisors)

State 设计

  • 每个 Worker 只读自己需要的字段,只写自己生成的字段
  • Supervisor 状态必须包含 called: list[str],用于路由去重
  • Pipeline 状态按阶段命名(outline, draft, polished),便于调试

路由可靠性

  • 避免纯 LLM 路由(LLM 无法可靠追踪调用历史)
  • 推荐:LLM 做一次分类 + Python 做确定性路由
  • 设置 recursion_limit(建议 20--30),防止意外循环

Worker 设计

  • 每个 Worker 只做一件事,输入输出明确
  • Worker 之间通过 State 传递结果,不直接相互调用
  • 写好 Worker 的 System Prompt,不要让它猜测上下文

可观测性

  • 在每个节点打印执行日志(worker 名称、输入/输出摘要)
  • 记录 called 列表,方便回溯路由决策
  • 对异常分支(如 FINISH 提前触发)加 warning log

总结

五个核心结论:

  1. 多 Agent 的本质是职责分离:不是为了炫技,而是当单 Agent 的 Prompt 开始失控时,拆成多个专注的角色
  2. Pipeline 胜在简单可预测:执行路径写在代码里,trace 是线性的,测试和调试成本最低
  3. Supervisor 胜在自适应:同一张图,简单问题一步到位,复杂任务全程走完,不需要改代码
  4. LLM 路由 + Python 执行是最佳搭档:让 LLM 做分类(它擅长),让 Python 做路由(它可靠)
  5. called 列表是 Supervisor State 的关键字段:路由确定性的基础,缺了它 Supervisor 容易出现重复调用或死循环

参考资料


更多实用知识和有趣产品,欢迎访问我的个人主页

相关推荐
冬奇Lab1 小时前
每日一个开源项目(第118篇):SkillOpt - 像训练神经网络一样优化 LLM Agent 的技能
人工智能·开源·agent
chengzi_beibei2 小时前
浏览器自动化的下一层:为什么 CloakBrowser 把指纹问题推到了源码层?
人工智能
甲维斯2 小时前
免费的Qwen3.7max终于来了!
人工智能
摆烂大大王2 小时前
玩转 OpenClaw:用 TaskFlow + Heartbeat 打造自动化工作流
前端·人工智能·自动化
zhangfeng11332 小时前
AI 每日动态推送|2026-05-30 codidng 机器人方向
人工智能·机器人
zhangxingchao2 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
陆业聪2 小时前
WebView代理方案实现:拦截请求、注入资源与离线包架构
人工智能·ai编程