多Agent協作入門:移交編(bian)排模(mo)式
大家好,我是Edison。
上一篇我們學習了Semantic Kernel中的群聊編排模式,它非(fei)常適合集思廣益、協作解決問題(ti)等類型(xing)任務場景。今天,我(wo)們(men)學習新的模式(shi):移交編排(pai)。
移交編排模式簡介
在移(yi)(yi)(yi)交(也可以叫做交接)編排模(mo)式中,允許各(ge)個Agent根據上下文或(huo)用(yong)戶(hu)請求相(xiang)互轉移(yi)(yi)(yi)控制權,每(mei)個Agent都(dou)可以通過適當的(de)(de)專(zhuan)業知(zhi)識(shi)將對話“移(yi)(yi)(yi)交”給另一個Agent,確保每(mei)個Agent處理(li)任務的(de)(de)某個指定部分。這種模(mo)式非常(chang)適合于客(ke)戶(hu)支持(客(ke)服)、專(zhuan)家系統或(huo)需要(yao)動(dong)態委(wei)派類型的(de)(de)任務場景(jing)。
下(xia)圖展示了一個客戶支(zhi)持的用例(li)場(chang)景,當用戶提(ti)交售(shou)后請(qing)求(qiu),先由某個前臺代理(li)(這里(li)是General Support)進行請(qing)求(qiu)分析,并將(jiang)具體請(qing)求(qiu)轉移給某個后臺專家(如(ru)(ru)Technical Expert)或(huo) 計費人員(如(ru)(ru)Billing)。

實現移交編排(pai)模式(shi)
這(zhe)里我(wo)們(men)來(lai)實現一個(ge)客戶支(zhi)持(chi)的DEMO,假設我(wo)們(men)是一個(ge)電商的后臺客服中心,我(wo)們(men)找了一群AI Agent來(lai)幫我(wo)們(men)進行一些(xie)訂單查詢、退(tui)款、退(tui)貨等通用(yong)類請求的客戶服務支(zhi)持(chi)。

