[Ai Agent] 12 Swarm 与 Agents SDK —— 去中心化的多智能体协作

博客配套代码发布于github12 Agents SDK & Swarm 模式

相关Agent专栏:Ai Agent教学

本系列所有博客均配套Gihub开源代码,开箱即用,仅需配置API_KEY。

如果该Agent教学系列帮到了你,欢迎给我个Star⭐

知识点Swarm | Handoff | Context_variables | Agents SDK | 去中心化


前言、

在上一篇 M11 中,我们借助 MCP 协议,让 Agent 能够自由调用任意外部工具------无论是查天气、发飞书,还是调用高德地图,都变得像插 USB 一样简单。

但现实中的任务往往不是单打独斗,而是多人协作

比如"航空公司客服"系统:

  • 有人负责退票,
  • 有人处理改签,
  • 有人追踪行李。

面对这种需求,我们有两种主流解法:

  • 老办法(M09:LangGraph) :设一个"车间主任"(Supervisor),所有请求先汇总给它,由它决定派活给谁、何时汇总结果。这是一种中央控制式架构。
  • 新方法(本篇:Swarm 模式) :像一场接力赛 ------前台接待员处理不了,就直接把电话转接 给退票专员;如果退票专员也搞不定,再转给上级......整个过程无需总控,自主交接

本篇,我们将使用 OpenAI 官方推出的 Agents SDK,亲手实现这种轻量、灵活、高内聚的协作模式。

一、Langgraph vs Swarm:选对工具

要真正理解 Swarm 的价值,最好的方式是和 LangGraph 对比。

▶ LangGraph:适合"确定性流程"

当你用 LangGraph 构建多 Agent 系统时,本质上是在画一张状态转移图

  • 每个节点(Node)是一个 Agent 或工具;
  • 每条边(Edge)是一条路由规则(比如"如果用户说'改签',跳转到 ChangeAgent");
  • 通常还有一个 Supervisor 节点,作为"大脑"协调所有子 Agent。

这种方式强大但繁重

  • 你需要同时设计节点逻辑 + 路由逻辑;
  • 流程越复杂,图就越庞大,维护成本越高;
  • 但它能保证执行顺序确定、可审计、可回溯------适合金融审批、医疗诊断等强流程场景。

▶ Swarm:适合"自由协作"

Swarm 的设计哲学恰恰相反:去掉大脑,让手脚自己说话

它把 LangGraph 中 Supervisor 管理的那些子 Agent(a、b、c)直接释放出来,每个 Agent 自己决定:

"这事我干不了,应该交给谁?"

你不再需要定义 Node、Edge、条件跳转------只需专注每个 Agent 的核心能力与可转接对象(handoffs)

开发效率大幅提升,代码也极度简洁。

💡 一句话理解 Swarm

它就像是把 LangGraph 里的 Supervisor "拆掉",让原本被调度的子 Agent 获得自主权,彼此直接对话。

▶ 那么,该如何选择?

场景 推荐方案 原因
单 Agent + 工具调用 LangChain / 原生 LLM 调用 结构最简,无需引入多 Agent 框架
多 Agent 自由协作 (如客服、导购、分诊) Swarm 模式(Agents SDK) 角色清晰、转接灵活、开发高效
多 Agent 强流程控制 (如工单审批、合规检查) LangGraph 时间轴明确、状态可控、可追溯

没有"最好",只有"最合适"

Swarm 不是 LangGraph 的替代品,而是它的轻量化补充------专治"不需要复杂编排,只要几个专家能互相配合"的场景。

二、Swarm 与 Agents SDK:思想 vs 工程

前文我们花了较多篇幅对比 Swarm 与 LangGraph ,但从严格意义上讲,这种对比并不完全对等。

真正与 LangGraph 同级的,其实是 Agents SDK ------ 它才是一个完整的、可用于生产环境的多智能体开发框架。

那么,Swarm 到底是什么?

Swarm 并不是一个框架,而是一种设计思想(Design Pattern),其核心主张是:

去中心化协作 + 自主任务转接(Handoff)

