大家好,我是Edison。

上一篇我们学习了Semantic Kernel中的顺序编排模式,它非常适合如文档审阅、数据处理管道等工作流类型任务场景。今天,我们学习新的模式:群聊编排。

群聊编排模式简介

在群聊编排模式中,各个Agent就像加入了一个微信群,由群管理员协调进行群聊对话。这种模式非常适合于模拟会议、辩论或协作解决问题的会议类型场景。

下图展示了一个讨论业务建议的用例,由一个Chat Manager负责主持对话,依次让三个参与对话的Agent进行建议发表。这个Chat Manager就像是群管理员,它不仅负责主持对话,也会在必要时引入人工干预。

实现群聊编排模式

这里我们来实现一个和上一节类似的DEMO,我们定义2个Agent:广告文案写手(CopyWriter) 和 编辑/审稿人(Editor),假设他们是一个小Team,在承接广告文案的创作。假设每个文案都需要审稿人审核,可以有多轮审核,直到审核确认OK才能交付。

为了简单地实现这个功能,我们还是创建一个.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();

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

定义Agent

这里我们来定义2个Agent:Writer,Editor

(1)Writer 文案写手

var writerAgent = new ChatCompletionAgent()
{
Name = "CopyWriter",
Instructions = """
You are a copywriter with ten years of experience and are known for brevity and a dry humor.
The goal is to refine and decide on the single best copy as an expert in the field.
Only provide a single proposal per response.
You're laser focused on the goal at hand.
Don't waste time with chit chat.
Consider suggestions when refining an idea.
""",
Description = "A copy writer.",
Kernel = kernel
};

(2)Editor 审核编辑

var editorAgent = new ChatCompletionAgent()
{
Name = "Reviewer",
Instructions = """
You are an art director who has opinions about copywriting born of a love for David Ogilvy.
The goal is to determine if the given copy is acceptable to print.
If so, state that it is approved.
If not, provide insight on how to refine suggested copy without example.
""",
Description = "An editor.",
Kernel = kernel
};

选择编排模式

这里我们选择的是群聊编排模式:GroupChatOrchestration,将需要编排的2个Agent作为参数传递给它。

需要注意的是:这里我们选择Semantic Kernel预定义好的群管理员类 RoundRobinGroupChatManager,顾名思义,它的策略就是轮流让参与的群成员发言,没有特殊的指定逻辑

// Set up the GroupChat Orchestration
ChatHistory history = [];
ValueTask responseCallback(ChatMessageContent response)
{
history.Add(response);
return ValueTask.CompletedTask;
}
// Use RoundRobinGroupChatManager to manage the conversation flow
const string topic = "Create a slogan for a new electric SUV that is affordable and fun to drive.";
var orchestration = new GroupChatOrchestration(
new RoundRobinGroupChatManager { MaximumInvocationCount = 5 }, // Maximum 5 rounds of conversation
writerAgent,
editorAgent)
{
ResponseCallback = responseCallback
};

启动运行时

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

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

调用编排 并 收集结果

准备工作差不多了,现在我们可以开始调用编排了。

这也是老面孔代码了,不过多解释。