我們(men)定義(yi)4個Agent:
(1)分流客服Agent:負責初(chu)步分流客戶問(wen)題;
(2)訂單狀態查詢Agent:負責處理客戶的訂(ding)單狀(zhuang)態查詢問題;
(3)訂單退貨處理Agent:負(fu)責(ze)處理客戶申(shen)請的退貨(huo)請求;
(4)訂單退款處理Agent:負責處理(li)客(ke)戶申請的(de)退款請求;
為了簡單地實現這個功能,我們還(huan)是創建一個.NET控制(zhi)臺項目(mu),然后(hou)安裝(zhuang)以下包(bao):
Microsoft.SemanticKernel.Agents.Core
Microsoft.SemanticKernel.Agents.OpenAI (Preview版(ban)本)
Microsoft.SemanticKernel.Agents.Orchestration (Preview版(ban)本)
Microsoft.SemanticKernel.Agents.Runtime.InProcess (Preview版(ban)本)
需要注(zhu)意的(de)(de)是,由于(yu)(yu)Semantic Kernel的(de)(de)較多(duo)功能(neng)目前還處于(yu)(yu)實驗預覽階段,所以建議在該項目的(de)(de)csproj文(wen)件中加入以下配置,統一(yi)取消警告:
<PropertyGroup> <NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn> </PropertyGroup>
創建一(yi)個(ge)appsettings.json配(pei)置(zhi)文件,填入以下關于LLM API的配(pei)置(zhi),其中API_KEY請輸入你(ni)自己的:
{
"LLM": {
"BASE_URL": "//api.siliconflow.cn",
"API_KEY": "******************************",
"MODEL_ID": "Qwen/Qwen2.5-32B-Instruct"
}
}
這里我們使用SiliconCloud提供的 Qwen2.5-32B-Instruct 模型,你可以通過這個URL注(zhu)冊賬號: 獲(huo)取大(da)量免費的Token來進行本次實驗。
有(you)了LLM API,我們可(ke)以創建一個Kernel供(gong)后(hou)續使用(yong),這也是老面(mian)孔了:
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();
接下來,我們(men)就一步一步地來看看核心的代(dai)碼。
定義4個Agent
這里我們來定義4個(ge)Agent:
(1)分流客服Agent:負責初步分流客戶(hu)問題(ti);
var triageAgent = new ChatCompletionAgent() { Name = "TriageAgent", Description = "處理客戶請求", Instructions = "一個(ge)負責分流客戶問題的客服智能體", Kernel = kernel.Clone() };
(2)訂單狀態查詢Agent:負責處理客戶的訂單狀態查詢問題;
var statusAgent = new ChatCompletionAgent() { Name = "OrderStatusAgent", Description = "一個負責查詢訂單狀態的客(ke)服智能體", Instructions = "處理訂單(dan)狀態請求", Kernel = kernel.Clone() }; statusAgent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(new OrderStatusPlugin()));
(3)訂單退貨處理Agent:負責處理(li)客戶申請(qing)的退貨請(qing)求;
var returnAgent = new ChatCompletionAgent() { Name = "OrderReturnAgent", Description = "一個負責處理(li)訂單退貨的客服智(zhi)能體", Instructions = "處理訂(ding)單(dan)退貨并記錄退貨原因(yin)(用戶需確認原因(yin):不(bu)想要了 或 7天(tian)無(wu)理由(you)退換 或 沒有時間消(xiao)費)", Kernel = kernel.Clone() }; returnAgent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(new OrderReturnPlugin()));
(4)訂單退款處理Agent:負(fu)責處理客戶(hu)申請的退款請求(qiu);
var refundAgent = new ChatCompletionAgent() { Name = "OrderRefundAgent", Description = "一個負(fu)責處理訂(ding)單(dan)退款(kuan)的客服(fu)智能(neng)體", Instructions = "處理訂單(dan)退(tui)款請(qing)求并記(ji)錄退(tui)款原因(yin)(用戶(hu)需確認原因(yin):不想(xiang)要(yao)了 或 7天無理由退(tui)換(huan) 或 沒有(you)時間(jian)消費)", Kernel = kernel.Clone() }; refundAgent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(new OrderRefundPlugin()));
需要注意的(de)是:這里(li)多個Agent都有使用Function Calling調(diao)用外部方(fang)法實(shi)(shi)現一些功能,所以在Kernel的(de)設(she)置處通過Clone方(fang)法實(shi)(shi)現隔(ge)離。
下面(mian)是(shi)這幾個訂單幫助的(de)Plugin的(de)定義:
public sealed class OrderRefundPlugin { [KernelFunction] public string ProcessReturn(string orderId, string reason) => $"訂單 {orderId} 的退款申請已通(tong)過(guo)!退款理由:{reason}"; } public sealed class OrderReturnPlugin { [KernelFunction] public string ProcessReturn(string orderId, string reason) => $"訂單 {orderId} 的退(tui)(tui)貨(huo)申(shen)請(qing)已通過!退(tui)(tui)貨(huo)理由(you):{reason} "; } public sealed class OrderStatusPlugin { [KernelFunction] public string CheckOrderStatus(string orderId) => $"訂(ding)單 {orderId} 已發貨(huo) 并將于 2-3日(ri)內送(song)達(da)!"; }
選擇(ze)編排模式
這里我們選擇的是群聊編排模式:HandoffOrchestration,除了將需(xu)要編排的4個Agent作為參數(shu)傳遞給它之外,我們還需(xu)要定義(yi)一(yi)個移交(jiao)流程,讓Agent知(zhi)道他們應(ying)該(gai)如(ru)何實(shi)現交(jiao)接。
var handoffs = OrchestrationHandoffs .StartWith(triageAgent) .Add(source: triageAgent, targets: [statusAgent, returnAgent, refundAgent]) // 分流客服可交接(jie)給(gei)狀態、退貨、退款智能體 .Add(source: statusAgent, target: triageAgent, "如非訂單狀態相關(guan)問題則(ze)交回分(fen)流客(ke)服") .Add(source: returnAgent, target: triageAgent, "如非(fei)退貨相關問題則交(jiao)回分流客服") .Add(source: refundAgent, target: triageAgent, "如非退款相關(guan)問題則交回分(fen)流客服(fu)");
同時,為了實現聊天(tian)記錄的存儲和監控,自定義(yi)一個Monitor類:
public sealed class OrchestrationMonitor { public ChatHistory History { get; } = new ChatHistory(); public ValueTask ResponseCallback(ChatMessageContent response) { History.Add(response); return ValueTask.CompletedTask; } }
最后,初(chu)始化移交編排:
// -- Create the HandoffOrchestration var orchestration = new HandoffOrchestration( handoffs, members: [triageAgent, statusAgent, returnAgent, refundAgent]) { Name = "CustomerSupportOrchestration", Description = "處理客戶請求并根(gen)據問題類(lei)型交(jiao)接(jie)給對應的智能體", 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中,需要(yao)運(yun)行(xing)時(Runtime)才能管理Agent的(de)執行(xing),因此這里(li)我們需要(yao)在正式(shi)開(kai)始前使用InProcessRuntime并啟動(dong)起來。
// Start the Runtime var runtime = new InProcessRuntime(); await runtime.StartAsync();
調用編排 并 收集結果
準備工(gong)作(zuo)差不多了,現在我(wo)們可以(yi)開始調用編排(pai)了。這也是老面孔代碼了,不(bu)過多解(jie)釋。
唯一需要(yao)注意的是:這里設(she)置(zhi)TimeSpan.FromSeconds(100*3)是為了給足對話(hua)時間(jian)。
// Start the Chat Console.WriteLine($"Welcome to use CustomerSupport!\n"); var task = "你好(hao),我需要訂單上的幫助"; 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($"# 處理結(jie)果總(zong)結(jie): \n{output}\n"); } catch (HttpOperationException ex) { Console.WriteLine($"Exception: {ex.Message}"); } finally { await runtime.RunUntilIdleAsync(); Console.WriteLine($"\n----------See you next time!----------"); Console.ReadKey(); }
上面的代碼示例中我們給出的第一句話是:“你(ni)好,我需要訂單上的幫(bang)助”來進入客服場景。
效果展示
假設我是客戶,我有3個(ge)訂(ding)單(dan),想要查詢一個(ge)訂(ding)單(dan)的狀態,以及對另外(wai)兩(liang)個(ge)訂(ding)單(dan)進行(xing)退款和退貨,對話過(guo)程如下圖(tu)所示。
請求1:查詢訂單狀態