它刻意不设中央控制器 ,也不显式定义流程图或状态机。每个 Agent 只需知道自己能做什么、以及"干不了时该转给谁"。代码极简,理念清爽,非常适合教学和原型验证。

但也正因如此,原生 Swarm 存在明显局限

  • 不支持异步并发;
  • 缺乏工具集成机制;
  • 没有上下文管理、安全护栏、可观测性等工程能力;
  • 难以支撑真实业务场景的稳定性与扩展性。

换句话说:Swarm 适合"讲清楚原理",但不适合"跑在生产环境"

Agents SDK 正是为了弥补这一 gap 而诞生的。

它是 OpenAI 对 Swarm 思想的系统化工程封装,不仅保留了"自主 Handoff"的核心体验,还全面增强了以下能力:

  • ✅ 原生异步支持(async/await)
  • ✅ MCP 协议深度集成(无缝调用你在 M10--M11 构建的工具服务)
  • ✅ 统一上下文管理(共享背包)
  • ✅ 安全护栏(Guardrails)、全链路追踪(Tracing)等企业级特性

正因如此,它被命名为 Agents SDK ,而非 "Swarm 2.0"------

因为它不只是 Swarm 的简单升级,而是一个融合了多智能体最佳实践的完整开发框架

更重要的是,Agents SDK 并非要取代 LangGraph。

相反,它在设计上借鉴了 LangGraph 对多 Agent 的结构化抽象 ,同时采纳了 Swarm 去中心化、自主转接(Handoff)的协作理念,在开发效率、系统灵活性与工程可靠性之间,找到了一条更实用的路径。

📌 一句话总结

Swarm 提出了一个好想法,Agents SDK 把它变成了可落地的工具。

接下来,我们将先基于 Swarm 的核心思想,用 Agents SDK 实现一个典型的多 Agent 协作场景;

随后再逐步展开它的高级能力------MCP 集成、安全护栏、上下文管理等,带你全面掌握这个新一代智能体开发平台。

三、核心机制:接力棒与背包

在 Agents SDK 中,实现 Swarm 式协作只需掌握两个核心概念:

1. 接力棒:Handoff(转接)

这是 Swarm 模式的灵魂------任务交接由 Agent 自主触发,无需外部调度

在代码中,转接非常简单:一个函数直接返回另一个 Agent 对象

python 复制代码
def transfer_to_sales():
    """用户想购票时,转接给销售专员"""
    return sales_agent

def transfer_to_refund():
    """用户申请退款或取消行程时,转接给退票专员"""
    return refund_agent

这些函数会被注册为 Agent 的"可选动作"。

当用户说"我要退钱",当前 Agent 会根据指令和上下文,自主决定调用 transfer_to_refund()

SDK 检测到返回值是一个 Agent,便立即把对话控制权"交棒"过去。

关键点

你通过代码定义了"有哪些路可走",而 LLM 根据对话内容动态选择走哪一条


2. 共享背包:Context Variables(上下文)

接力赛中,交棒的同时,信息也得跟着传递。

Agents SDK 通过一个全局的 字典 context_variables(即"共享背包")来实现这一点。

  • 用户刚接入时,系统将客户信息(如"姓名:张三,航班 CA1234")放入背包;
  • 前台 Agent 背着这个包接待;
  • 当发生 Handoff 时,背包自动传递给下一个 Agent
  • 退票专员拿到背包后,可在自己的提示词中动态引用客户信息,无需重复询问。
python 复制代码
def refund_instructions(context_variables):
    user_info = context_variables.get("user_info", "未知用户")
    return f"你是退票专员。当前客户:{user_info}。请按流程处理退款。"

理解了"接力棒"和"共享背包",你就掌握了 Swarm 协作的全部基础。

接下来,我们将在实战中用 Agents SDK 搭建一个完整的多 Agent 客服系统。

四、代码实战:航空公司客服集群

本节我们将用 Agents SDK 搭建一个由三个 Agent 组成的客服系统:

  • 前台分诊员:接收用户请求,判断意图;
  • 退票专员:处理退款;
  • 改签专员:处理航班变更。

