如何设计多Agent

多 Agent 的设计,核心不是"造很多个 Agent",而是把原本塞在一个循环里的职责,拆成多个边界清晰、可独立测试、可独立授权的工作单元,再用一个受控的编排层把它们串起来。如果单 Agent + 状态机已经能稳定解决问题,就不要引入多 Agent。多 Agent 的价值在于:领域隔离、权限隔离、上下文隔离、并行执行、以及不同模型/成本的按需分配。

下面从设计判断、架构模式、状态与通信、编排实现、生产级要点五个方面展开。

一、先判断:你真的需要多 Agent 吗?

多 Agent 会带来额外的通信开销、状态同步复杂度和调试难度。以下信号出现时,才值得拆:

领域知识差异大:一个 Agent 既要懂 SQL,又要懂合同条款,还要写营销文案,Prompt 会互相干扰。

权限差异大:查询数据库和发起支付不应由同一个 Agent 持有凭证。

上下文窗口压力大:不同子任务的中间结果互相污染,导致模型注意力分散。

需要并行:多个独立子任务可以同时执行,缩短端到端延迟。

需要不同模型档位:简单分类用小模型,复杂推理用大模型,按 Agent 分配。

如果只是工具多,优先考虑单 Agent + 工具分组 + 状态机路由,而不是直接上多 Agent。

二、常见多 Agent 架构模式

  1. Supervisor / Orchestrator-Workers(最推荐用于生产) 一个中心化的 Supervisor Agent 负责理解任务、拆解、调度 Worker,并汇总结果。Worker 只执行自己领域的子任务,完成后把结果交回 Supervisor。

用户 → Supervisor → Worker A → Supervisor → Worker B → Supervisor → 最终回答

优点:控制流集中,易于审计、中断、重试、限制轮次。

缺点:Supervisor 可能成为瓶颈;需要精心设计路由 Prompt。

适用:大多数企业级任务,如客服、数据分析、审批流程。

  1. Hierarchical(层级式) Supervisor 下面再挂子 Supervisor,每个子 Supervisor 管理一组 Worker。适合超大型任务,比如"市场分析"下面分"数据采集""竞品分析""报告撰写"三个子团队。

顶层 Supervisor ├── 数据子 Supervisor → SQL Agent / 爬虫 Agent ├── 分析子 Supervisor → 统计 Agent / 归因 Agent └── 撰写子 Supervisor → 文案 Agent / 审校 Agent

  1. Network / Peer-to-Peer(去中心化) Agent 之间通过"交接工具"(Handoff)直接转移控制权。例如 OpenAI Swarm 的模式:Agent A 调用 transfer_to_agent_B,控制权就交给 B。

优点:灵活,适合对话式、边界模糊的场景。

缺点:全局控制弱,容易死循环,生产级慎用。

适用:轻量级、探索性、人工介入频繁的场景。

  1. Pipeline / Sequential(流水线) 按固定顺序执行:

Agent A → Agent B → Agent C

每个 Agent 的输出是下一个的输入。

优点:简单、可预测、易测试。

缺点:无法动态分支,不适合复杂决策。

适用:文档处理、ETL、内容生成流水线。

  1. Blackboard(黑板模式) 所有 Agent 共享一个全局状态(黑板),每个 Agent 监听自己关心的部分,条件满足时触发执行。

优点:解耦,适合事件驱动。

缺点:状态竞争、调试困难。

适用:实时协作、多模态感知融合。

生产级首选 Supervisor 或 Hierarchical,因为它们把控制流显式化,便于加入检查点、中断、权限网关和可观测性。

三、Agent 的契约设计:每个 Agent 是一个"函数"

无论哪种模式,每个 Agent 都应定义清晰的输入输出契约,而不是随意共享全部上下文。

一个 Agent 的规格至少包括:

关键原则:Agent 之间传递的是结构化任务包,而不是完整的对话历史。例如:

{ "task_id": "t-123", "goal": "查询上季度华东区销售额", "constraints": "仅使用只读权限", "输出 JSON", "input_data": {"region": "华东", "quarter": "2025Q1"}, "expected_output": {"schema": {"sales": "number"}} }

Worker 返回结果时,也只返回结构化结果和简短摘要,由 Supervisor 决定是否注入全局状态。这样能避免上下文爆炸和交叉污染。

四、状态与通信:共享状态 vs 消息传递

在 LangGraph 这类状态图框架中,多 Agent 通常通过共享状态通信。状态是全局的,但每个 Agent 只读写自己关心的字段。

全局状态设计示例

