MAF 入门(5):多 Agent 编排全解

MAF 入门(5):多 Agent 编排全解------Sequential / Concurrent / Handoff / GroupChat

写在前面:一个 Agent 不够用了(非常重要)

前面几篇,我们学会了让一个 Agent 对话、调工具、记历史、挂中间件。现在咱们把多个agent放一块编排,这篇博客主要以一个实际业务中的例子切入,让你快速学习agent编排。现在有一个电商的工单需要处理,会有不通角色的AI来分析这个工单。

「这个工单既要查物流,又要看政策,还要评估退款比例------让一个 Agent 全包,要么回答又长又散,要么顾此失彼。」

这不是模型「不够聪明」,而是分工的问题。人类客服中心也不会让一个人同时扮演分拣员、法务、财务------大家各管一摊,再按流程协作。

Microsoft Agent Framework(MAF)Microsoft.Agents.AI.Workflows 里提供了四种常见的多 Agent 编排模式:

模式 一句话 生活类比
Concurrent 多个 Agent 同时处理同一输入 三位专家同时看同一份病历
Sequential 多个 Agent 按顺序接力 写稿 → 改稿 → 终审
Handoff Agent 主动转交给更合适的同事 前台分拣 → 专科医生
GroupChat 多个 Agent 轮流发言协商 科室会诊圆桌

本文用同一笔电商客服工单 (订单 ORD-8842,签收第 8 天、礼盒角部压损、要求全额退款 ¥299)贯穿四种模式。你可以把它想象成:同一位委屈的客户,公司用四种不同的「内部流程」来应对------每种流程适合不同的业务场景。


一、场景设定:一笔「烫手」的工单

Demo 把背景故事集中在 SupportScenario.cs

csharp 复制代码
public const string OrderId = "ORD-8842";

public const string TicketSummary =
    """
    工单 #{0}:客户签收第 8 天反馈课程礼盒角部压损,要求全额退款 ¥299。
    会员银卡,物流显示已签收,商品为 P002《.NET AI 开发课程》实体礼盒。
    争议点:已超过 7 天无理由退货窗口,但客户主张运输途中挤压损坏。
    """;

争议点很典型:超期 vs 运输损坏 ,政策、物流、质检各有一套说法。这正是多 Agent 编排发挥价值的场景------不是让一个大模型「硬想」,而是让角色清晰的小专家分别贡献视角,再按流程合成结论。

二、Workflow 执行模型:四种模式共用的「发动机」

不管选哪种编排,跑起来的代码骨架是一样的。以 Concurrent 为例:

csharp 复制代码
// ① 用 AgentWorkflowBuilder 组装 Workflow
Workflow workflow = AgentWorkflowBuilder.BuildConcurrent(/* ... */);

// ② 准备输入
var input = new List<ChatMessage> { new(ChatRole.User, prompt) };

// ③ 启动流式执行
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(
    workflow, input, cancellationToken: cancellationToken);

// ④ 发送 TurnToken ------ 告诉引擎「可以开始本轮了」
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));

// ⑤ 订阅事件流
await foreach (WorkflowEvent evt in run.WatchStreamAsync(cancellationToken))
{
    // 根据事件类型处理输出
}

2.1 三个关键类型

类型 作用
Workflow 编排好的「流程图」,由 Builder 构建
InProcessExecution 进程内执行器,适合 Demo 和单机场景
StreamingRun 一次运行实例,可发消息、读事件流

2.2 常见事件

Demo 里主要处理三类:

  • AgentResponseUpdateEvent --- 某个 Agent 流式输出的文字片段(Sequential / Handoff / GroupChat 控制台逐字打印靠它)
  • WorkflowOutputEvent --- Workflow 最终输出(Concurrent 汇总结果靠它,避免多 Agent 并行时控制台文字交错)
  • WorkflowErrorEvent --- 执行出错

扩展知识 :生产环境还可以把 Workflow 部署到支持 MAF 的宿主(如 Azure、自定义 Worker),InProcessExecution 只是最轻量的本地跑法。事件驱动模型意味着你可以把输出接到 SignalR、SSE、日志系统,而不只是 Console.Write

2.3 创建 Agent 的统一方式

所有角色都通过 SupportAgentsFactory 从同一个 IChatClient 创建------和前面几篇一样,底层模型不变,变的只是 Instructions 和 Tools

csharp 复制代码
public static class SupportAgentsFactory
{
    private static AIAgent Create(
        IChatClient chatClient,
        string id,
        string name,
        string description,
        string instructions,
        IList<AITool>? tools = null) =>
        chatClient.AsAIAgent(new ChatClientAgentOptions
        {
            Id = id,
            Name = name,
            Description = description,
            ChatOptions = new ChatOptions
            {
                Instructions = instructions,
                Tools = tools,
            },
        });