請(qing)求2&3:申請(qing)退款(kuan) 和 退貨(huo)

示例源碼
GitHub:
小結
本文介紹了移交編(bian)排模式的基本概念(nian),然后通(tong)過一個案例(li)介紹了如何實(shi)現(xian)一個移交編(bian)排的經典場景:客戶(hu)支持,相信通(tong)過這(zhe)個案例(li)你能夠有個感(gan)性(xing)的認識。
下一篇(pian),我們將學習(xi)磁性編(bian)排模式。
參考資料
Microsoft Learn:
推薦學習
圣杰:《.NET+AI | Semantic Kernel入門到精通》

作者:愛迪生
出處:
本文(wen)版權歸作(zuo)者(zhe)和博客園共(gong)有(you),歡迎(ying)轉載,但未(wei)經作(zuo)者(zhe)同(tong)意必須保留此(ci)段(duan)聲明,且在文(wen)章頁面(mian)明顯位置(zhi)給出原文(wen)鏈(lian)接(jie)。

在移交(也可以叫做交接)編排模式中,允許各個Agent根據上下文或用戶請求相互轉移控制權,每個Agent都可以通過適當的專業知識將對話“移交”給另一個Agent,確保每個Agent處理任務的某個指定部分。這種模式非常適合于客戶支持(客服)、專家系統或需要動態委派類型的任務場景。