多Agent協作入(ru)門:AgentGroupChat
大家(jia)好,我(wo)是Edison。
近日抽(chou)空學習了下Semantic Kernel提(ti)供的AgentGroupChat對(dui)象(xiang)寫了一個多Agent群組對(dui)話的Demo,總結(jie)一下分享(xiang)與你(ni)。當然,多Agent協(xie)作還有其他(ta)的方式,就留(liu)到(dao)后(hou)續慢(man)慢(man)介紹給你(ni)。
AgentChat是什么鬼?
在Semantic Kernel中,AgentChat提(ti)供(gong)了一(yi)個框架,可(ke)以啟用多個代(dai)理之間的交(jiao)互,即使它們屬于不同(tong)類型的代(dai)理。 這使得(de) ChatCompletionAgent和 OpenAIAssistantAgent 可(ke)以在同(tong)一(yi)對(dui)話中協同(tong)工作。 AgentChat還定義了用于啟動(dong)代(dai)理之間協作的入(ru)口點,無(wu)論是通(tong)過多個響應還是單個代(dai)理響應。
在實現(xian)層面,AgentGroupChat 提供了 AgentChat 的(de)(de)具(ju)體實現(xian),它是(shi)使(shi)用(yong)基于策略的(de)(de)方法來管(guan)理(li)聊天的(de)(de)動(dong)態。