python 复制代码
from typing import TypedDict, Annotated
import operator

class MultiAgentState(TypedDict):
    # 全局消息流,用于审计和最终回答
    messages: Annotated[list, operator.add]
    # 当前任务包
    task: dict
    # 各 Agent 产出的结构化结果
    artifacts: Annotated[dict, lambda a, b: {**a, **b}]
    # 下一个要执行的 Agent
    next_agent: str
    # 已执行的 Agent 历史,用于循环检测
    agent_history: Annotated[list, operator.add]
    # 全局重试/轮次计数
    handoff_count: int

私有上下文

每个 Agent 内部可以有自己的私有消息列表,不写入全局状态。例如 SQL Agent 调用模型多次生成 SQL、执行、修正,这些中间步骤只留在 Agent 内部,最终只把 {"sql": "...", "result": ...} 写入 artifacts。

通信方式对比 方式 实现 优点 缺点 共享状态 LangGraph StateGraph 简单、可持久化、可回溯 需设计 reducer,避免竞争 消息传递 消息队列 / 事件总线 解耦、可扩展 延迟高、调试难 Handoff Agent 调用转移工具 灵活、对话自然 控制弱、易循环 生产级推荐共享状态 + 结构化任务包,必要时再引入消息队列做异步解耦。

五、用 LangGraph 实现 Supervisor 多 Agent 下面是一个可运行骨架,展示 Supervisor 如何调度多个 Worker。

python 复制代码
from typing import TypedDict, Annotated, Literal
import operator
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver

1. 定义全局状态

python 复制代码
class State(TypedDict):
    messages: Annotated[list, operator.add]
    next_agent: str
    task: dict
    artifacts: Annotated[dict, lambda a, b: {**a, **b}]
    handoff_count: int

2. 定义 Worker 节点(每个 Worker 内部可以有自己的 tool call 循环)

python 复制代码
def research_agent(state: State):
    # 内部调用 LLM + 工具,返回结构化结果
    result = {"research": "华东区上季度销售额为 1.2 亿"}
    return {
        "artifacts": result,
        "messages": [{"role": "assistant", "content": "研究完成"}],
        "next_agent": "supervisor"
    }

def sql_agent(state: State):
    result = {"sql_result": [{"region": "华东", "sales": 120000000}]}
    return {
        "artifacts": result,
        "messages": [{"role": "assistant", "content": "查询完成"}],
        "next_agent": "supervisor"
    }

def writer_agent(state: State):
    result = {"final_report": "根据数据,华东区上季度销售额为 1.2 亿。"}
    return {
        "artifacts": result,
        "messages": [{"role": "assistant", "content": "报告完成"}],
        "next_agent": "supervisor"
    }

3. Supervisor 节点:决定下一个 Agent 或结束

python 复制代码
def supervisor(state: State):
    # 这里调用 LLM,根据 task、artifacts 和 agent 描述决定 next_agent
    # 简化示例:按顺序调度
    history = [m["content"] for m in state["messages"] if m["role"] == "assistant"]
    if "研究完成" not in history:
        next_agent = "research_agent"
    elif "查询完成" not in history:
        next_agent = "sql_agent"
    elif "报告完成" not in history:
        next_agent = "writer_agent"
    else:
        next_agent = "FINISH"
    return {"next_agent": next_agent, "handoff_count": state.get("handoff_count", 0) + 1}

4. 构建图

python 复制代码
builder = StateGraph(State)
builder.add_node("supervisor", supervisor)
builder.add_node("research_agent", research_agent)
builder.add_node("sql_agent", sql_agent)
builder.add_node("writer_agent", writer_agent)

builder.add_edge(START, "supervisor")

Supervisor 条件路由

python 复制代码
builder.add_conditional_edges(
    "supervisor",
    lambda state: state["next_agent"],
    {
        "research_agent": "research_agent",
        "sql_agent": "sql_agent",
        "writer_agent": "writer_agent",
        "FINISH": END
    }
)

每个 Worker 完成后回到 Supervisor

python 复制代码
builder.add_edge("research_agent", "supervisor")
builder.add_edge("sql_agent", "supervisor")
builder.add_edge("writer_agent", "supervisor")

5. 编译,注入检查点

graph = builder.compile(checkpointer=MemorySaver())

6. 执行

python 复制代码
config = {"configurable": {"thread_id": "task-123"}}
result = graph.invoke(
    {"messages": [{"role": "user", "content": "分析华东区上季度销售并写报告"}],
     "task": {"goal": "销售分析报告"},
     "artifacts": {},
     "handoff_count": 0},
    config=config
)

