如何设计多 Agent 的协作与动态切换机制?

摘要:随着大语言模型(LLM)从单任务处理走向复杂跨领域业务求解,单一 Agent(单体智能体)由于 Prompt 膨胀、工具过多导致的召回稀释(Tool Dilution)、上下文污染(Context Pollution)以及规划深度不足,正迅速触碰到性能天花板。

多智能体系统(Multi-Agent System, MAS) 通过"分而治之"的设计哲学,将复杂的系统目标解耦至多个具有专业角色、独立工具集与独立上下文的专精 Agent。而在多 Agent 体系中,如何设计高效的协作拓扑确定性的动态切换/控制权移交(Handoff)机制,是决定系统稳定性、响应延迟与成本控制的核心命题。

本文将从单 Agent 的结构性瓶颈出发,深入拆解四大主流多 Agent 协作拓扑(主从层级式、点对点接力式、黑板共享式、动态图工作流);详细剖析动态切换协议设计(意图路由、状态迁移、上下文无损裁剪与死循环熔断);并基于 Python 异步生态提供一套开箱即用、具备工业级韧性的多 Agent 动态路由与协同框架实战源码。

一、 为什么单 Agent 走向终结?多 Agent 系统的必然性

在构建复杂的 LLM 应用(如全自动软件开发流水线、企业 ERP 智能中枢、跨业务客服系统)时,很多团队最初尝试使用"单一巨型 Agent":把 30 个外部工具、数千字的 System Prompt 以及全量业务规则塞入一个模型上下文中。

然而,单体 Agent 架构在面临复杂长流程时,必然遭遇以下四大结构性困境:

复制代码
┌────────────────────────────────────────────────────────────────────────┐
│                      单体 Agent 的四大结构性困境                       │
├──────────────────┬─────────────────────────────────────────────────────┤
│ 1. 规则与角色干扰│ 赋予模型的角色越杂,模型越容易混淆职责,遵循性急剧下降 │
├──────────────────┼─────────────────────────────────────────────────────┤
│ 2. 工具选择稀释  │ 当可用工具超过 10~20 个时,模型选择错误工具的概率呈指数上升│
├──────────────────┼─────────────────────────────────────────────────────┤
│ 3. 上下文过度污染│ 检索出来的无关历史与中间计算碎片挤占窗口,引发严重幻觉│
├──────────────────┼─────────────────────────────────────────────────────┤
│ 4. 成本与延迟失控│ 每次简单的工具调用都要携带全量巨型 Prompt,无法利用缓存│
└──────────────────┴─────────────────────────────────────────────────────┘

【单体 Agent (Monolithic Agent)】
用户请求 ──► [ 巨型 Prompt + 30 个工具 + 庞大上下文 ] ──► 输出混乱、延迟高、易幻觉

【多 Agent 协作 (Multi-Agent Modularization)】
用户请求 ──► [ 分流路由 / Router ]
                  │
        ┌─────────┼─────────┐
        ▼         ▼         ▼
   [销售专家]  [售后技术]  [财务开票]  (每个 Agent 仅持 3~5 个专属工具与精简上下文)

多 Agent 架构的核心优势

  1. 关注点分离(Separation of Concerns):每个 Agent 仅拥有极度专注的 Prompt 边界与最小化的工具集(Tool Set),极大降低了单次决策的推理复杂度。

  2. 异构模型混用(Heterogeneous LLM Routing):分流与规划可以使用高推理能力的大模型(如 DeepSeek-R1、GPT-4o),而具体的格式清洗或简单检索可以派发给极低成本的轻量级小模型(如 GPT-4o-mini、7B 本地模型),大幅优化 ROI。

  3. 上下文隔离与独立维护:各 Agent 维护独立的 Working Memory,交互时仅传递结构化的输出结果,避免无关历史碎片污染其他环节。

二、 多 Agent 协作的四大主流拓扑模式

在设计多 Agent 系统前,必须根据业务场景的确定性与灵活性需求,选择合理的协作拓扑架构。

复制代码
                     ┌─────────────────────────────────────────┐
                     │          多 Agent 协作拓扑形态          │
                     └────────────────────┬────────────────────┘
                                          │
       ┌──────────────────┬───────────────┴───────────────┬──────────────────┐
       ▼                  ▼                               ▼                  ▼