    // ── Concurrent:三专家并行 ──

    public static AIAgent CreateLogisticsAnalyst(IChatClient client) =>
        Create(client, "logistics", "物流分析师", "分析签收、运输损坏与物流责任",
            "你是物流分析师。只分析物流与签收责任,简洁中文,3-5句。");

    public static AIAgent CreatePolicyAnalyst(IChatClient client) =>
        Create(client, "policy", "政策分析师", "分析退换货政策与合规边界",
            "你是退换货政策分析师。只分析政策合规与可执行方案,简洁中文,3-5句。");

    public static AIAgent CreateQualityAnalyst(IChatClient client) =>
        Create(client, "quality", "质检分析师", "分析商品损坏程度与责任归属",
            "你是商品质检分析师。只分析损坏性质与举证建议,简洁中文,3-5句。");

    // ── Sequential:结构化 → 草稿 → 润色 ──

    public static AIAgent CreateTicketStructurer(IChatClient client) =>
        Create(client, "structurer", "工单结构化专员", "把多方意见整理为结构化纪要",
            """
            你是工单结构化专员。将输入整理为:
            1. 问题摘要 2. 各方观点 3. 待决事项
            只输出结构化纪要,不要写给客户的话术。
            """);

    public static AIAgent CreateReplyDrafter(IChatClient client) =>
        Create(client, "drafter", "回复草稿专员", "根据结构化纪要撰写客服回复草稿",
            "你是客服文案专员。根据上游纪要,写一段发给客户的回复草稿,语气专业友善,150字内。");

    public static AIAgent CreateReplyReviewer(IChatClient client) =>
        Create(client, "reviewer", "回复质检专员", "润色草稿并输出最终可发送版本",
            "你是质检专员。润色上游草稿,输出最终可发送给客户的回复,保留关键承诺与时限。");

    // ── Handoff:分拣 → 专员(带工具)──

    public static AIAgent CreateTriageAgent(IChatClient client) =>
        Create(client, "triage", "分拣专员", "判断工单类型并转交订单/退货/退款专员",
            """
            你是客服分拣专员。判断用户诉求后,必须 handoff 给合适专员处理,不要自己编造订单数据。
            物流/订单状态 → 订单专员;退货流程 → 退货专员;退款审批 → 退款专员。
            """);

    public static AIAgent CreateOrderAgent(IChatClient client)
    {
        IList<AITool> tools = [AIFunctionFactory.Create(SupportTools.CheckOrderStatus)];
        return Create(client, "order", "订单专员", "查询订单物流状态并解释签收情况",
            "你是订单专员。先调用 CheckOrderStatus 查订单,再简洁回答用户。", tools: tools);
    }

    public static AIAgent CreateReturnAgent(IChatClient client) =>
        Create(client, "return", "退货专员", "处理退货流程与举证要求",
            "你是退货专员。说明退货条件、举证材料、寄回地址(可模拟),简洁中文。");

    public static AIAgent CreateRefundAgent(IChatClient client)
    {
        IList<AITool> tools = [AIFunctionFactory.Create(SupportTools.CheckOrderStatus)];
        return Create(client, "refund", "退款专员", "评估退款金额与审批路径",
            "你是退款专员。需要订单信息时调用 CheckOrderStatus,再给出退款建议。", tools: tools);
    }

    // ── GroupChat:圆桌协商 ──

    public static AIAgent CreateFacilitator(IChatClient client) =>
        Create(client, "facilitator", "客服主持", "主持圆桌并推动形成一致方案",
            "你是客服主管。主持讨论,总结各方观点,推动形成一致的退款/退货方案。");

    public static AIAgent CreatePolicyExpert(IChatClient client) =>
        Create(client, "policy-expert", "政策顾问", "从政策与合规角度发表意见",
            "你是政策顾问。从 7 天无理由与运输损坏条款出发发表意见,简洁有力。");

    public static AIAgent CreateRefundLead(IChatClient client) =>
        Create(client, "refund-lead", "退款主管", "从成本与客户体验权衡退款比例",
            "你是退款主管。提出可执行的退款比例与替代方案(换货/部分退款)。");
}

NameDescription 在 Handoff 模式里尤其重要------模型靠它们判断「该转交给谁」。


三、Concurrent:三专家并行,快但各说各话

3.1 什么时候用?

  • 子任务彼此独立,没有严格先后依赖
  • 希望缩短总耗时(三个专家同时看,而不是排队)
  • 最后需要有人汇总多方意见