这个骨架的关键点:

Supervisor 是唯一控制流入口,所有 Worker 完成后必须回到 Supervisor。

Worker 不直接调用其他 Worker,避免网状依赖。

全局状态只保留结构化产物和消息摘要,Worker 内部细节不外泄。

通过 handoff_count 和 agent_history 可以检测循环、限制轮次。

检查点保存器让整个多 Agent 流程可恢复、可中断。

六、生产级多 Agent 的关键工程问题

1. 循环与踢皮球

多个 Agent 互相交接时,容易出现"A 交给 B,B 又交回 A"的死循环。必须设置:

max_handoffs:全局最大交接次数。

agent_history:滑动窗口检测重复模式。

Supervisor 路由 Prompt 中明确禁止无进展的重复交接。

超过阈值后强制转人工或返回错误。

2. 上下文隔离与共享的平衡

共享:任务目标、约束、全局产物索引、最终答案。

隔离:每个 Agent 的内部推理、工具原始返回、临时草稿。

传递:通过结构化任务包,而不是把整个对话历史塞给下一个 Agent。

3. 权限与安全

每个 Worker 持有独立的最小权限凭证。

Supervisor 只负责调度,不应持有业务工具的写权限。

高影响动作(支付、删除、外发)必须经过独立策略网关或人工确认。

外部内容标记为不可信,防止提示注入跨 Agent 传播。

4. 错误处理与降级

Worker 失败时返回结构化错误给 Supervisor,而不是抛出异常中断全局。

Supervisor 根据错误类型决定:重试、换 Agent、降级、转人工。

每个 Worker 有独立超时和重试上限,避免拖垮全局。

使用检查点,失败后可从最后一个成功超步恢复。

5. 可观测性

为每个 Agent 创建独立的 trace span,但共享同一个 thread_id。

记录 Supervisor 的路由决策依据:为什么选择这个 Agent。

记录每次 Handoff 的输入任务包和输出产物。

监控指标:每个 Agent 的调用次数、成功率、Token 消耗、延迟、循环次数。

6. 成本控制

按 Agent 分配模型档位:路由用轻量模型,推理用大模型。

限制每个 Agent 的内部工具循环次数。

对 Worker 返回结果做结构化裁剪,只把必要字段写入全局状态。

监控"每成功任务成本",而不是只看单次调用成本。

七、演进路径:从单 Agent 到多 Agent

不要一步到位。推荐路径:

单 Agent + Tool Call 循环:能跑通基本任务。

单 Agent + 状态图:把循环显式化,加入条件边、检查点、中断。

Supervisor + 2~3 个 Worker:按领域拆分,Supervisor 负责路由。

Hierarchical 或并行 Worker:任务复杂后引入层级或并行执行。

事件驱动 + 消息队列:高并发、长任务时解耦。

每一步都要有评测集验证:任务完成率、平均轮次、成本、延迟、人工介入率。只有当前架构在这些指标上遇到瓶颈时,才引入下一层复杂度。

一句话总结:多 Agent 设计的本质是职责拆分 + 受控编排。用 Supervisor 或层级结构把控制流集中起来,让每个 Agent 只做自己擅长的事,通过结构化任务包通信,共享必要的全局状态,隔离私有上下文,并配上检查点、权限网关、循环检测和可观测性。这样多 Agent 才是生产级的协作系统,而不是一群互相踢皮球的聊天机器人。

相关推荐
高频因子挖掘机1 小时前
盘中筛选要同时看价格和盘口?按需分层比“一次全拿”更好维护
后端·github·api
asong1 小时前
从写代码到部署上线,Cloudflare 给 AI 配了一把新钥匙
前端·javascript·后端
卷无止境1 小时前
当AI写代码遇见Rust为什么会卡壳
后端·python
Percy_kk1 小时前
Django的CRUD映射
后端·django
用户EasyAdminBlazor1 小时前
Blazor Server 性能优化实战:Circuit、数据库、Redis 与并发
后端
王中阳Go1 小时前
Agent 第一句就 500,我们查了三轮:入口日志少打了一个参数
后端·agent·ai编程
羑悻1 小时前
穿越Docker内核迷雾:揭秘镜像分层存储的叠加态与卷挂载的多维空间穿梭技术
后端·docker·容器
励志不掉头发的内向程序员1 小时前
从鼠标点击到画出一条线:CAD 交互层的状态机设计
后端·架构
用户EasyAdminBlazor1 小时前
EasyAdminBlazor 日志系统源码解析:为什么数据库日志需要有界队列?
后端