快(kuai)速(su)入門案例
這里我們來快速實現一(yi)個(ge)案(an)例:Reviewer & Writer,讓這兩個(ge)不同(tong)功(gong)能的(de)Agent能夠(gou)相(xiang)互配合協作,完成(cheng)一(yi)個(ge)指定(ding)的(de)功(gong)能:
(1)Reviewer 可以(yi)審核用戶輸(shu)入(ru)的文案并給出優化建議;
(2)Writer 則根據優(you)化建(jian)議進行(xing)文案的(de)優(you)化創作;
為了簡單(dan)地實現這個功能,我們創建(jian)一個.NET控制臺項目,然后安裝以下包:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json
Microsoft.SemanticKernel.Agents.Core
Microsoft.SemanticKernel.Agents.OpenAI (Preview版本)
需要注(zhu)意的是,由于Semantic Kernel的較多功能目前還處于實驗預覽階段,所以建(jian)議在(zai)該項目的csproj文件中加入以下配(pei)置(zhi),統一取消警告(gao):
<PropertyGroup> <NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn> </PropertyGroup>
創建一(yi)個(ge)appsettings.json配置(zhi)文件,填入以下關(guan)于LLM API的(de)(de)配置(zhi),其中API_KEY請(qing)輸(shu)入你自己的(de)(de):
{ "LLM": { "BASE_URL": "//api.siliconflow.cn", "API_KEY": "******************************", "MODEL_ID": "Qwen/Qwen2.5-32B-Instruct" } }
這里我們使用SiliconCloud提供的 Qwen2.5-32B-Instruct 模型,你可以通過: 注冊一個賬號,獲取大量(liang)免費的Token來(lai)來(lai)進行這個DEMO實驗。
有了LLM API,我(wo)們可以(yi)創(chuang)建一個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();
接下(xia)來,我們就一(yi)步一(yi)步地來看看核心的代(dai)碼。
定義兩(liang)個Agent
這里我們(men)來(lai)定義兩個Agent:Reviewer & Writer:
(1)Reviewer
public class ReviewerAgent { public const string AgentName = "Reviewer"; public static ChatCompletionAgent Build(Kernel kernel) { var toolKernel = kernel.Clone(); toolKernel.Plugins.AddFromType<ClipboardAccessPlugin>(); var reviewerAgent = new ChatCompletionAgent() { Name = AgentName, Instructions = """ Your responsibility is to review and identify how to improve user provided content. If the user has providing input or direction for content already provided, specify how to address this input. Never directly perform the correction or provide example. Once the content has been updated in a subsequent response, you will review the content again until satisfactory. Always copy satisfactory content to the clipboard using available tools and inform user. RULES: - Only identify suggestions that are specific and actionable. - Verify previous suggestions have been addressed. - Never repeat previous suggestions. """, Kernel = toolKernel, Arguments = new KernelArguments( new OpenAIPromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }) }; return reviewerAgent; } }
(2)Writer
public class WriterAgent { public const string AgentName = "Writer"; public static ChatCompletionAgent Build(Kernel kernel) { var writerAgent = new ChatCompletionAgent() { Name = AgentName, Instructions = """ Your sole responsibility is to rewrite content according to review suggestions. - Always apply all review direction. - Always revise the content in its entirety without explanation. - Never address the user. """, Kernel = kernel }; return writerAgent; } }
這(zhe)里(li)可(ke)以通過靜(jing)態方法直接Build出來兩個(ge)Agent實(shi)例:
// Initialize Reviewer Agent Console.WriteLine("Now loading the Reviewer Agent..."); var reviewerAgent = ReviewerAgent.Build(kernel); // Initialize Writer Agent Console.WriteLine("Now loading the Writer Agent..."); var writerAgent = WriterAgent.Build(kernel);
定義選擇策略 和 終止策略
對(dui)于多Agent協作(zuo),在AgentGroupChat中需(xu)要定義選擇(ze)Agent輪次的(de)策(ce)略(即(ji)SelectionStrategy)和 終止聊(liao)天循環的(de)策(ce)略(即(ji)TerminationStrategy)。我們可以通過使用(yong) AgentGroupChat.CreatePromptFunctionForStrategy 來輕松(song)地實現,它提供了一種方(fang)便的(de)機制,避免了對(dui)消息參(can)數進行HTML編(bian)碼:
(1)SelectionStrategy
所謂選擇策略,就(jiu)是(shi)如(ru)何定義下一(yi)個發言的是(shi)誰,或者誰來接龍。這里我們首先讓Reviewer評估用戶(hu)輸入的內容,如(ru)果覺得需要優化(hua),就(jiu)給出(chu)建議(yi),下一(yi)個就(jiu)輪到(dao)Writer來進行(xing)優化(hua)內容協作。
// Define Selection Policy var selectionFunction = AgentGroupChat.CreatePromptFunctionForStrategy( $$$""" Examine the provided RESPONSE and choose the next participant. State only the name of the chosen participant without explanation. Never choose the participant named in the RESPONSE. Choose only from these participants: - {{{ReviewerAgent.AgentName}}} - {{{WriterAgent.AgentName}}} Always follow these rules when choosing the next participant: - If RESPONSE is user input, it is {{{ReviewerAgent.AgentName}}}'s turn. - If RESPONSE is by {{{ReviewerAgent.AgentName}}}, it is {{{WriterAgent.AgentName}}}'s turn. - If RESPONSE is by {{{WriterAgent.AgentName}}}, it is {{{ReviewerAgent.AgentName}}}'s turn. RESPONSE: {{${{{KernelFunctionTerminationStrategy.DefaultHistoryVariableName}}}}} """);
(2)TerminationStrategy
這(zhe)個(ge)終(zhong)止策略(lve)至關(guan)重要,它定義了如(ru)何評(ping)估(gu)什么(me)時候退出聊(liao)天循(xun)環。對于這(zhe)個(ge)案例來說,就(jiu)是評(ping)估(gu)Writer優(you)化的內容是否滿足用戶的需求了。
// Define Termination Policy const string TerminationToken = "yes"; var terminationFunction = AgentGroupChat.CreatePromptFunctionForStrategy( $$$""" Examine the RESPONSE and determine whether the content has been deemed satisfactory. If content is satisfactory, respond with a single word without explanation: {{{TerminationToken}}}. If specific suggestions are being provided, it is not satisfactory. If no correction is suggested, it is satisfactory. RESPONSE: {{${{{KernelFunctionTerminationStrategy.DefaultHistoryVariableName}}}}} """);
這兩種策(ce)略都只需要了解最新的1條聊天消息,因此可以使用下面的代碼來定一個HistoryReducer,它可以只將最近的1條消息作為歷史記錄傳遞給下一個聊天參與者。這將減少Token消耗 也能 一定程(cheng)度提高性能。
var historyReducer = new ChatHistoryTruncationReducer(1);
初始化(hua)AgentGroupChat
AgentGroupChat對象會將之(zhi)前定(ding)義的所有內容(rong)聚集在一起,相當于(yu)我們創建了一個(ge)微信群(qun)聊(liao),添加了群(qun)聊(liao)的對象(Reviewer + Writer),以及告訴(su)群(qun)主或管(guan)理(li)員(yuan)如何(he)選擇Agent的策(ce)(ce)略(lve) 和 終止循環(huan)的策(ce)(ce)略(lve)。
// Initialize AgentGroupChat var groupChat = new AgentGroupChat(reviewerAgent, writerAgent) { ExecutionSettings = new AgentGroupChatSettings() { SelectionStrategy = new KernelFunctionSelectionStrategy(selectionFunction, kernel) { InitialAgent = reviewerAgent, HistoryReducer = historyReducer, HistoryVariableName = KernelFunctionTerminationStrategy.DefaultHistoryVariableName, ResultParser = (result) => { var val = result.GetValue<string>() ?? ReviewerAgent.AgentName; return val.ReplaceLineEndings("\n").Trim(); } }, TerminationStrategy = new KernelFunctionTerminationStrategy(terminationFunction, kernel) { Agents = [reviewerAgent], HistoryReducer = historyReducer, HistoryVariableName = KernelFunctionTerminationStrategy.DefaultHistoryVariableName, MaximumIterations = 10, ResultParser = (result) => { var val = result.GetValue<string>() ?? string.Empty; return val.Contains(TerminationToken, StringComparison.OrdinalIgnoreCase); } } } };
開始聊天循環
下面的(de)代碼也是(shi)老面孔了(le),就不過(guo)多(duo)介紹了(le):
// Start Working! Console.WriteLine("----------Agents are Ready. Let's Start Working!----------"); while (true) { Console.WriteLine("User> "); var input = Console.ReadLine(); if (string.IsNullOrWhiteSpace(input)) continue; input = input.Trim(); if (input.Equals("EXIT", StringComparison.OrdinalIgnoreCase)) break; if (input.Equals("RESET", StringComparison.OrdinalIgnoreCase)) { await groupChat.ResetAsync(); Console.ResetColor(); Console.WriteLine("System> Conversation has been reset!"); continue; } groupChat.AddChatMessage(new ChatMessageContent(AuthorRole.User, input)); groupChat.IsComplete = false; try { await foreach (var response in groupChat.InvokeAsync()) { if (string.IsNullOrWhiteSpace(response.Content)) continue; Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(); Console.WriteLine($"{response.AuthorName} ({response.Role})> "); Console.WriteLine($"{response.Content.ReplaceLineEndings("\n").Trim()}"); } Console.ResetColor(); Console.WriteLine(); } catch (HttpOperationException ex) { Console.ResetColor(); Console.WriteLine(ex.Message); if (ex.InnerException != null) { Console.WriteLine(ex.InnerException.Message); if (ex.InnerException.Data.Count > 0) Console.WriteLine(JsonSerializer.Serialize(ex.InnerException.Data, new JsonSerializerOptions() { WriteIndented = true })); } } } Console.ResetColor(); Console.WriteLine("----------See you next time!----------"); Console.ReadKey();
效(xiao)果(guo)展示(shi)
第一輪:我給了(le)它一段(duan)待優(you)化的(de)文(wen)本段(duan)落,文(wen)本內容如下。
Semantic Kernel (SK) is an open-source SDK that enables developers to build and orchestrate complex AI workflows that involve natural language processing (NLP) and machine learning models. It provides a flexible platform for integrating AI capabilities such as semantic search, text summarization, and dialogue systems into applications. With SK, you can easily combine different AI services and models, define their relationships, and orchestrate interactions between them.
第二輪:讓(rang)Agent幫忙將其拆(chai)分為兩個段(duan)落

