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 容易出现重复调用或死循环

参考资料


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

相关推荐
Linguwen2 分钟前
中山GEO分享会回顾文
人工智能·chatgpt
武子康4 分钟前
多 Agent 不是多开几个终端:Pi 的 Sub-agent 取舍
人工智能·llm·agent
俊哥V11 分钟前
每日 AI 研究简报 · 2026-08-22
人工智能·ai
龙虾PRO15 分钟前
数据中台 + AI 落地:2026 五大趋势与零售出海完整实操案例
人工智能·零售
wish36620 分钟前
K8S 免镜像部署:通过 API 上传文件动态创建服务(以 Ollama 为例)
人工智能·云原生·容器·kubernetes·local llm
AI导出鸭PC端26 分钟前
文心怎样生成word文档?一键智能排版,AI导出鸭解决格式错乱痛点
人工智能·ai·word·豆包·deepseek·ai导出鸭
深小乐27 分钟前
DeepSeek Harness 发布一周,登顶 Top10 的插件,暴露了哪些真实需求?
人工智能
奈斯先生Vector36 分钟前
OpenScience 安装失败怎么办?Windows 环境、API 配置与本地服务排错指南
人工智能·windows·架构·开源·aigc·midjourney
武子康36 分钟前
Email Thread 不是 Agent Session:生产级异步通信网关的状态、幂等与审批合同
人工智能·llm·agent