┌──────────────┐   ┌──────────────┐                ┌──────────────┐   ┌──────────────┐
│  主从层级式  │   │  点对点接力  │                │  黑板共享式  │   │  动态图工作流│
│ (Supervisor) │   │ (Peer Handoff│                │ (Blackboard) │   │ (Graph-based)│
└──────────────┘   └──────────────┘                └──────────────┘   └──────────────┘

2.1 主从层级式(Supervisor / Router Pattern)

这是工业界最常见、最可控的模式。系统中存在一个"主管(Supervisor / Master Agent)",负责接收用户输入、分解任务、调度具体的"工蜂(Worker Agents)",并汇总结算。

复制代码
                          ┌─────────────────────┐
                          │  Supervisor Agent   │
                          └──────────┬──────────┘
                                     │ 任务指派 / 汇总
            ┌────────────────────────┼────────────────────────┐
            ▼                        ▼                        ▼
  ┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
  │  Research Agent  │     │   Coder Agent    │     │  Reviewer Agent  │
  └──────────────────┘     └──────────────────┘     └──────────────────┘
  • 核心机制:Worker 之间彼此不可见、不直接通信。Worker 完成局部任务后,将结果上报给 Supervisor,由 Supervisor 决定下一步是派发给下一个 Worker 还是直接回复用户。

  • 优点:控制流极其清晰,便于进行全局权限校验、成本统计与审计,不易出现混乱死循环。

  • 缺点:Supervisor 容易成为单点瓶颈;每次中间交互都需要经过 Supervisor 转发,Token 消耗较大。

2.2 点对点接力式(Peer-to-Peer / Swarm Handoff Pattern)

以 OpenAI Swarm 架构为代表的轻量级接力协作模式。系统中没有常驻的中心调度器,每个 Agent 都有能力直接将"控制权与上下文"移交给另一个更专业的 Agent。

复制代码
[用户输入] ──► [Triage Agent (分诊)]
                     │
                     │ transfer_to_billing() (控制权移交)
                     ▼
              [Billing Agent (财务)]
                     │
                     │ transfer_to_technical() (控制权移交)
                     ▼
              [Technical Agent (技术支持)] ──► [输出最终结果]
  • 核心机制 :通过在 Function Calling 中暴露特定的转移函数(如 transfer_to_sales_agent())。当模型认为当前问题超出其专业范围时,自主调用转移函数返回新 Agent 的引用,宿主环境随即切换当前活跃的 Agent。

  • 优点:极度扁平、轻量、低延迟,去中心化设计让业务链路非常直接。

  • 缺点:缺乏全局监管,若未设置严格的 TTL(生存时间)或循环检测,容易在两个 Agent 之间发生"踢皮球(Ping-Pong Handover)"死循环。

2.3 黑板共享式(Blackboard / Shared State Store Pattern)

源自经典人工智能架构。系统中存在一个全局共享的状态存储中心(Blackboard / Event Pool),多个专精 Agent 共同监听黑板上的数据变动,并自主决定何时介入。

复制代码
┌────────────────────────────────────────────────────────────────────────┐
│                     全局共享黑板 (Shared Blackboard)                    │
│   - 任务目标状态   - 当前代码制品 (Artifacts)   - 待解决 Bug 清单      │
└─────────┬──────────────────────┬───────────────────────┬───────────────┘
          │ 读写数据             │ 读写数据              │ 读写数据
          ▼                      ▼                       ▼
┌──────────────────┐   ┌──────────────────┐    ┌──────────────────┐
│  Architect Agent │   │  Frontend Agent  │    │  Database Agent  │
│  (设计数据结构)  │   │  (消费结构写UI)  │    │  (消费结构写SQL) │
└──────────────────┘   └──────────────────┘    └──────────────────┘
  • 核心机制:基于事件驱动(Event-Driven)或状态变更轮询。例如架构师 Agent 将数据库 Schema 发布到黑板后,前端 Agent 和后端 Agent 同时感知到事件并并行启动工作,产出合并回黑板。

  • 优点:天然支持高并发与异构 Agent 协作,解耦程度最高。

  • 缺点:状态冲突解决(Conflict Resolution)和并发一致性控制复杂度高,需要引入分布式锁或乐观锁机制。

2.4 动态图工作流(Dynamic Graph Workflow / LangGraph Style)

将整个系统的协作建模为一张有向状态图(State Graph)。节点(Nodes)是 Agent 或 Python 执行函数,边(Edges)是带有条件判断的逻辑分支。

复制代码
             ┌──────────────┐
             │ [Start Node] │
             └──────┬───────┘
                    │
                    ▼
             ┌──────────────┐
             │ [Draft Plan] │
             └──────┬───────┘
                    │
                    ▼
         ┌─────────────────────┐
 ┌──────►│  [Code Generator]   │
 │       └──────────┬──────────┘
 │                  │
 │                  ▼
 │       ┌─────────────────────┐
 │       │    [Unit Tester]    │
 │       └──────────┬──────────┘
 │                  │
 │                  ▼
 │         / 单元测试是否通过? \
 └──[否]── ◄                    ► ──[是]──► [End Node]
           \                  /
  • 核心机制:支持循环(Cyclic)、分支判断与人工介入(Human-in-the-loop)。通过显式定义图的转移条件,精准控制执行路径。

  • 优点:兼具灵活性与强确定性,特别适合需要"生成-测试-反思-修正"闭环的代码或报告生成任务。

  • 缺点:需要预先对业务流程有清晰的图结构建模,冷启动设计成本稍高。

三、 动态切换(Handoff)核心机制深度设计

动态切换是多 Agent 系统的"中枢神经"。一个健壮的 Handoff 机制必须解决三大核心问题:何时切(Routing Trigger)怎么切(State Handover)如何防爆(Safety & Loop Prevention)

复制代码
┌────────────────────────────────────────────────────────────────────────┐
│                   动态切换机制 (Handoff) 三大核心环节                  │
├──────────────────┬─────────────────────────────────────────────────────┤
│ 1. 意图分流与触发│ - 语义路由 (Semantic Router)                         │
│                  │ - 专用分类器 (Triage LLM)                           │
│                  │ - 原生函数调用分发 (Function Calling Handoff)       │
├──────────────────┼─────────────────────────────────────────────────────┤
│ 2. 状态与上下文接力│ - 无损全量移交 (Full State Migration)                │
│                  │ - 摘要与结构化传递 (Summary-based Handoff)           │
│                  │ - 隔离域传递 (Scoped Context Handover)              │
├──────────────────┼─────────────────────────────────────────────────────┤
│ 3. 运行防御与控制│ - 跳数硬上限 (Hop Limit / Max Steps)                 │
│                  │ - 历史环路检测 (Visited Hash Detection)              │
│                  │ - 超时与降级机制 (Fallback to Supervisor/Human)     │
└──────────────────┴─────────────────────────────────────────────────────┘

3.1 切换触发机制:三种路由判定路径

1. 基于 Function Calling 的自主动态让渡(Autonomous Handoff)

在 Agent 的工具定义中,注入系统内置的切换工具:

复制代码
{
  "name": "transfer_to_billing_agent",
  "description": "当用户问题涉及充值、退款、发票、账单查询时,必须调用此工具将控制权移交发票财务专家",
  "parameters": {
    "type": "object",
    "properties": {
      "reason": {"type": "string", "description": "移交原因"},
      "user_intent_summary": {"type": "string", "description": "对用户诉求的核心提炼"}
    },
    "required": ["reason", "user_intent_summary"]
  }
}

当大模型判断当前上下文符合工具描述时,直接触发 tool_calls,宿主引擎解析该调用并执行 Agent 切换。

2. 前置轻量级语义路由器(Semantic Router / Embedding Classifier)

对于意图非常明确的场景,不需要每次都调用昂贵的大模型进行分类。

  • 原理:预先为每个 Agent 维护一组代表性的样本语句(Utterances),计算其向量并存入内存。

  • 执行:用户输入到达后,计算 Query 向量,通过余弦相似度(Cosine Similarity)在 5 毫秒内计算出最近匹配的 Agent。若相似度高于阈值(如 0.85),直接定向路由,跳过 Triage Agent 的 LLM 推理,大幅降低延迟与费用。

3.2 状态与上下文接力(Context Handover Protocol)

切换 Agent 时,最忌讳的是机械地把当前 Agent 产生的所有冗长中间思考、无效工具报错直接复制给下一个 Agent。这会导致下游 Agent 的上下文迅速耗尽并受到干扰。

生产级系统通常采用以下三种上下文迁移协议:

复制代码
【协议 A:全量追加传递 (适用于短链条/紧密协作)】
Context_B = Context_A + [System_Prompt_B]

【协议 B:摘要提取接力 (适用于长长上下文跨部门切换)】
Context_B = [System_Prompt_B] + [LLM_Generated_Summary(Context_A)] + [User_Original_Query]

【协议 C:结构化 Payload 传递 (工业级推荐)】
Context_B = [System_Prompt_B] + [Structured_Handoff_Payload(Slots, Intent, Auth_Info)]
工业级标准:结构化 Payload 移交规范

定义标准的强类型数据协议(Pydantic 规范),确保跨 Agent 传递的数据绝对受控:

复制代码
from pydantic import BaseModel, Field
from typing import Dict, Any, Optional

class HandoffPayload(BaseModel):
    source_agent: str = Field(description="移交方 Agent 名称")
    target_agent: str = Field(description="接收方 Agent 名称")
    user_id: str = Field(description="用户全局唯一ID")
    intent: str = Field(description="已识别的用户核心意图")
    extracted_slots: Dict[str, Any] = Field(default_factory=dict, description="已提取的实体槽位 (如 order_id, product_name)")
    handoff_reason: str = Field(description="切换原因说明")
    raw_query: str = Field(description="用户原始最新输入")

3.3 运行防御机制:防踢皮球与死循环熔断

在去中心化或双向切换系统中,极易出现 Ping-Pong Loop(乒乓死循环)

  • 销售 Agent 认为技术问题该找技术 Agent;

  • 技术 Agent 认为涉及价格又转回给销售 Agent。

防御策略组合拳:
  1. TTL 跃点计数器(Hop Counter) :全局请求携带 hop_count,每经历一次 Agent 切换则 hop_count += 1。一旦达到阈值(如 max_hops = 5),强行拦截并将控制权收缴至兜底 Supervisor 或人工客服。

  2. 访问轨迹哈希环检测(Cycle Detection) :维护一个有序访问链表 visited_path = ["Triage", "Sales", "Tech", "Sales"]。当算法检测到出现重复循环子序列(如 Sales -> Tech -> Sales)时,判定为逻辑锁死,触发熔断。

    [切换请求] ──► 检查 Hop Count >= Max Hops? ──[是]──► 触发熔断 ──► 接入兜底/人工
    │ [否]

    检查是否命中环路特征? ─────────[是]──► 强制终止 ──► 降级 Supervisor
    │ [否]

    正常执行 Agent 上下文迁移与激活

四、 共享记忆、状态总线与黑板架构设计

在复杂多 Agent 系统中,除了上下文(Context)传递,还需要考虑持久化的记忆与状态存储

复制代码
┌────────────────────────────────────────────────────────────────────────┐
│                   多 Agent 分层存储与通信矩阵                          │
├──────────────────┬───────────────────────┬─────────────────────────────┤
│ 存储/通信层级    │ 技术实现方案          │ 适用数据类型                │
├──────────────────┼───────────────────────┼─────────────────────────────┤
│ 1. 局部私有记忆  │ 内存 List[Dict]       │ 个人未完成的中间思考与临时变量│
├──────────────────┼───────────────────────┼─────────────────────────────┤
│ 2. 会话共享总线  │ Redis / In-Memory Dict│ 当前会话的槽位、全局锁、状态 │
├──────────────────┼───────────────────────┼─────────────────────────────┤
│ 3. 长期知识资产  │ 向量数据库 (Qdrant)   │ 企业知识库、历史对话案例    │
├──────────────────┼───────────────────────┼─────────────────────────────┤
│ 4. 异步事件通信  │ Kafka / Redis Streams │ 跨 Agent 异步解耦任务通知    │
└──────────────────┴───────────────────────┴─────────────────────────────┘

乐观并发控制(Optimistic Locking)在黑板模式中的应用

当多个 Agent 同时尝试修改全局共享状态(例如:前端 Agent 和后端 Agent 同时尝试修改项目计划表的同一字段)时,必须通过版本号机制防止数据覆盖:

复制代码
class BlackboardState(BaseModel):
    version: int = 1
    data: Dict[str, Any] = Field(default_factory=dict)

def update_blackboard(key: str, value: Any, expected_version: int) -> bool:
    # 模拟从 Redis 获取当前状态
    current_state = get_current_state()
    if current_state.version != expected_version:
        # 版本号不匹配,说明已被其他 Agent 先行修改,触发更新冲突重试
        return False
    
    current_state.data[key] = value
    current_state.version += 1
    save_state(current_state)
    return True

五、 端到端生产级实战:手搓动态多 Agent 路由与接力系统

下面提供一份完整、生产级、基于原生 Python 异步并发生态构建的多 Agent 协作与动态切换系统。

本实现包含:

  • 三个专精 AgentTriageAgent(分诊接待)、BillingAgent(财务账单)、TechSupportAgent(技术运维);

  • 标准 Handoff 协议:通过 Function Calling 实现结构化让渡;

  • 全局执行引擎:包含死循环熔断、跳数限制、上下文精准提取与状态流转。

5.1 环境准备

复制代码
pip install openai pydantic

5.2 核心代码实现

复制代码
import os
import json
import asyncio
import logging
from typing import List, Dict, Any, Optional, Callable, Tuple
from pydantic import BaseModel, Field

# 配置结构化日志输出
logging.basicConfig(level=logging.INFO, format="%(asctime)s - [%(levelname)s] - %(message)s")
logger = logging.getLogger("MultiAgentRuntime")


# ==================== 1. 核心协议与实体模型 ====================

class HandoffSignal(Exception):
    """用于在工具执行中抛出切换控制权的特殊信号"""
    def __init__(self, target_agent_name: str, payload: Dict[str, Any]):
        self.target_agent_name = target_agent_name
        self.payload = payload


class AgentTool(BaseModel):
    name: str
    description: str
    parameters: Dict[str, Any]
    func: Callable


class AgentContext:
    """全局会话运行时上下文"""
    def __init__(self, session_id: str, user_id: str):
        self.session_id = session_id
        self.user_id = user_id
        self.global_state: Dict[str, Any] = {}
        self.hop_count: int = 0
        self.visited_history: List[str] = []


# ==================== 2. Agent 基类定义 ====================

class BaseAgent:
    def __init__(self, name: str, system_prompt: str, model: str = "gpt-4o-mini"):
        self.name = name
        self.system_prompt = system_prompt
        self.model = model
        self.tools: Dict[str, AgentTool] = {}

    def register_tool(self, name: str, description: str, parameters: Dict[str, Any], func: Callable):
        self.tools[name] = AgentTool(name=name, description=description, parameters=parameters, func=func)

    def get_tools_schema(self) -> List[Dict[str, Any]]:
        return [
            {
                "type": "function",
                "function": {
                    "name": tool.name,
                    "description": tool.description,
                    "parameters": tool.parameters
                }
            }
            for tool in self.tools.values()
        ]


# ==================== 3. 具体业务工具与 Handoff 函数定义 ====================

# --- 财务工具 ---
async def tool_query_invoice(order_id: str) -> str:
    logger.info(f"[财务系统] 正在查询订单 {order_id} 的发票状态...")
    await asyncio.sleep(0.3)
    return json.dumps({"order_id": order_id, "invoice_status": "ISSUED", "amount": 299.00, "url": "https://invoice.example.com/pdf/123"})

# --- 技术运维工具 ---
async def tool_restart_server(server_id: str) -> str:
    logger.info(f"[运维系统] 正在向服务器 {server_id} 发送重启指令...")
    await asyncio.sleep(0.5)
    return json.dumps({"server_id": server_id, "status": "RESTARTED", "uptime": "0 min"})

# --- 动态切换工具 (Handoff Functions) ---
async def transfer_to_billing(reason: str, order_id: Optional[str] = None) -> str:
    """切换至财务结算专家"""
    raise HandoffSignal("BillingAgent", {"reason": reason, "order_id": order_id})

async def transfer_to_tech_support(reason: str, server_id: Optional[str] = None) -> str:
    """切换至技术运维专家"""
    raise HandoffSignal("TechSupportAgent", {"reason": reason, "server_id": server_id})


# ==================== 4. 初始化多 Agent 角色 ====================

def create_agent_system() -> Dict[str, BaseAgent]:
    agents = {}

    # 1. 前置分诊 Agent (Triage Agent)
    triage = BaseAgent(
        name="TriageAgent",
        system_prompt="你是一位负责客户接待的前台服务专家。仔细倾听用户诉求,并将其准确转交给对应的专业技术或财务支持部门。不要自行回答深度专业问题。"
    )
    triage.register_tool(
        name="transfer_to_billing",
        description="当用户问题涉及发票、退款、账单、支付时调用此工具移交财务专家",
        parameters={
            "type": "object",
            "properties": {
                "reason": {"type": "string", "description": "转交财务的原因"},
                "order_id": {"type": "string", "description": "若用户提及了订单号则提取传入,否则传空"}
            },
            "required": ["reason"]
        },
        func=transfer_to_billing
    )
    triage.register_tool(
        name="transfer_to_tech_support",
        description="当用户问题涉及服务器宕机、Bug 报错、系统部署、重启等技术运维故障时调用此工具移交技术专家",
        parameters={
            "type": "object",
            "properties": {
                "reason": {"type": "string", "description": "转交技术支持的原因"},
                "server_id": {"type": "string", "description": "若用户提及了服务器ID则提取传入,否则传空"}
            },
            "required": ["reason"]
        },
        func=transfer_to_tech_support
    )
    agents[triage.name] = triage

    # 2. 财务专家 Agent (Billing Agent)
    billing = BaseAgent(
        name="BillingAgent",
        system_prompt="你是一位资深的财务结算专家,专精于处理订单发票、退款结算与账单查询。回答风格严谨、专业。"
    )
    billing.register_tool(
        name="query_invoice",
        description="根据订单号查询电子发票详情及下载链接",
        parameters={
            "type": "object",
            "properties": {
                "order_id": {"type": "string", "description": "6位以上订单编号"}
            },
            "required": ["order_id"]
        },
        func=tool_query_invoice
    )
    # 财务也可在必要时转给技术
    billing.register_tool(
        name="transfer_to_tech_support",
        description="如果用户在财务沟通过程中提出了系统崩溃、报错等技术问题,可调用此工具转交技术支持",
        parameters={"type": "object", "properties": {"reason": {"type": "string"}}, "required": ["reason"]},
        func=transfer_to_tech_support
    )
    agents[billing.name] = billing

    # 3. 技术支持 Agent (Tech Support Agent)
    tech = BaseAgent(
        name="TechSupportAgent",
        system_prompt="你是一位高级云运维工程师,负责诊断服务器运行状态、系统排错与运维指令下发。语言风格简练有力。"
    )
    tech.register_tool(
        name="restart_server",
        description="安全重启指定的云服务器实例",
        parameters={
            "type": "object",
            "properties": {
                "server_id": {"type": "string", "description": "服务器唯一实例标识符,例如 srv-bj-001"}
            },
            "required": ["server_id"]
        },
        func=tool_restart_server
    )
    agents[tech.name] = tech

    return agents


# ==================== 5. 工业级多 Agent 调度编排引擎 ====================

class MultiAgentOrchestrator:
    def __init__(self, agents: Dict[str, BaseAgent], max_hops: int = 5):
        from openai import AsyncOpenAI
        self.agents = agents
        self.max_hops = max_hops
        self.client = AsyncOpenAI(
            api_key=os.getenv("OPENAI_API_KEY", "your-api-key"),
            base_url=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
        )

    async def execute_turn(self, user_query: str, session_context: AgentContext) -> str:
        """执行单轮用户交互,支持跨 Agent 动态接力"""
        current_agent_name = "TriageAgent"  # 默认由分诊前台接待
        messages: List[Dict[str, Any]] = [
            {"role": "user", "content": user_query}
        ]

        logger.info(f"======== 开启会话执行: Session[{session_context.session_id}] ========")
        logger.info(f"初始接待 Agent: [{current_agent_name}] | 用户输入: {user_query}")

        while True:
            # 1. 熔断与死循环检测
            session_context.hop_count += 1
            session_context.visited_history.append(current_agent_name)

            if session_context.hop_count > self.max_hops:
                logger.error(f"【熔断告警】Agent 切换跳数已达 {self.max_hops} 次,触发防爆机制!")
                return "抱歉,当前业务协同流程较为复杂,已为您自动转接人工高级客服团队为您处理。"

            # 环路检测 (简单乒乓环路判定)
            if len(session_context.visited_history) >= 4:
                last_four = session_context.visited_history[-4:]
                if last_four[0] == last_four[2] and last_four[1] == last_four[3]:
                    logger.error(f"【环路告警】检测到 Agent 陷入乒乓震荡: {last_four},强行终止!")
                    return "系统检测到业务诉求存在循环转接,已终止自动化流程并记录日志。"

            current_agent = self.agents[current_agent_name]
            logger.info(f"--- [Hop {session_context.hop_count}] Agent 运行中: [{current_agent_name}] ---")

            # 2. 构造符合当前 Agent 视角的独立上下文
            turn_messages = [{"role": "system", "content": current_agent.system_prompt}] + messages
            tools_schema = current_agent.get_tools_schema()

            # 3. 发起大模型推理
            try:
                response = await self.client.chat.completions.create(
                    model=current_agent.model,
                    messages=turn_messages,
                    tools=tools_schema if tools_schema else None,
                    tool_choice="auto" if tools_schema else None,
                    temperature=0.0
                )
            except Exception as e:
                logger.error(f"调用模型发生网络或鉴权异常: {e}")
                return f"系统服务繁忙,底层调用失败: {str(e)}"

            msg_obj = response.choices[0].message
            tool_calls = msg_obj.tool_calls

            # 4. 如果模型未发起工具调用,说明当前 Agent 已经可以给出最终结论
            if not tool_calls:
                logger.info(f"Agent [{current_agent_name}] 任务完成,准备输出最终答案。")
                return msg_obj.content or "(无文本输出)"

            # 5. 处理工具调用
            # 记录模型的思考与工具调用意图
            messages.append(msg_obj.model_dump(exclude_none=True))

            for tool_call in tool_calls:
                func_name = tool_call.function.name
                func_args_raw = tool_call.function.arguments
                logger.info(f"触发动作: [{current_agent_name}] -> Tool:[{func_name}] Args:{func_args_raw}")

                target_tool = current_agent.tools.get(func_name)
                if not target_tool:
                    messages.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "content": json.dumps({"error": f"Tool {func_name} not found"})
                    })
                    continue

                try:
                    args_dict = json.loads(func_args_raw)
                    # 执行具体函数
                    result = await target_tool.func(**args_dict)
                    
                    # 正常工具执行完成,将结果装入上下文
                    messages.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "name": func_name,
                        "content": result
                    })

                except HandoffSignal as handoff:
                    # 捕获到动态切换信号!
                    logger.warning(
                        f"【控制权移交】[{current_agent_name}] ➔ [{handoff.target_agent_name}] "
                        f"原因: {handoff.payload.get('reason')} | 携带载荷: {handoff.payload}"
                    )

                    # 核心技术点:无损上下文接力与精简
                    # 模拟向新 Agent 投递转移说明,并切换活跃指针
                    current_agent_name = handoff.target_agent_name
                    
                    # 注入系统级切换通知,告知新 Agent 上下文由谁转交而来
                    messages.append({
                        "role": "system",
                        "content": f"[系统控制权变更] 本次对话已从前序部门移交给 {current_agent_name}。转交附言: {json.dumps(handoff.payload, ensure_ascii=False)}。请直接根据前序信息为用户办理,避免重复询问用户已知信息。"
                    })
                    # 跳出当前工具轮次,直接让新 Agent 接入下一个循环
                    break