第三輪:提出(chu)更(geng)高的要求,需要更(geng)加學術化以(yi)便大學教授能(neng)夠欣賞

可以看到,Reviewer 和 Writer 的(de)配(pei)合還(huan)是不(bu)錯,準(zhun)確完(wan)成(cheng)了我給它們的(de)Task。
小結(jie)
本文介紹了如何通過(guo)Semantic Kernel提供的AgentGroupChat來實現(xian)多(duo)Agent的協作,其(qi)中(zhong)最要的部分就是定義選擇輪次(ci)策略 和 終(zhong)止聊天(tian)策略,相信(xin)通過(guo)這個案(an)例(li)你能夠有個感性(xing)的認識。
當(dang)然,除了群組聊天模式之外(wai),多Agent協作還有(you)很多其他的(de)方(fang)式(比如 并行(xing)、順序、移交、磁性等(deng)等(deng)),也(ye)還有(you)不同的(de)框架(jia)實現(如AutoGen),這(zhe)就(jiu)留到后面一一介紹(shao)給你,因為我也(ye)還在學。
示例(li)源碼
Github:
參考(kao)資料
Microsoft Learn:
推薦學習
圣杰:《.NET+AI | Semantic Kernel入門到精通》

作者:愛(ai)迪生
出處:
本文(wen)(wen)版權歸(gui)作者和博客(ke)園共有,歡迎轉(zhuan)載,但未經作者同(tong)意必須(xu)保(bao)留此段聲明,且在文(wen)(wen)章頁面明顯位置給出(chu)原文(wen)(wen)鏈接。

本文介紹了如何通過Semantic Kernel提供的AgentGroupChat來實現多Agent的協作,其中最要的部分就是定義選擇輪次策略 和 終止聊天策略,相信通過這個案例你能夠有個感性的認識。當然,除了群組聊天模式之外,多Agent協作還有很多其他的方式(比如 并行、順序、移交、磁性等等),也還有不同的框架實現(如AutoGen),這就留到后面一一介紹給你,因為我也還在學。