大家好,我是Edison。

上一篇我们学习了Semantic Kernel中的群聊编排模式,它非常适合集思广益、协作解决问题等类型任务场景。今天,我们学习新的模式:移交编排。

移交编排模式简介

在移交(也可以叫做交接)编排模式中,允许各个Agent根据上下文或用户请求相互转移控制权,每个Agent都可以通过适当的专业知识将对话“移交”给另一个Agent,确保每个Agent处理任务的某个指定部分。这种模式非常适合于客户支持(客服)、专家系统或需要动态委派类型的任务场景。

下图展示了一个客户支持的用例场景,当用户提交售后请求,先由某个前台代理(这里是General Support)进行请求分析,并将具体请求转移给某个后台专家(如Technical Expert)或 计费人员(如Billing)。

实现移交编排模式

这里我们来实现一个客户支持的DEMO,假设我们是一个电商的后台客服中心,我们找了一群AI Agent来帮我们进行一些订单查询、退款、退货等通用类请求的客户服务支持。

我们定义4个Agent:

(1)分流客服Agent:负责初步分流客户问题;

(2)订单状态查询Agent:负责处理客户的订单状态查询问题;

(3)订单退货处理Agent:负责处理客户申请的退货请求;

(4)订单退款处理Agent:负责处理客户申请的退款请求;

为了简单地实现这个功能,我们还是创建一个.NET控制台项目,然后安装以下包:

Microsoft.SemanticKernel.Agents.Core
Microsoft.SemanticKernel.Agents.OpenAI (Preview版本)
Microsoft.SemanticKernel.Agents.Orchestration (Preview版本)
Microsoft.SemanticKernel.Agents.Runtime.InProcess (Preview版本)

需要注意的是,由于Semantic Kernel的较多功能目前还处于实验预览阶段,所以建议在该项目的csproj文件中加入以下配置,统一取消警告:

<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>

创建一个appsettings.json配置文件,填入以下关于LLM API的配置,其中API_KEY请输入你自己的:

{
"LLM": {
"BASE_URL": "https://api.siliconflow.cn",
"API_KEY": "******************************",
"MODEL_ID": "Qwen/Qwen2.5-32B-Instruct"
}
}

这里我们使用SiliconCloud提供的 Qwen2.5-32B-Instruct 模型,你可以通过这个URL注册账号:https://cloud.siliconflow.cn/i/DomqCefW 获取大量免费的Token来进行本次实验。

有了LLM API,我们可以创建一个Kernel供后续使用,这也是老面孔了:

Console.WriteLine("Now loading the configuration...");
var config = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json", optional: false, reloadOnChange: true)
.Build();
Console.WriteLine("Now loading the chat client...");
var chattingApiConfiguration = new OpenAiConfiguration(
config.GetSection("LLM:MODEL_ID").Value,
config.GetSection("LLM:BASE_URL").Value,
config.GetSection("LLM:API_KEY").Value);
var openAiChattingClient = new HttpClient(new OpenAiHttpHandler(chattingApiConfiguration.EndPoint));
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(chattingApiConfiguration.ModelId, chattingApiConfiguration.ApiKey, httpClient: openAiChattingClient)
.Build();

接下来,我们就一步一步地来看看核心的代码。

定义4个Agent

这里我们来定义4个Agent:

(1)分流客服Agent:负责初步分流客户问题;

var triageAgent = new ChatCompletionAgent()
{
Name = "TriageAgent",
Description = "处理客户请求",
Instructions = "一个负责分流客户问题的客服智能体",
Kernel = kernel.Clone()
};

(2)订单状态查询Agent:负责处理客户的订单状态查询问题;

var statusAgent = new ChatCompletionAgent()
{
Name = "OrderStatusAgent",
Description = "一个负责查询订单状态的客服智能体",
Instructions = "处理订单状态请求",
Kernel = kernel.Clone()
};
statusAgent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(new OrderStatusPlugin()));

(3)订单退货处理Agent:负责处理客户申请的退货请求;

var returnAgent = new ChatCompletionAgent()
{
Name = "OrderReturnAgent",
Description = "一个负责处理订单退货的客服智能体",
Instructions = "处理订单退货并记录退货原因(用户需确认原因:不想要了 或 7天无理由退换 或 没有时间消费)",
Kernel = kernel.Clone()
};
returnAgent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(new OrderReturnPlugin()));

(4)订单退款处理Agent:负责处理客户申请的退款请求;

var refundAgent = new ChatCompletionAgent()
{
Name = "OrderRefundAgent",
Description = "一个负责处理订单退款的客服智能体",
Instructions = "处理订单退款请求并记录退款原因(用户需确认原因:不想要了 或 7天无理由退换 或 没有时间消费)",
Kernel = kernel.Clone()
};
refundAgent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(new OrderRefundPlugin()));

需要注意的是:这里多个Agent都有使用Function Calling调用外部方法实现一些功能,所以在Kernel的设置处通过Clone方法实现隔离。

下面是这几个订单帮助的Plugin的定义:

public sealed class OrderRefundPlugin
{
[KernelFunction]
public string ProcessReturn(string orderId, string reason) => $"订单 {orderId} 的退款申请已通过!退款理由:{reason}";
}
public sealed class OrderReturnPlugin
{
[KernelFunction]
public string ProcessReturn(string orderId, string reason) => $"订单 {orderId} 的退货申请已通过!退货理由:{reason} ";
}
public sealed class OrderStatusPlugin
{
[KernelFunction]
public string CheckOrderStatus(string orderId) => $"订单 {orderId} 已发货 并将于 2-3日内送达!";
}

选择编排模式

这里我们选择的是群聊编排模式:HandoffOrchestration,除了将需要编排的4个Agent作为参数传递给它之外,我们还需要定义一个移交流程,让Agent知道他们应该如何实现交接。

var handoffs = OrchestrationHandoffs
.StartWith(triageAgent)
.Add(source: triageAgent, targets: [statusAgent, returnAgent, refundAgent]) // 分流客服可交接给状态、退货、退款智能体
.Add(source: statusAgent, target: triageAgent, "如非订单状态相关问题则交回分流客服")
.Add(source: returnAgent, target: triageAgent, "如非退货相关问题则交回分流客服")
.Add(source: refundAgent, target: triageAgent, "如非退款相关问题则交回分流客服");

同时,为了实现聊天记录的存储和监控,自定义一个Monitor类:

public sealed class OrchestrationMonitor
{
public ChatHistory History { get; } = new ChatHistory();
public ValueTask ResponseCallback(ChatMessageContent response)
{
History.Add(response);
return ValueTask.CompletedTask;
}
}

最后,初始化移交编排:

// -- Create the HandoffOrchestration
var orchestration = new HandoffOrchestration(
handoffs,
members: [triageAgent, statusAgent, returnAgent, refundAgent])
{
Name = "CustomerSupportOrchestration",
Description = "处理客户请求并根据问题类型交接给对应的智能体",
InteractiveCallback = () =>
{
var lastMessage = monitor.History.LastOrDefault();
Console.WriteLine($"# Agent: \n{lastMessage?.Content}\n");
Console.WriteLine($"# User:");
var userInput = Console.ReadLine();
Console.WriteLine();
var message = new ChatMessageContent(AuthorRole.User, userInput);
monitor.History.Add(message);
return ValueTask.FromResult(message);
},
ResponseCallback = monitor.ResponseCallback
};

启动运行时

在Semantic Kernel中,需要运行时(Runtime)才能管理Agent的执行,因此这里我们需要在正式开始前使用InProcessRuntime并启动起来。

// Start the Runtime
var runtime = new InProcessRuntime();
await runtime.StartAsync();

调用编排 并 收集结果

准备工作差不多了,现在我们可以开始调用编排了。这也是老面孔代码了,不过多解释。

唯一需要注意的是:这里设置TimeSpan.FromSeconds(100*3)是为了给足对话时间。

