大家好,我是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. GStreamer开发笔记(四):ubuntu搭建GStreamer基础开发环境以及基础Demo

    前言   本篇开始gstreamer的编程学习,先搭建基础的环境,跑通一个基础的Demo对GStreamer编程有个初步的了解.   Demo         GStreamer   GStreame ...

  2. 金融科技应用:基于XGBoost与SHAP的信用评分模型构建全流程解析

    引言 在传统金融体系中,信用评估高度依赖央行征信数据,但全球仍有约20亿人口处于"信用隐形"状态.随着金融科技发展,通过整合社交数据.消费行为等替代数据源构建智能信用评估系统,已成 ...

  3. 已经在为VKProxy写UI配置站点和文档了

    VKProxy 是使用c#开发的基于 Kestrel 实现 L4/L7的代理 有兴趣的同学点个赞呗 目前已经在写文档了, 文档在 https://fs7744.github.io/VKProxy.Do ...

  4. Intellij IDEA 关闭和开启自动更新提示

    Intellij IDEA 几乎所有的设置操作都在 file ->settings 下,进入Settings页面,在左侧搜索框输入updates关键字,取消勾选Automatically che ...

  5. 【实战教程】雷池 WAF + 阿里云 CDN 深度联动:性能优化与安全防护双升级指南

    雷池 WAF(Web Application Firewall)是一款强大的网络安全防护产品,通过实时流量分析和精准规则拦截,有效抵御各种网络攻击.在部署雷池 WAF 的同时,结合阿里云 CDN(内容 ...

  6. tkinter使用pyinstaller 打包报错,ModuleNotFoundError: No module named ‘babel.numbers‘

    @ 目录 报错原因 解决办法 报错原因 导入的tkcalendar 包 中,模块名与原生的冲突 from tkcalendar import DateEntry 解决办法 打包时加入参数--hidde ...

  7. Go 进阶训练营 Week02: error 错误处理

    Error vs Exception Error: Go error 就是普通的一个接口,普通的值.Errors are values type error interface { Error() s ...

  8. 企业如何通过数据资产化,激活“数据要素x”,乘出新质生产力

    放眼全球,数据作为一种新兴生产要素,在全球经贸活动中扮演着至关重要的角色,驱动着数字经济的蓬勃兴起.据前瞻预测,至2025年,全球数据流动对整体经济增长的贡献预估将达到惊人的11万亿美元. 近几年国家 ...

  9. unity shader 消融效果

    消融效果 基础消融效果原理 多方向和可控消融

  10. C# 注释 各个关键字段 使用说明

    https://www.cnblogs.com/xdot/p/6632313.html#:~:text=%E5%9C%A8C%23%E6%99%BA%E8%83%BD%E6%B3%A8%E9%87%8 ...