# ==================== 6. 场景模拟与端到端运行验证 ====================

async def main():
    agent_pool = create_agent_system()
    orchestrator = MultiAgentOrchestrator(agent_pool, max_hops=4)

    # 模拟用户 Session 状态
    context_session_1 = AgentContext(session_id="SESS_8888", user_id="USER_001")

    # 场景 A:财务发票诉求,测试 Triage -> Billing 动态接力
    query_1 = "你好,请帮我查一下订单号 ORD-202607-99 的电子发票开具情况。"
    print(f"\n==================== 测试用例 1 ====================")
    answer_1 = await orchestrator.execute_turn(query_1, context_session_1)
    print(f"\n【最终用户端收到的回复】:\n{answer_1}\n")

    # 场景 B:技术运维诉求,测试 Triage -> TechSupport 动态接力
    context_session_2 = AgentContext(session_id="SESS_9999", user_id="USER_002")
    query_2 = "大事不好了!我们的生产服务器 srv-bj-001 发生告警,请帮我执行紧急重启!"
    print(f"\n==================== 测试用例 2 ====================")
    answer_2 = await orchestrator.execute_turn(query_2, context_session_2)
    print(f"\n【最终用户端收到的回复】:\n{answer_2}\n")

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

六、 生产级可观测性与分布式追踪(Tracing & OpenTelemetry)