// Start the Chat
Console.WriteLine($"# INPUT: {topic}{Environment.NewLine}");
try
{
// Invoke the Orchestration
var result = await orchestration.InvokeAsync(topic, runtime);
// Collect Results from multi Agents
var output = await result.GetValueAsync(TimeSpan.FromSeconds(10 * 3));
// Print the Results
Console.WriteLine($"{Environment.NewLine}# RESULT: {output}");
Console.WriteLine($"{Environment.NewLine}#ORCHESTRATION HISTORY:{Environment.NewLine}");
foreach (var message in history)
{
Console.WriteLine($"#{message.Role} - {message.AuthorName}:");
Console.WriteLine($"{message.Content}{Environment.NewLine}");
}
}
catch (HttpOperationException ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
finally
{
await runtime.RunUntilIdleAsync();
Console.ResetColor();
Console.WriteLine();
}

上面的代码示例中主动输出了编排过程中每个Agent的生成结果历史记录,便于我们一会儿查看。

效果展示

用户输入问题:"Create a slogan for a new electric SUV that is affordable and fun to drive."

假设客户公司有一个新产品:一辆新的电动SUV汽车,它性价比高,且驾驶乐趣足。

最终经过2个Agent的多轮对话,结果显示如下:

可以看到:

第一轮对话:Agent1-文案写手根据用户需求写了一个初稿,Agent2-审核员对这个初稿进行了初步审核。

第二轮对话:Agent1-文案写手并没有着急修改而是让审核员确认是否需要进一步完善,Agent2-审核员则在这次对话给出了一些修改建议。

第三轮对话:Agent1-文案写手根据修改建议给出了第二版,这次Agent2-审核员确认OK也没有再给出其他建议,群聊对话也就此为止结束了。

自定义群管理员

除了预定好的群管理员之外,我们还可以通过继承 GroupChatManager 来创建自定义的群管理员类,如下所示:

using Microsoft.SemanticKernel.Agents.Orchestration.GroupChat;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Threading;
using System.Threading.Tasks; public class CustomGroupChatManager : GroupChatManager
{
public override ValueTask<GroupChatManagerResult<string>> FilterResults(ChatHistory history, CancellationToken cancellationToken = default)
{
// Custom logic to filter or summarize chat results
return ValueTask.FromResult(new GroupChatManagerResult<string>("Summary") { Reason = "Custom summary logic." });
}
public override ValueTask<GroupChatManagerResult<string>> SelectNextAgent(ChatHistory history, GroupChatTeam team, CancellationToken cancellationToken = default)
{
// Randomly select an agent from the team
var random = new Random();
int index = random.Next(team.Members.Count);
string nextAgent = team.Members[index].Id;
return ValueTask.FromResult(new GroupChatManagerResult<string>(nextAgent) { Reason = "Custom selection logic." });
}
public override ValueTask<GroupChatManagerResult<bool>> ShouldRequestUserInput(ChatHistory history, CancellationToken cancellationToken = default)
{
// Custom logic to decide if user input is needed
return ValueTask.FromResult(new GroupChatManagerResult<bool>(false) { Reason = "No user input required." });
}
public override ValueTask<GroupChatManagerResult<bool>> ShouldTerminate(ChatHistory history, CancellationToken cancellationToken = default)
{
// Optionally call the base implementation to check for default termination logic
var baseResult = base.ShouldTerminate(history, cancellationToken).Result;
if (baseResult.Value)
{
// If the base logic says to terminate, respect it
return ValueTask.FromResult(baseResult);
}
// Custom logic to determine if the chat should terminate
bool shouldEnd = history.Count > 10; // Example: end after 10 messages
return ValueTask.FromResult(new GroupChatManagerResult<bool>(shouldEnd) { Reason = "Custom termination logic." });
}
}

定义好之后,只需要在使用群聊编排模式的时候使用这个自定义管理器即可,如下所示:

GroupChatOrchestration orchestration
= new (new CustomGroupChatManager { MaximumInvocationCount = 5 }, ...);

AgentChat

对于多Agent群聊来说,Semantic Kernel还提供了AgentChat的具体实现AgentGroupChat,它使用基于策略的方法来管理聊天的动态,具体实现上就是自己定义两个Strategy:SelectionStrategy 和 TerminationStrategy。

// 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);
}
}
}
};

更多详细内容,请阅读《多Agent协作入门:AgentGroupChat

小结

本文介绍了群聊编排模式的基本概念,然后通过一个案例介绍了如何实现一个群聊编排模式,相信通过这个案例你能够有个感性的认识。

下一篇,我们将学习移交(Handoff)编排模式,它特别适合于动态工作流、专家交接方案等应用场景。

参考资料

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

推荐学习

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