它们之间通过 Handoff(转接) 自主协作,无需预设复杂路由逻辑------谁该接手,由当前 Agent 根据上下文实时决定。


1. 环境准备

bash 复制代码
# 仅需一行命令(要求 Python >= 3.10)
pip install openai-agents

2. 定义"共享背包"与"工具手脚"

  • 共享背包(Context):用于在所有 Agent 间传递用户信息;
  • 工具(Tools):Agent 可调用的业务函数,使用 @function_tool 装饰器注册。
python 复制代码
# tools
from agents import function_tool

# 全局上下文,所有 Agent 共享
context_variables = {
    "user_name":"张三(白金会员)",
    "flight_no":"CA1234"
}

# 业务工具
@function_tool
def execute_refund():
    """执行退款逻辑"""
    return "✅️ 退款申请已提交,预计3个工作日内到账。"

@function_tool
def check_seat():
    """查询座位余量"""
    return "✅ 明日航班尚有余票。"

💡 提示:工具写法与 LangChain/LangGraph 类似,只需将 @tool 替换为 @function_tool。

3. 创建并连接 Agent

我们先创建三个 Agent,再通过 handoffs 属性建立"转接网络"。

python 复制代码
# agent.py
from openai import AsyncOpenAI
from agents import Agent,OpenAIChatCompletionsModel,function_tool
from config import OPENAI_API_KEY
from m12_agents_sdk_swarm.tools import execute_refund,check_seat

client = AsyncOpenAI(api_key=OPENAI_API_KEY,base_url="https://api.deepseek.com") # 创建异步OpenAI客户端实例
model = OpenAIChatCompletionsModel(model="deepseek-chat",openai_client=client) # 创建模型实例


# === 1.创建所有Agent(先不指定handoffs) ===

# 退票专员
refund_agent = Agent(
    name="RefundAgent",
    instructions="得知用户想退票后,直接调用execute_refund退票工具,无需确认其他信息。如果用户想改签或咨询其他问题,请转接。",
    tools=[execute_refund],
    model=model
)

# 改签专员
change_agent = Agent(
    name="ChangeAgent",
    instructions="得知用户想改签后,直接调用check_seat改签工具,无需确认其他信息。如果用户想退票或咨询其他问题,请转接。",
    tools=[check_seat],
    model=model,
)

# 前台分诊员(动态提示词)
triage_agent = Agent(
    name="TriageAgent",
    # 动态指令:读取用户背包里的用户名,实现"千人千面"
    instructions=lambda context, __: (
        f"你是前台,客户名:{context.context.get('user_name')}。根据意图转接。"
    ),
    model=model
)
建立转接关系(织网)

为了避免循环引用,我们在所有 Agent 实例化后再设置 handoffs:

python 复制代码
# 前台可转给两位专员
triage_agent.handoffs = [refund_agent, change_agent]

# 专员之间也可互相转接,并能回退到前台
refund_agent.handoffs = [change_agent, triage_agent]
change_agent.handoffs = [refund_agent, triage_agent]

✅ 这样就形成了一个全联通的协作网络 :每个 Agent 都知道自己"能找谁帮忙",但具体找谁,由 LLM 动态决策

4. 启动引擎:事件驱动的流式对话

Agents SDK 采用事件驱动架构,运行时会持续推送各类事件(文本生成、Agent 切换、工具调用等)。我们需要监听这些事件,实时响应。

以下是精简后的主循环逻辑:

python 复制代码
import asyncio
from agents import Runner, set_tracing_disabled

# 关闭 OpenAI 官方 Tracing(避免报错)
set_tracing_disabled(True)