在单 Agent 系统中,排查错误只需查看单次请求的日志;但在多 Agent 系统中,一次用户提问可能触发 3 次 Agent 切换和 5 次工具调用。如果缺乏全链路追踪,线上排错将如同大海捞针。

复制代码
┌────────────────────────────────────────────────────────────────────────┐
│                   多 Agent 分布式追踪 Span 拓扑模型                    │
└──────────────────────────────────┬─────────────────────────────────────┘
                                   │ Trace: session_trace_001
 ┌─────────────────────────────────┴─────────────────────────────────┐
 │ Span 1: TriageAgent.execute (Duration: 350ms)                     │
 │   ├─ Span 1.1: LLM.Generate (Prompt Tokens: 420, Output: 35)      │
 │   └─ Span 1.2: Tool.transfer_to_billing (Handoff Triggered)       │
 └─────────────────────────────────┬─────────────────────────────────┘
                                   │ Context Propagation
 ┌─────────────────────────────────┴─────────────────────────────────┐
 │ Span 2: BillingAgent.execute (Duration: 820ms)                    │
 │   ├─ Span 2.1: LLM.Generate (Prompt Tokens: 610, Output: 40)      │
 │   ├─ Span 2.2: Tool.query_invoice (HTTP GET 200 OK)               │
 │   └─ Span 2.3: LLM.FinalSynthesis (Prompt Tokens: 750, Output: 90)│
 └───────────────────────────────────────────────────────────────────┘