物流、政策、质检三个角度互不阻塞------Perfect fit。

3.2 代码

csharp 复制代码
Workflow workflow = AgentWorkflowBuilder.BuildConcurrent(
    "support-concurrent",
    [
        SupportAgentsFactory.CreateLogisticsAnalyst(chatClient),
        SupportAgentsFactory.CreatePolicyAnalyst(chatClient),
        SupportAgentsFactory.CreateQualityAnalyst(chatClient),
    ],
    aggregator: agentOutputs =>
    {
        var merged = new List<ChatMessage>
        {
            new(ChatRole.User, "【并行分析汇总】以下是三位专家的结论:"),
        };

        string[] roles = ["物流", "政策", "质检"];
        for (int i = 0; i < agentOutputs.Count; i++)
        {
            ChatMessage? last = agentOutputs[i].LastOrDefault(m => m.Role == ChatRole.Assistant);
            if (last?.Text is { Length: > 0 } text)
            {
                string role = i < roles.Length ? roles[i] : $"专家{i + 1}";
                merged.Add(new ChatMessage(ChatRole.Assistant, $"[{role}] {text}"));
            }
        }

        return merged;
    });

三个 Agent 的 Instructions 都强调「只从你负责的角度 分析,3-5 句话」------这是在并行场景下的重要技巧:收窄职责,避免每个 Agent 都写一份完整小作文,汇总时反而冗长重复。

3.3 Aggregator 是灵魂

aggregator 回调收到每个 Agent 的完整消息列表,由你决定怎么合并。Demo 取了每位专家最后一条 Assistant 消息,贴上 [物流][政策][质检] 标签。

扩展知识

  • Aggregator 也可以再调一次 LLM 做「摘要仲裁」,Demo 用规则合并是为了让输出稳定、省 Token。
  • 并行 = 三次模型调用同时发生,latency 接近单次最慢的那路 ,但 Token 费用是三份
  • 在 AutoGen lineage 里,这类似多 Agent 并行回复后由 GroupChatManager 或自定义函数汇总;MAF 把它收敛成 BuildConcurrent 一个 API。

四、Sequential:流水线接力,适合「一步步精炼」

4.1 什么时候用?

  • 后一步依赖前一步的结构化输出
  • 流程固定:整理 → 起草 → 润色
  • 类似内容生产、代码 Review 链、ETL 式信息加工

Concurrent 解决「同时看」,Sequential 解决「接着干」。

4.2 本 Demo 的三棒接力

csharp 复制代码
Workflow workflow = AgentWorkflowBuilder.BuildSequential(
    "support-sequential",
    [
        SupportAgentsFactory.CreateTicketStructurer(chatClient),  // 结构化纪要
        SupportAgentsFactory.CreateReplyDrafter(chatClient),      // 回复草稿
        SupportAgentsFactory.CreateReplyReviewer(chatClient),     // 润色定稿
    ]);
顺序 角色 产出
1 工单结构化专员 问题摘要、各方观点、待决事项
2 回复草稿专员 发给客户的回复草稿(150 字内)
3 回复质检专员 最终可发送版本

Sequential 模式下,上一个 Agent 的输出会自动成为下一个 Agent 的上下文 ,你不需要手动传参------这是 BuildSequential 帮你做的「管道胶合」。

扩展知识

Sequential 的总耗时 ≈ 各步之和,Token 也是累加的。若某一步失败,整条链中断------生产上可在 Agent 外包装 Middleware 做重试或降级(第 4 篇学过)。

对比 Prompt Chaining (手写多次 RunAsync 并拼接),Workflow 的好处是编排可视化潜力统一事件模型、以及后续扩展 Handoff / 条件分支时不用推倒重来。


五、Handoff:前台分拣,专员接手

5.1 什么时候用?

  • 用户意图一开始不确定,需要路由到不同专家
  • 对话是多轮 的,处理过程中可能换负责人
  • 各专员能力不同(有的带工具,有的只讲政策)

这最接近真实客服:分拣台 → 订单组 / 退货组 / 退款组

5.2 构建 Handoff 图

csharp 复制代码
var triage = SupportAgentsFactory.CreateTriageAgent(chatClient);
var order = SupportAgentsFactory.CreateOrderAgent(chatClient);
var returnAgent = SupportAgentsFactory.CreateReturnAgent(chatClient);
var refund = SupportAgentsFactory.CreateRefundAgent(chatClient);