async def main():
    print("✈️ 航空公司客服系统启动...\n")
    messages = []
    current_agent = triage_agent

    while True:
        user_input = input("\n👤 用户: ")
        if user_input == "quit":
            break
        messages.append({"role": "user", "content": user_input})

        # 启动流式执行
        result = Runner.run_streamed(current_agent, input=messages, context=context_variables)
        
        agent_name = None
        is_printing = False

        async for event in result.stream_events():
            # 文本输出(逐 Token)
            if event.type == "raw_response_event":
                if not is_printing:
                    print(f"🤖 [{agent_name or '...'}] ", end="", flush=True)
                    is_printing = True
                print(event.data.delta, end="", flush=True)

            # Agent 切换
            elif event.type == "agent_updated_stream_event":
                new_name = event.new_agent.name
                if agent_name and agent_name != new_name:
                    if is_printing:
                        print()  # 换行
                    print(f"🔀 [转接] {agent_name} → {new_name}")
                    is_printing = False
                agent_name = new_name

            # 工具调用
            elif event.type == "run_item_stream_event":
                item = event.item.raw_item
                if event.name == "tool_called":
                    if is_printing:
                        print()
                    tool_type = "📞 [转接]" if item.name.startswith("transfer_") else "🔧 [工具]"
                    print(f"{tool_type} {item.name}")
                elif event.name == "tool_output":
                    print(f"✅ [结果] {event.item.output}")
                    is_printing = False

        if is_printing:
            print()

        # 更新状态
        messages = result.to_input_list()
        current_agent = result.last_agent

if __name__ == "__main__":
    asyncio.run(main())

📌 设计要点

  • 使用 run_streamed() 实现流式响应;
  • 通过事件类型区分文本、转接、工具调用;
  • 动态更新 current_agent,确保下一轮对话直接与最新 Agent 交互。

测试运行:

运行成功。如感觉展现过程略复杂,可根据实际需求做删改。

5. 实用注意事项(避坑指南)

Agents SDK 仍在快速迭代中,使用时需注意以下细节:

✅ 5.1 Agent 名称必须符合标识符规范

name 字段不支持中文或特殊字符,只能包含字母、数字和下划线(如 "RefundAgent" )。否则可能引发内部解析错误。

✅ 5.2 默认启用 OpenAI Tracing,需显式关闭或配置

SDK 默认尝试上报运行日志到 OpenAI 的追踪服务(类似 LangSmith)。若未提供 API Key,会频繁报错。

  • 不想用 -- 加一行关闭:

    python 复制代码
    from agents import set_tracing_disabled
    set_tracing_disabled(True)
  • 想用 -- https://platform.openai.com/account/api-keys:申请 OpenAI 平台 API Key 并配置:

    python 复制代码
    from agents import set_tracing_export_api_key
    set_tracing_export_api_key("your-openai-api-key")

🔍 Tracing 功能非常有用:可查看每一步推理、工具调用耗时、Token 消耗等,强烈建议在开发阶段开启。

五、深度架构观点:Agents SDK vs LangGraph

经过上文实战,我们可以更深入地探讨 Agents SDKLangGraph 在智能体协作架构上的本质差异,并回答一个关键问题:

为什么说 Agents SDK 的设计(原生异步、事件驱动、弱 Schema)比 LangGraph 更适合构建动态、响应式的多智能体系统?

我们从五个维度进行对比,用"LangGraph 的逻辑"对照"Agents SDK 的实现"。

5.1 运行模式:步骤(Steps) vs 事件流(Event Stream)

  • LangGraph(Step-based)

    本质是一个状态机。执行逻辑是"走一步,停一下":必须等一个 Node 完全执行完毕、状态更新后,才能进入下一个 Node。在涉及工具调用或长思考链的场景中,这会导致较高的 首字延迟(TTFT) ------用户只能看着加载圈,无法获得任何即时反馈。

  • Agents SDK(Event-Driven)

    基于 asyncio 的事件循环模型。通过 Runner.run_streamed() 返回一条实时事件流,即使 Agent 仍在推理或工具尚未返回,系统也能立即推送高层级事件(如"开始调用地图工具"、"即将转接至退票专员")到前端。

python 复制代码
# main.py:消费实时事件流
result = Runner.run_streamed(cur_agent, input=messages, context=context_variables)

async for event in result.stream_events():
    if event.type == "raw_response_event":
        print(event.data.delta, end="")  # 打字机式输出
    elif event.type == "agent_updated_stream_event":
        print(f"🔀 [系统]: {event.old_agent.name} → {event.new_agent.name}")
    elif event.type == "run_item_stream_event" and event.name == "tool_called":
        print(f"🔧 [工具]: {event.item.raw_item.name}")