生产级追踪指标体系

构建多 Agent 系统时,必须将以下指标采集并接入 Prometheus/Grafana 或 Langfuse/Arize Phoenix:

  1. Handoff Efficiency(移交有效率)有效移交率 = 成功在目标 Agent 解决的次数 / 触发 Handoff 的总次数 若该比率过低,说明 Triage Agent 的路由 Prompt 需要重新调优。

  2. Hop Count Distribution(跳数分布):监控 95% 的请求是否能在 2 跳内解决。若 P99 跳数显著升高,说明出现了冗余流转。

  3. Per-Agent Token Consumption(Agent 维度成本分摊):精准统计每个专精 Agent 消耗的 Token 比例,定位系统中哪个 Agent 是"成本刺客"。

七、 总结与前沿演进

多 Agent 系统的协作与动态切换机制,是实现复杂场景企业级大模型落地的核心技术。

复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│                      多 Agent 协作系统设计演进四步法                    │
├─────────────────────────────────────────────────────────────────────────┤
│ 1. 职责边界清晰化:每个 Agent 遵循单一职责原则,工具集控制在 3~5 个内 │
│ 2. 协议强类型化  :使用 Pydantic 规范 Handoff Payload,消灭无序上下文   │
│ 3. 运行防御前置化:设置跳数硬上限 (TTL)、环路哈希熔断与超时回滚        │
│ 4. 链路可观测化  :基于 OpenTelemetry 实现跨 Agent 级联分布式追踪      │
└─────────────────────────────────────────────────────────────────────────┘