Workflow workflow = AgentWorkflowBuilder
    .CreateHandoffBuilderWith(triage)                              // 入口:分拣专员
    .WithHandoffs(triage, [order, returnAgent, refund])            // 分拣可转给三人
    .WithHandoffs(returnAgent, [refund])                           // 退货后可转退款
    .WithHandoffs([order, returnAgent, refund], triage,            // 专员可转回分拣
        "问题已处理完毕或需重新分拣")
    .WithName("support-handoff")
    .Build();

画成图更直观:

text 复制代码
                    ┌─────────┐
         ┌─────────│ 分拣专员 │─────────┐
         │         └────┬────┘         │
         │    handoff   │   handoff    │ handoff
         ▼              ▼              ▼
    ┌─────────┐   ┌─────────┐   ┌─────────┐
    │ 订单专员 │   │ 退货专员 │   │ 退款专员 │
    │ (+工具) │   │         │   │ (+工具) │
    └────┬────┘   └────┬────┘   └────┬────┘
         │             │             │
         └─────────────┴──────┬──────┘
                              │ 处理完 / 需重新分拣
                              ▼
                         ┌─────────┐
                         │ 分拣专员 │
                         └─────────┘

Handoff 不是你在代码里写 if (退款) goto RefundAgent,而是模型通过 Handoff 工具决定「下一位是谁」。因此 Demo 启动时会提示:

Handoff 需模型支持 Tool Calling / Handoff 工具

若模型不支持工具调用,分拣 Agent 可能「自己编造订单信息」------Instructions 里虽然写了「不要编造」,但没有 Handoff 能力就转交不出去。

5.3 专员带工具

订单专员和退款专员挂载了 CheckOrderStatus

csharp 复制代码
[Description("查询订单状态、签收时间与商品信息")]
public static string CheckOrderStatus(
    [Description("订单号,例如 ORD-8842")] string orderId)
{
    return orderId.Trim().ToUpperInvariant() switch
    {
        "ORD-8842" =>
            "ORD-8842 | 状态=已签收 | 签收=8天前 | 商品=P002 .NET AI课程礼盒 ¥299 | ...",
        _ => $"未找到订单 {orderId}(模拟数据)"
    };
}

Handoff + Tool Calling 是黄金搭档:路由 决定谁说话,工具让专员能查真实(模拟)数据,而不是瞎编。

用户消息用的是口语化版本,和内部工单摘要不同:

csharp 复制代码
public static string HandoffUserMessage =>
    $"你好,我的订单 {OrderId} 签收 8 天了,课程礼盒角被压坏了,我想全额退款,能帮我查一下并处理吗?";

5.4 运行

扩展知识

Handoff 图可以很复杂:多入口、条件边、循环回分拣。设计时要想清楚谁可以转给谁 ,避免模型在专员之间 ping-pong 踢皮球。

这和 OpenAI 的 handoff、AutoGen 的 Swarm 模式思路相近------MAF 用 WithHandoffs 显式声明图结构,可审计、可静态检查,比纯 Prompt 里写「必要时转交」靠谱得多。


六、GroupChat:圆桌会诊,要「商量出一致」

6.1 什么时候用?

  • 需要多方观点碰撞,而不是单人决策
  • 没有固定流水线顺序,但要控制轮次防止无限扯皮
  • 典型:评审会、方案定稿、争议仲裁

Concurrent 是「各交作业」,GroupChat 是「开会讨论」。

6.2 代码

csharp 复制代码
Workflow workflow = AgentWorkflowBuilder
    .CreateGroupChatBuilderWith(agents =>
        new RoundRobinGroupChatManager(agents) { MaximumIterationCount = 6 })
    .AddParticipants([
        SupportAgentsFactory.CreateFacilitator(chatClient),    // 客服主持
        SupportAgentsFactory.CreatePolicyExpert(chatClient),   // 政策顾问
        SupportAgentsFactory.CreateRefundLead(chatClient),     // 退款主管
    ])
    .WithName("support-groupchat")
    .Build();

RoundRobinGroupChatManager 按固定顺序轮流发言:主持 → 政策 → 退款 → 主持 → ...

MaximumIterationCount = 6 表示最多 6 轮发言------给讨论设上限,避免 Token 账单爆炸,也逼 Agent 尽快收敛。

三位角色的 Instructions 分别强调:

  • 主持:总结观点、推动一致方案
  • 政策顾问:7 天无理由 vs 运输损坏条款
  • 退款主管:退款比例、换货/部分退款等可执行方案

Prompt 要求「讨论并给出一致的处理方案」------这是在 GroupChat 里减少「各说各话、没有结论」的关键 Prompt 设计。

6.3 和 Concurrent 的区别

Concurrent GroupChat
发言方式 同时 轮流
是否看到彼此 否(各自只看原始输入) 是(能看到前面发言)
典型目标 收集并行意见 协商一致
耗时 ~最慢那路 轮次 × 单次