优势:用户体验更流畅,系统行为透明可感知。


5.2 上下文管理:强类型 Schema vs 动态字典

  • LangGraph(Strict Schema)

    要求预先定义 TypedDict 或 Pydantic 模型来规范 State(如 class State(TypedDict): messages: list)。一旦业务需要新增字段(如 user_tier: str),就必须修改全局 Schema,影响所有节点,扩展成本高、耦合性强

  • Agents SDK(Flexible Context)

    使用标准 Python dict 作为上下文载体(context_variables),天然支持动态扩展。配合 lambda 提示词注入,Agent 可以按需读取任意字段,无需修改核心架构。

python 复制代码
# agent.py:动态上下文注入
context_variables = {"user_name": "张三(白金会员)", "flight_no": "CA1234"}

triage_agent = Agent(
    name="TriageAgent",
    instructions=lambda ctx, _: f"当前用户:{ctx.context.get('user_name')}",
    model=model
)

优势:符合 Python "Duck Typing"哲学,开发灵活,迭代敏捷。


5.3 可扩展性:图结构耦合 vs 配置化解耦

  • LangGraph(Graph)

    新增一个业务分支(如"酒店预订"),需三步:

      1. 定义新 Node;
      1. 在图中 add_node
      1. 最关键的 :修改前置节点的条件边逻辑(add_conditional_edges)。
        节点越多,图越复杂,路由逻辑高度耦合
  • Agents SDK(Handoff)

    只需在"通讯录"中新增一项。Agent 的 handoffs 列表即声明了所有可能的协作对象,路由由 SDK 自动处理,无需修改提示词或条件判断。

python 复制代码
# agent.py:仅需扩展 handoffs 列表
hotel_agent = Agent(name="HotelAgent", ...)

triage_agent.handoffs = [refund_agent, change_agent, hotel_agent]  # 一行搞定

优势:新增业务零侵入,系统可组合性极强。

5.4 MCP 工具集成:适配器转换 vs 原生一等公民

  • LangGraph

    工具被视为外部插件。要集成 MCP Server,通常需借助 langchain-mcp-adapters 等中间层:

      1. 启动 MCP 客户端;
      1. 将 MCP 工具描述转换为 LangChain Tool 格式;
      1. 再绑定到 LLM。
        这不仅增加代码复杂度,还引入额外的序列化/反序列化开销,本质上是"翻译"而非"集成"。 (我们在MCP篇学了非常久,很明显能感知到这点)
  • Agents SDK
    MCP Server 被视为与 Python 函数同级的一等公民。SDK 底层直接封装了 MCP 协议(JSON-RPC over stdio),开发者只需声明连接参数,工具自动注册、自动调用。

python 复制代码
# agent.py:声明式配置 MCP Server
amap_server = MCPServerStdio(
    name="amap",
    params=MCPServerStdioParams(
        command="npx",
        args=["-y", "@amap/amap-maps-mcp-server"],
        env={"AMAP_MAPS_API_KEY": "your_key"}
    )
)

# Agent 直接引用,无需转换
map_agent = Agent(name="地图助手", mcp_servers=[amap_server], model=model)
python 复制代码
# main.py:启动时连接
await amap_server.connect()

​ MCP链接效果如图所示。

优势零胶水代码,工具即服务,无缝融入 Agent 工作流。这也是为何在实战中,Agent 能直接调用 maps_text_search 并返回结构化结果------背后是 SDK 对 MCP 的原生支持,而非层层封装的适配器。
⚠️ 注:此前在P4部分的代码为了表述清晰,并未加入MCP服务。实际的main.py部分是含有MCP工具链接的,具体可参考仓库内原代码。