随着多模态大模型与推理模型(Reasoning Models)的持续演进,多 Agent 正在从"静态手工编排 "向"基于环境反馈的自主动态拓扑生成"迈进。

理解并掌握多 Agent 的拓扑设计、状态接力协议与工程防御体系,将帮助架构师和开发者在面对高复杂度业务系统时,构建出兼具高智能、高鲁棒性与低成本的企业级 AI 应用基础设施。

相关推荐
jkyy201420 分钟前
从娱乐座舱到健康服务空间:重构智能出行的主动守护范式
大数据·人工智能·健康医疗
ddshub_cc21 分钟前
GPT Image 2 Prompt 案例集:可直接套用的出图写法
gpt·ai·prompt·文生图·image2·ai生图·gpt-image-2
隔窗听雨眠25 分钟前
大模型实时通信协议深度对比:SSE、WebSocket与gRPC的选型指南与实战解析
网络·websocket·网络协议
啊阿狸不会拉杆29 分钟前
《计算机网络-自顶向下方法》1.3 网络核心 读书笔记
网络·人工智能·计算机网络·ai·php
zbtlink33 分钟前
上行带宽瓶颈的诊断、SQM 调度与双链路上行实战
网络·智能路由器
Dawson Zhu34 分钟前
Agent 工具体系:从 MCP 协议到层次化工具发现
人工智能·语言模型·架构·aigc·agi
星核0penstarry36 分钟前
三款Flash模型横向对比:GLM-5.3、DeepSeek-V4、Qwen3.8怎么选?
人工智能·qwen·flash·glm-5.3·deepseek-·横向测评
DeepAgent39 分钟前
AI Agent 工程实践(39):第一次实现——先做一个最小 Agent
大数据·人工智能·agent