作者:爱迪生

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

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

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

  1. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)

     SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...

  2. Java-->实现群聊功能(C/S模式--TCP协议)

    --> Java 对TCP协议的支持: --> java.net包中定义了两个类ServerSocket 和Socket ,分别用来实现双向连接的server 端和client 端. -- ...

  3. 一套高可用、易伸缩、高并发的IM群聊架构方案设计实践

    本文原题为“一套高可用群聊消息系统实现”,由作者“于雨氏”授权整理和发布,内容有些许改动,作者博客地址:alexstocks.github.io.应作者要求,如需转载,请联系作者获得授权. 一.引言 ...

  4. Tinychatserver: 一个简易的命令行群聊程序

    这是学习网络编程后写的一个练手的小程序,可以帮助复习socket,I/O复用,非阻塞I/O等知识点. 通过回顾写的过程中遇到的问题的形式记录程序的关键点,最后给出完整程序代码. 0. 功能 编写一个简 ...

  5. IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?

    1.前言 IM的群聊消息,究竟存1份(即扩散读方式)还是存多份(即扩散写方式)? 上一篇文章<IM群聊消息的已读回执功能该怎么实现?>是说,“很容易想到,是存一份”,被网友们骂了,大家争论 ...

  6. IM群聊消息的已读回执功能该怎么实现?

    本文引用了架构师之路公众号作者沈剑的文章,内容有改动,感谢原作者. 1.前言 我们平时在使用即时通讯应用时候,每当发出一条聊天消息,都希望对方尽快看到,并尽快回复,但对方到底有没有真的看到?我却并不知 ...

  7. spring websocket 和socketjs实现单聊群聊,广播的消息推送详解

    spring websocket 和socketjs实现单聊群聊,广播的消息推送详解 WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随 ...

  8. centos LB负载均衡集群 三种模式区别 LVS/NAT 配置 LVS/DR 配置 LVS/DR + keepalived配置 nginx ip_hash 实现长连接 LVS是四层LB 注意down掉网卡的方法 nginx效率没有LVS高 ipvsadm命令集 测试LVS方法 第三十三节课

    centos   LB负载均衡集群 三种模式区别 LVS/NAT 配置  LVS/DR 配置  LVS/DR + keepalived配置  nginx ip_hash 实现长连接  LVS是四层LB ...

  9. [Python 网络编程] TCP编程/群聊服务端 (二)

    群聊服务端 需求分析: 1. 群聊服务端需支持启动和停止(清理资源); 2. 可以接收客户端的连接; 接收客户端发来的数据 3. 可以将每条信息分发到所有客户端 1) 先搭架子: #TCP Serve ...

  10. 102.tcp实现多线程连接与群聊

    协议之间的关系 socket在哪 socket是什么 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP ...

随机推荐

  1. 关于HarmonyOS NEXT中的模块化开发

    今天不写页面和动画,斗胆给大家讲一讲软件工程. 软件工程讲究高内聚低耦合,意思就是把整个工程按照分工不同分成不同的模块,每一个模块紧密联系又互不影响.就像一座摩天大楼,它里面的电路网非常庞大和复杂,它 ...

  2. Django中图片不显示

    很多教程没教对,导致Django中的图片不能正确的显示出来,经过多次踩坑,发现如下方法可以解决该问题. 1.setting.py中添加: STATIC_URL = '/static/' STATICF ...

  3. Web安全入门学习--攻防世界web安全新手区过关心得

    这几天也是废了小小功夫,完成了这十道题目. 这十道题目说难不难,说简单对刚入门的同学来说也没有那么简单,但是做完可以对web有最初步的了解 下面开始解题 1.view_source 作为第一题,这题还 ...

  4. 第2讲、Transformer架构图详解

    1. Transformer架构运行机制 Transformer架构是一种强大的神经网络结构,主要用于自然语言处理任务.它摒弃了传统的循环神经网络(RNN)和卷积神经网络(CNN),完全基于注意力机制 ...

  5. .NET8带来的一些新特性

    最近收到任务,项目从.NET 5升级为.NET 8. 特意去了解了一下.NET8的新特性,经过验证后,分享一些这些新特性. 管他用得到用不到,先了解了再说. 一.性能提升 1.‌原生AOT深度优化 通 ...

  6. MySQL 根据时间排序失败

    问题背景:MySQL数据库中,如果使用datetime,那其实只是精确到了秒.如果基于它排序并分页查询,若同一秒的数据超过一页,则多次查询得到的结果集可能会出现不一样的灵异事件.SQL: SELECT ...

  7. Go与C/C++ 互相调用

    A. Go调用C 1.Go调用C:在go文件里调C(以下代码中除了开头的注释之外,其他注释不可删除) /* * go 和 C 互调用程序 */ package main /* int Add( int ...

  8. Krita的语言选项里没有中文

    sudo apt install krita-l10n 即可,

  9. 阿里也出手了!灵码AI IDE问世

    大家好,我是晓凡. 写在前面 各位程序员小伙伴们,是不是还在为写代码头秃?别担心,阿里云带着它的通义灵码 AI IDE 来拯救你啦! 相信不少小伙伴已经在VSCode.JetBrains IDE等主流 ...

  10. git add 报错The file will have its original line endings in your working directory

    执行如下命令,添加代码到本地仓库 git add . 出现警告信息:The file will have its original line endings in your working direc ...