5.5 流式逻辑:底层事件 vs 业务语义事件

  • LangGraph 的流式输出

    虽然支持异步流式响应(如 astream_events),但它暴露的是底层执行过程中的原始事件 :包括模型生成的 token 片段、节点进入/退出信号等。

    开发者需要手动:

    • 过滤掉空或无效的文本块;
    • 将零散的 token 拼接成可读内容;
    • 并且无法直接获知当前是哪个 Agent 在响应------因为 LangGraph 本身没有"Agent 切换"这一高层事件,只能通过节点名称间接推测。

    这导致流式交互的实现成本较高,逻辑分散在多个判断分支中。

  • Agents SDK 的流式输出

    它对底层细节做了封装,直接提供面向业务场景的结构化事件

    • raw_response_event:代表 LLM 正在生成回复,delta 字段已过滤无效内容,可直接用于打字机式输出;
    • agent_updated_stream_event:明确通知 Agent 发生切换,例如从"前台客服"转到"退票专员",这是 Swarm 协作的核心信号;
    • run_item_stream_event:清晰标识工具调用的开始与结果,便于展示"正在查询酒店..."等中间状态。
python 复制代码
# 直接消费封装好的业务事件
async for event in result.stream_events():

    # 1. 文本流:SDK 已封装好 ResponseTextDeltaEvent,无需过滤空 chunk
    if event.type == "raw_response_event":
         if isinstance(event.data, ResponseTextDeltaEvent):
             print(event.data.delta, end="")

    # 2. Agent 切换事件:这是 LangGraph 没有的!
    # 直接告诉你:现在系统从 Triage 切到了 Refund
    elif event.type == "agent_updated_stream_event":
         print(f"🔀 [系统]: {event.old_agent.name} → {event.new_agent.name}")

    # 3. 工具调用:清晰的 Item 事件
    elif event.type == "run_item_stream_event" and event.name == "tool_called":
         print(f"🔧 [工具]: {event.item.raw_item.name}")

这种设计让开发者能直接响应业务动作,而无需关心底层 token 或执行图的细节,大幅简化了实时交互的实现。


总结:场景适配,而非优劣之分

场景 推荐框架 理由
智能客服 / Copilot / 动态协作 Agents SDK 原生异步、事件驱动、弱 Schema、MCP 无缝集成,适合高响应、低延迟、频繁跳转的交互场景。
审批流 / 代码审计 / 严格状态控制 LangGraph 强 Schema、可回溯、人工介入友好,适合流程固定、需状态持久化与审计的场景。

二者的关系,正如 FastAPI 之于 Django

一个追求响应速度与开发敏捷 ,一个强调结构严谨与流程可控
没有绝对优劣,只有场景适配。

总结、

本篇我们吃透了Swarm模式入手,理解了Handoff的协作思想。又通过实战代码,验证了Agents SDK在原生流式事件、动态上下文和配置化路由的巨大优势。它不仅仅是Swarm的升级,更是Agent应用上基于LangGraph的革新。

预告:13篇 Streamlit 快速入门

至此,我们的Agent集群在后端已近乎完美。下篇M13,我们将引入Streamlit,利用Agents SDK优秀的流式特性,打造一个能够实时显示打字机效果和系统转接动态的现代化Web应用。

它将是我们打通Agent毕业项目的最后一站。

相关推荐
糖果店的幽灵11 分钟前
langgraph的四种state解析
java·前端·javascript·langgraph
ZDGJ60991 小时前
外盘与国内期货交易品种核心区
区块链
糖果店的幽灵13 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
维克兜率天19 小时前
【维克】量化交易的法律边界:程序化交易和“拔网线“的区别
区块链
Web3_Daisy20 小时前
什么是 Robinhood Chain?
大数据·人工智能·web3·区块链
黄焖鸡能干四碗1 天前
IT数据架构规划设计方案(PPT文件)
大数据·网络·数据库·人工智能·架构·区块链
糖果店的幽灵2 天前
【langgraph 从入门到精通graphApi 篇】Checkpoint 持久化与状态管理
人工智能·langgraph
糖果店的幽灵2 天前
【langgraph 从入门到精通graphApi 篇】生产部署与性能优化
人工智能·性能优化·langgraph
糖果店的幽灵2 天前
【langgraph 从入门到精通graphApi 篇】流式输出与 Streaming
人工智能·langgraph
lhxcc_fly2 天前
LangGraph 项目部署知识点总结
ai·langchain·项目部署·langgraph