多Agent协作入门:群组聊天-AgentGroupChat
大家好,我是Edison。
近日抽空学习了下Semantic Kernel提供的AgentGroupChat对象写了一个多Agent群组对话的Demo,总结一下分享与你。当然,多Agent协作还有其他的方式,就留到后续慢慢介绍给你。
AgentChat是什么鬼?
在Semantic Kernel中,AgentChat提供了一个框架,可以启用多个代理之间的交互,即使它们属于不同类型的代理。 这使得 ChatCompletionAgent和 OpenAIAssistantAgent 可以在同一对话中协同工作。 AgentChat还定义了用于启动代理之间协作的入口点,无论是通过多个响应还是单个代理响应。
在实现层面,AgentGroupChat 提供了 AgentChat 的具体实现,它是使用基于策略的方法来管理聊天的动态。
快速入门案例
这里我们来快速实现一个案例:Reviewer & Writer,让这两个不同功能的Agent能够相互配合协作,完成一个指定的功能:
(1)Reviewer 可以审核用户输入的文案并给出优化建议;
(2)Writer 则根据优化建议进行文案的优化创作;
为了简单地实现这个功能,我们创建一个.NET控制台项目,然后安装以下包:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json
Microsoft.SemanticKernel.Agents.Core
Microsoft.SemanticKernel.Agents.OpenAI (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 模型,你可以通过:https://cloud.siliconflow.cn/i/DomqCefW 注册一个账号,获取大量免费的Token来来进行这个DEMO实验。
有了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();
接下来,我们就一步一步地来看看核心的代码。
定义两个Agent
这里我们来定义两个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;
}
}
这里可以通过静态方法直接Build出来两个Agent实例:
// 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);
定义选择策略 和 终止策略
对于多Agent协作,在AgentGroupChat中需要定义选择Agent轮次的策略(即SelectionStrategy)和 终止聊天循环的策略(即TerminationStrategy)。我们可以通过使用 AgentGroupChat.CreatePromptFunctionForStrategy 来轻松地实现,它提供了一种方便的机制,避免了对消息参数进行HTML编码:
(1)SelectionStrategy
所谓选择策略,就是如何定义下一个发言的是谁,或者谁来接龙。这里我们首先让Reviewer评估用户输入的内容,如果觉得需要优化,就给出建议,下一个就轮到Writer来进行优化内容协作。
// 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
这个终止策略至关重要,它定义了如何评估什么时候退出聊天循环。对于这个案例来说,就是评估Writer优化的内容是否满足用户的需求了。
// 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}}}}}
""");
这两种策略都只需要了解最新的1条聊天消息,因此可以使用下面的代码来定一个HistoryReducer,它可以只将最近的1条消息作为历史记录传递给下一个聊天参与者。这将减少Token消耗 也能 一定程度提高性能。
var historyReducer = new ChatHistoryTruncationReducer(1);
初始化AgentGroupChat
AgentGroupChat对象会将之前定义的所有内容聚集在一起,相当于我们创建了一个微信群聊,添加了群聊的对象(Reviewer + Writer),以及告诉群主或管理员如何选择Agent的策略 和 终止循环的策略。
// 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);
}
}
}
};
开始聊天循环
下面的代码也是老面孔了,就不过多介绍了:
// 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();
效果展示
第一轮:我给了它一段待优化的文本段落,文本内容如下。
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.
第二轮:让Agent帮忙将其拆分为两个段落
第三轮:提出更高的要求,需要更加学术化以便大学教授能够欣赏
可以看到,Reviewer 和 Writer 的配合还是不错,准确完成了我给它们的Task。
小结
本文介绍了如何通过Semantic Kernel提供的AgentGroupChat来实现多Agent的协作,其中最要的部分就是定义选择轮次策略 和 终止聊天策略,相信通过这个案例你能够有个感性的认识。
当然,除了群组聊天模式之外,多Agent协作还有很多其他的方式(比如 并行、顺序、移交、磁性等等),也还有不同的框架实现(如AutoGen),这就留到后面一一介绍给你,因为我也还在学。
示例源码
Github: https://github.com/EdisonTalk/MultiAgentSamples
参考资料
Microsoft Learn: https://learn.microsoft.com/en-us/semantic-kernel/support/archive/agent-chat-example?pivots=programming-language-csharp
推荐学习
圣杰:《.NET+AI | Semantic Kernel入门到精通》
多Agent协作入门:群组聊天-AgentGroupChat的更多相关文章
- vue-cli3.0 Typescript 项目集成环信WebIM 群组聊天
项目背景 环信webim 官方没有vue版本的,自己就根据sdk重写了个vue版本的,只实现了基础的 登录 群组功能,其他的可以根据需要参考官方文档,添加相应的功能. 环信webim SDK相关文档: ...
- Android之Socket群组聊天
在这只做了一个简单的例子,没有用到数据库,思路就是客户端发送信息到服务器端,服务器端转发所有数据到客户端,校验服务器端发来消息是否是自己发出的,如果是自己发出的,则不显示自己的消息 贴一下Androi ...
- ASP.NET SignalR 系列(五)之群组推送
在上一章介绍了 一对一推送的方式,这章重点介绍下群组推送和多人推送 群组主要就是用到了方法:Groups.Add(Context.ConnectionId, groupName); 将不同的连接id加 ...
- java ssm 后台框架平台 项目源码 websocket即时聊天发图片文字 好友群组 SSM源码
官网 http://www.fhadmin.org/D 集成安全权限框架shiro Shiro 是一个用 Java 语言实现的框架,通过一个简单易用的 API 提供身份验证和授权,更安全,更可靠E ...
- Activiti6.0 工作流引擎 websocket即时聊天发图片文字 好友群组 SSM源码
即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...
- java工作流引擎 Activiti6.0 websocket 即时聊天发图片文字 好友群组 SSM源码
时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 工作流模块--------------------------------------------------------- ...
- 【实战】SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
JavaDog Chat v1.0.0 基于SpringBoot+uniapp简单通讯聊天软件 项目介绍 JavaDog Chat 简单通讯聊天软件是基于SpringBoot+MybatisPlus+ ...
- 微信小程序的机会在于重新理解群组与二维码
历时一年,唯一一个尚未发布就获得Pony Ma与Allen Zhang站台的产品:微信小程序,将于2017年1月9日正式上线了.我很期待.唯一要警惕的是:防止长考出臭棋. 在上线前夕,我对于如何借助小 ...
- XMPP系列(六)---创建群组
最近公司项目需要,要做一个自己的IMSDK,顺便先把之前没有记录的群聊功能记录一下. 先上资料,查看XMPP群聊相关的资料,可以去这里看协议:XEP-0045 . 创建群组 XMPP 框架里有一个类X ...
- python3企业微信群组报警
公司提出一个需求需要做一个企业微信的一个消息推送,需要将消息发送到聊天群里详细信息如下. 如何创建应用请阅读我的上篇文章:https://www.cnblogs.com/wangyajunblog/p ...
随机推荐
- 一文速通Python并行计算:06 Python多线程编程-基于队列进行通信
一文速通 Python 并行计算:06 Python 多线程编程-基于队列进行通信 摘要: 队列是一种线性数据结构,支持先进先出(FIFO)操作,常用于解耦生产者和消费者.慢速生产-快速消费场景中,队 ...
- 开箱即用!推荐一款Python开源项目:DashGo,支持定制改造为测试平台!
大家好,我是狂师. 市面上的开源后台管理系统项目层出不穷,对应所使用到的技术栈也不尽相同. 今天给大家推荐一款开源后台管理系统: DashGo,不仅部署起来非常的简单,而且它是基于Python技术栈实 ...
- mybatis的模糊查询的实现方式
一.比较灵活 1:xml的配置 <select id="selectUserByUsername1" parameterType="string" res ...
- windows下jdk版本切换(bat)
1.jdk下载 Oracle官网 https://www.oracle.com/cn/ 资源->下载->Java下载 jdk当前最新版本 jdk22版本 jdk8版本 当前页面向下拉 2. ...
- 小程序组件使用全局样式app.wxss
Component({ options: { addGlobalClass: true } })
- mac 系统软件推荐
幕布: https://mubu.com/home 石墨文档: https://shimo.im
- mysql如何替换部分字符串
本篇内容主要讲解"mysql如何替换部分字符串",感兴趣的朋友不妨来看看.本文介绍的方法操作简单快捷,实用性强.下面就让小编来带大家学习"mysql如何替换部分字符串&q ...
- python基础必练题!!
水仙花数 水仙花数 info = 3 while info: # 用户输入数字 try: print(f"请输入数字,您有{info}次机会!!") num = int(input ...
- 从零到一:利用金仓社区数据,LoRa微调与Spring AI 构建私有化千问模型
上次我们在Coze平台上成功搭建了一个针对金仓问题的解决助手.这个智能体的核心工作流程相对简单:每次它通过HTTP接口调用插件,在金仓平台内部进行搜索,随后利用大模型的推理能力对查询结果进行分析,从而 ...
- vue3 基础-non-props 特性
本篇探讨当父组件通过属性给子组件传数据时, 子组件如果不通过 props 属性进行接收, 那数据会挂载到哪里, 以及子组件如何能使用这些数据. 正常的父子组件传值 <!DOCTYPE html& ...