// Start the Chat
Console.WriteLine($"Welcome to use CustomerSupport!\n");
var task = "你好,我需要订单上的帮助";
Console.WriteLine($"# User: \n{task}\n");
try
{
// Invoke the Orchestration
var result = await orchestration.InvokeAsync(task, runtime);
// Collect Results from multi Agents
var output = await result.GetValueAsync(TimeSpan.FromSeconds(100 * 3));
// Print the Results
Console.WriteLine($"# 处理结果总结: \n{output}\n");
}
catch (HttpOperationException ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
finally
{
await runtime.RunUntilIdleAsync();
Console.WriteLine($"\n----------See you next time!----------");
Console.ReadKey();
}

上面的代码示例中我们给出的第一句话是:“你好,我需要订单上的帮助”来进入客服场景。

效果展示

假设我是客户,我有3个订单,想要查询一个订单的状态,以及对另外两个订单进行退款和退货,对话过程如下图所示。

请求1:查询订单状态

请求2&3:申请退款 和 退货

示例源码

GitHub: https://github.com/EdisonTalk/MultiAgentOrchestration

小结

本文介绍了移交编排模式的基本概念,然后通过一个案例介绍了如何实现一个移交编排的经典场景:客户支持,相信通过这个案例你能够有个感性的认识。

下一篇,我们将学习磁性编排模式。

参考资料

Microsoft Learn: https://learn.microsoft.com/zh-cn/semantic-kernel/frameworks/agent/agent-orchestration

推荐学习

圣杰:《.NET+AI | Semantic Kernel入门到精通

作者:爱迪生

出处:https://edisontalk.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

多Agent协作入门:移交编排模式的更多相关文章

  1. 6. oracle学习入门系列之六 模式

    oracle学习入门系列之六 模式 上篇咱们学习记录了ORACLE数据库中的数据库结构.内存结构和进程等.篇幅 蛤蟆感觉偏多了.这次要休整下,每次笔记不宜太多,不然与书籍有何差别. 我们要保证的是每次 ...

  2. IView入门练习~CDN模式全局加载JS

    关于 iView iView 是一套基于 Vue.js 的开源 UI 组件库,主要服务于 PC 界面的中后台产品. 特性 高质量.功能丰富 友好的 API ,自由灵活地使用空间 细致.漂亮的 UI 事 ...

  3. RabbitMQ入门-消息订阅模式

    消息派发 上篇<RabbitMQ入门-消息派发那些事儿>发布之后,收了不少反馈,其中问的最多的还是有关消息确认以及超时等场景的处理. 楼主,有遇到消费者后台进程不在,但consumer连接 ...

  4. 前端之Android入门(3):MVC模式(上)

    很多Android的入门书籍,在前面介绍完布局后就会逐个介绍组件,然后开始编写组件使用的例子.每每到此时小伙伴们都可能会有些疑问:是否应该先啃完一本<Java编程思想>学点 Java 知识 ...

  5. 微信企业号开发入门(回调模式)java

    最近在开发微信企业号,刚接触时云里雾里的,在摸索过程中终于清晰了一点. 刚开始我以为订阅号.服务号.企业号的接口差不多,就一直用订阅号的教程来入门,后来才发现差的挺多的. 首先,微信企业号不像订阅号和 ...

  6. Mybatis入门案例中设计模式的简单分析

    Talk is cheap, show me the code! public class TestMybatis { public static void main(String[] args) t ...

  7. 使用 Skywalking Agent,这里使用sidecar 模式挂载 agent

    文章转载自:https://bbs.huaweicloud.com/blogs/315037 方法汇总 Java 中使用 agent ,提供了以下三种方式供你选择 使用官方提供的基础镜像 将 agen ...

  8. 设计模式入门之桥接模式Bridge

    Abstraction:抽象部分的父类,定义须要实现的接口.维护对实现部分的引用,从而把实现桥接到Implementor中去 Implementor:实现部分的接口 RefinedAbstractio ...

  9. CMDB服务器管理系统【s5day88】:采集资产之Agent、SSH和Salt模式讲解

    在对获取资产信息时,简述有四种方案. 1.Agent  (基于shell命令实现) 原理图 Agent方式,可以将服务器上面的Agent程序作定时任务,定时将资产信息提交到指定API录入数据库 优点: ...

  10. RabbitMQ入门-竞争消费者模式

    上一篇讲了个 哈喽World,现在来看看如果存在多个消费者的情况. 生产者: package com.example.demo; import com.rabbitmq.client.Channel; ...

随机推荐

  1. Merge Two Binary Trees——LeetCode进阶路

    原题链接https://leetcode.com/problems/merge-two-binary-trees/ 题目描述 Given two binary trees and imagine th ...

  2. 【多线程】Java多线程与并发编程全解析

    Java多线程与并发编程全解析 多线程编程是Java中最具挑战性的部分之一,它能够显著提升应用程序的性能和响应能力.本文将全面解析Java多线程与并发编程的核心概念.线程安全机制以及JUC工具类的使用 ...

  3. L2-2、示范教学与角色扮演:激发模型"模仿力"与"人格"

    一.Few-shot 教学的核心原理与优势 在与大语言模型交互时,Few-shot(少样本)教学是一种强大的提示技术.其核心原理是通过提供少量示例,引导模型理解我们期望的输出格式和内容风格. Few- ...

  4. Kubernetes中的多租户

    多租户 多租户集群由多个用户和/或工作负载共享,这些用户和/或工作负载被称为"租户".多租户集群的运营方必须将租户彼此隔离,以最大限度地减少被盗用的租户或恶意租户可能对集群和其他租 ...

  5. P7185 [CRCI2008-2009] CIJEVI

    hack&&虐题解爽! 私题里有 \(3\) 组 hack. 还得是中模拟. 从 M 或者 Z 遍历整个管道,若遇到断点.断点有两种情况,若与 M 或者 Z 不直接接触,枚举附近的一个 ...

  6. C#/.NET/.NET Core技术前沿周刊 | 第 41 期(2025年6.1-6.8)

    前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...

  7. k8s gpu共享

    k8s gpu 共享方案 1.配置环境变量实现 通过在POD的启动文件中,配置NVIDIA_VISIBLE_DEVICES的环境变量,指定显卡号.NVIDIA_VISIBLE_DEVICES可配置为具 ...

  8. wso2~api生命周期与关联事件

    api生命周期 CREATED 建立 PRE-RELEASED 预发布 PUBLISHED 发布 DEPRECATED 删除 BLOCKED 冻结 RETIRED 退役 状态为Published的ap ...

  9. 袋鼠云数栈UI5.0体验升级背后的故事:可用性原则与交互升级

    最近,我们袋鼠云的UED部⻔小伙伴们,不声不响地⼲了⼀件⼤事--升级了全新设计语言「数栈UI5.0」. 众所周知,用户在使用产品时,是一个动态的过程,用户和产品之间进行交互的可用性,能否让用户愉悦.快 ...

  10. oracle库exp导出dmp文件导入 expdp导出不适用

    //创建用户 create user test identified by test; grant connect,resource,dba to test; //cmd 命令执行dmp文件 imp ...