6.4 运行

`

扩展知识

MAF 的 GroupChat 可换不同的 GroupChatManager(如基于 LLM 的「下一位谁发言」选择器),RoundRobin 最简单、行为最可预测,适合 Demo 和需要稳定复现的场景。

若讨论质量不够,常见调优手段:换更强的模型当「主持」、减少参与人数、在 Prompt 里要求「最后一轮必须输出 JSON 方案」等。


七、四种模式怎么选?一张决策表

你的问题 推荐模式
多个独立分析维度,要快 Concurrent
固定加工步骤,后步吃前步 Sequential
用户意图不明,对话中要换人 Handoff
需要讨论、博弈、达成一致 GroupChat

还可以组合 ------本 Demo 的 all 模式就是:

text 复制代码
Concurrent(收集意见)→ Sequential(生成客户回复)→ Handoff(模拟用户对话路径)→ GroupChat(内部仲裁方案)

真实项目不必四次全跑;选一种或两种组合即可。



八、踩坑与建议

8.1 Handoff 不转交

  • 确认模型支持 Tool Calling
  • 检查 Agent 的 Name / Description 是否清晰
  • 分拣 Agent 的 Instructions 是否明确写了「必须 handoff,不要自己处理」

8.2 Concurrent 控制台乱码式交错

并行 Agent 会同时流式输出。Demo 刻意只打印 WorkflowOutputEvent 的最终汇总;若你要看每路详情,可分别订阅并带 Agent 标识打印。

9.3 GroupChat 扯不完

  • 降低 MaximumIterationCount
  • 主持 Agent 的 Instructions 里要求「第 N 轮必须表决」
  • 参与 Agent 不宜过多(3-5 个为宜)

8.4 Token 与成本

多 Agent ≠ 免费午餐。粗略估算:

  • Concurrent:调用次数 = Agent 数
  • Sequential:调用次数 = 步骤数
  • Handoff:取决于转交轮数,通常 2-4 次
  • GroupChat:≈ 轮次 × 参与人数

生产环境建议配合第 4 篇的 RequestLogging Middleware 按 Workflow 维度做用量统计。


九、小结

模式 MAF API 本 Demo 角色
Concurrent AgentWorkflowBuilder.BuildConcurrent 物流 / 政策 / 质检分析师
Sequential AgentWorkflowBuilder.BuildSequential 结构化 → 草稿 → 润色
Handoff CreateHandoffBuilderWith + WithHandoffs 分拣 → 订单 / 退货 / 退款专员
GroupChat CreateGroupChatBuilderWith + RoundRobinGroupChatManager 主持 + 政策顾问 + 退款主管

同一笔 ORD-8842 工单,四种编排给出了四种「组织方式」:

  • Concurrent 像同时拨通三个专家电话
  • Sequential 像经三道工序写出回复
  • Handoff 像客户打进 400 电话被转接
  • GroupChat 像下午三点的跨部门协调会

MAF 用统一的 Workflow + StreamingRun + 事件流把四种模式收口在同一套运行时里。下一篇可以在此基础上继续探索:条件分支、人工介入(Human-in-the-loop)、持久化 Workflow------那是从 Demo 走向生产编排的下一层台阶。


附录:依赖与相关阅读

NuGet:

xml 复制代码
<PackageReference Include="Microsoft.Agents.AI.Workflows" Version="1.7.0" />
相关推荐
treesforest2 小时前
AI安全系统如何识别异常访问?IP风险识别正在成为关键能力
网络·人工智能·tcp/ip·安全·web安全
harykali2 小时前
Hello-ROCm:Gemma4微调 #Datawhale #AMDev
人工智能·llm
用户5191495848452 小时前
Flowise预认证任意文件上传漏洞分析(CVE-2025-26319)
人工智能·aigc
shushangyun_2 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
闵孚龙2 小时前
《PyTorch 深度修炼》Dataset 和 DataLoader:数据如何喂给模型
人工智能·pytorch·python
双斜杠少年2 小时前
万字长文一文入门AI agent开发《AI agent开发相关概念》
人工智能
AI产品测评官2 小时前
Moka与北森用户如何接入世纪云猎,搭建完整AI招聘寻访链路
人工智能
qq_366566502 小时前
2026最新:5款AI视频口型同步工具实测横评,视频翻译后嘴型对不上的终极解决方案
人工智能·计算机视觉·新媒体运营
ofoxcoding2 小时前
在AI API聚合平台配置DeepSeek V3.2提示词缓存实战:快速接入与成本优化指南
人工智能·spring·缓存·ai