
AutoGen.NET 群聊机制详解RoundRobinGroupChat 与 GroupChat 动态群聊的原理、Graph 工作流与实战示例【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogenAutoGen.NET 中的群聊Group Chat是组织多个 Agent 在同一上下文下协作完成给定任务的基础能力核心抽象为 IGroupChat 接口及其两个主要实现按轮询顺序发言的RoundRobinGroupChat以及支持“LLM 管理员 图工作流”动态决策下一位发言者的GroupChat。本文基于仓库文档与源码完整讲解群聊的两种形态、下一位发言者的决策链路、Graph/Transition工作流机制并复现示例中“计算第 39 个斐波那契数”的四 Agent 动态群聊实战方案帮助你掌握如何在 AutoGen.NET 中搭建可控的多 Agent 协作流程。什么是群聊IGroupChat 的核心约定在 AutoGen 中群聊的本质是多个 Agent 共享同一段对话历史每轮由“调度逻辑”选出下一位发言者其回复被追加进历史直到任务完成或达到轮数上限。这一约定集中在 IGroupChat 接口中public interface IGroupChat { /// summary /// Send an introduction message to the group chat. /// /summary void SendIntroduction(IMessage message); TaskIEnumerableIMessage CallAsync(IEnumerableIMessage? conversation null, int maxRound 10, CancellationToken ct default); }从接口定义可以看出三个关键契约SendIntroduction向群聊注入初始化消息即“自我介绍”这些消息会作为对话历史的起点参与后续所有轮次CallAsync驱动群聊运行maxRound默认值为 10用于限制最大发言轮数防止群聊无限进行返回值是IEnumerableIMessage即包含初始化消息与新增消息在内的完整对话历史。GroupChat 基类 的CallAsync实现了主循环每一轮构造OrchestrationContext候选成员 对话历史调用编排器选出下一位发言者执行GenerateReplyAsync并把结果追加进历史若某条消息命中终止信号则立即返回详见后文“群聊终止控制”一节。两种群聊形态RoundRobinGroupChat 与 GroupChat官方文档 Group-chat-overview 指出AutoGen 提供两种群聊RoundRobinGroupChat轮询式群聊RoundRobinGroupChat按固定的轮询顺序依次调用各个 Agent聊天历史加上上一位 Agent 的最新回复会传给下一位 Agent。从 RoundRobinGroupChat 的源码看它只是GroupChat的一个薄封装没有传入 admin也没有 workflow因此按构造函数逻辑下文详述会落到轮询编排器/// summary /// A group chat that allows agents to talk in a round-robin manner. /// /summary public class RoundRobinGroupChat : GroupChat { public RoundRobinGroupChat( IEnumerableIAgent agents, ListIMessage? initializeMessages null) : base(agents, initializeMessages: initializeMessages) { } }轮询式群聊适合流程固定、每个 Agent 职责单一且无需根据对话内容调整发言顺序的场景例如“策划 → 写作 → 校对”的流水线。值得注意的是RoundRobinGroupChat的父类中还保留了一个已标记[Obsolete]的SequentialGroupChat它同样继承自RoundRobinGroupChat源码注释明确要求使用RoundRobinGroupChat迁移旧代码时应统一替换。GroupChat动态群聊GroupChat提供了“更动态但同样可控”的下一位发言者决策方式可以仅使用一个LLM Agent 作为 group admin由它根据对话上下文智能决定下一位发言者仅使用一张 Graph 工作流以规则驱动的方式确定流转路径两者结合先用工作流圈定候选集合再由 admin 在候选中做最终裁决。文档中的建议当GroupChat仅使用 group admin 决定下一位发言者时建议使用更强的模型如gpt-4级别作为 admin以保证决策质量。这一建议在源码层面有对应体现——admin 的发言者决策调用固定使用Temperature 0以降低随机性见下文编排器分析。下一位发言者如何被决定三个编排器GroupChat的构造函数是理解整个机制的关键。GroupChat.cs 中有两个构造重载第一个展示了 admin 与 workflow 如何组合成编排器public GroupChat( IEnumerableIAgent members, IAgent? admin null, IEnumerableIMessage? initializeMessages null, Graph? workflow null) { this.admin admin; this.agents members.ToList(); this.initializeMessages initializeMessages ?? new ListIMessage(); this.workflow workflow; if (admin is not null) { this.orchestrator new RolePlayOrchestrator(admin, workflow); } else if (workflow is not null) { this.orchestrator new WorkflowOrchestrator(workflow); } else { this.orchestrator new RoundRobinOrchestrator(); } this.Validation(); }从这段构造逻辑看编排器的选择遵循明确的优先级传入参数选用的编排器决策方式admin可选传workflowRolePlayOrchestrator工作流先筛候选LLM admin 在候选中做角色扮演式裁决仅workflowWorkflowOrchestrator完全由工作流转场规则决定都不传RoundRobinOrchestrator轮询顺序也就是说RoundRobinGroupChat实际上是“不传 admin 与 workflow 的 GroupChat”。构造函数还会执行Validation所有成员必须有名称且名称唯一若提供了 workflow则 workflow 中出现的所有 Agent 都必须是群聊成员否则抛出ArgumentException。第二个构造重载则允许完全自定义编排器传入IOrchestrator实例为需要更复杂调度策略的场景留出了扩展点。RolePlayOrchestratoradmin 决策的底层机制RolePlayOrchestrator 的GetNextSpeakerAsync揭示了 admin 决策的完整链路候选收敛若配置了 workflow先根据上一位发言者调用TransitToNextAvailableAgentsAsync得到工作流允许的候选集并与群聊成员求交集短路返回候选集为空则本轮结束返回null群聊主循环随之终止候选集只剩一个则直接返回不再消耗一次 LLM 调用角色扮演裁决候选多于一个时向 admin 发送如下系统提示You are in a role play game. Carefully read the conversation history and carry on the conversation. The available roles are: {候选者名称列表逗号分隔} Each message will start with From name:, e.g: From {第一个候选}: //your message//.响应解析admin 被要求以From 名称:的格式回答解析时执行name!.Substring(5)去掉 From 前缀再与候选名称做大小写不敏感的精确匹配若 admin 的回答不在候选列表或格式不符直接抛出ArgumentException。调用参数在 源码 中固定为var response await this.admin.GenerateReplyAsync( messages: messages, options: new GenerateReplyOptions { Temperature 0, MaxToken 128, StopSequence [:], Functions null, }, cancellationToken: cancellationToken);Temperature 0让裁决尽可能确定减少同一上下文的漂移MaxToken 128发言者名称很短限制输出长度防止冗余StopSequence [:]模型输出到冒号即停止恰好截取From 名称这一行。对话历史在送入 admin 前会经过 GroupChatExtension.ProcessConversationsForRolePlay 处理每条消息被重写为From {来源}:\n{内容}\neof_msg\nround # {序号}的形式保证 admin 能清晰分辨每条消息的归属与轮次。WorkflowOrchestrator纯规则驱动的流转当不提供 admin 时WorkflowOrchestrator 完全依赖工作流它取最后一条消息的来源作为“当前发言者”调用TransitToNextAvailableAgentsAsync计算可流转的下一跳 Agent并与群聊成员求交集。候选为空则结束群聊候选恰好一个则返回该 Agent候选多于一个时抛出异常因为纯工作流模式下无法在多个合法后继间做选择。这一约束提示仅用 workflow 时每个节点在给定消息状态下的合法后继应当是唯一的否则应配合 admin 使用。Graph 与 Transition用图描述 Agent 之间的流转Graph 是工作流的数据结构由一组Transition转场组成public class Graph { public Graph(IEnumerableTransition? transitions null) { ... } public void AddTransition(Transition transition) { ... } public IEnumerableTransition Transitions transitions; /// summary /// Get the next available agents that the messages can be transit to. /// /summary public async TaskIEnumerableIAgent TransitToNextAvailableAgentsAsync(IAgent fromAgent, IEnumerableIMessage messages, CancellationToken ct default); }TransitToNextAvailableAgentsAsync的语义是找出所有“从fromAgent出发”的转场逐一执行其CanTransitionAsync谓词把通过校验的ToAgent 收集为候选集合。Transition提供三个静态工厂方法源码// 无条件的转场只要从 from 发言过就允许流转到 to public static Transition CreateTFromAgent, TToAgent(TFromAgent from, TToAgent to) where TFromAgent : IAgent where TToAgent : IAgent { return new Transition(from, to, (fromAgent, toAgent, messages, _) Task.FromResult(true)); } // 带条件谓词的转场根据当前对话历史决定该转场是否生效 public static Transition CreateTFromAgent, TToAgent( TFromAgent from, TToAgent to, FuncTFromAgent, TToAgent, IEnumerableIMessage, Taskbool canTransitionAsync) where TFromAgent : IAgent where TToAgent : IAgent; // 带 CancellationToken 的条件转场重载签名同上附加 ct 参数canTransitionAsync默认恒为true因此Transition.Create(from, to)等价于“无条件允许流转”传入谓词后可以在流转时检查最新消息内容、轮次等状态实现“有错误才回退给 coder”这类分支逻辑。注意谓词返回false时该转场会被静默跳过如果某节点的所有出边都不满足条件工作流返回空候选群聊随之终止——这一点在设计分支时要留意。实战示例计算第 39 个斐波那契数的动态群聊仓库示例 Example07_Dynamic_GroupChat_Calculate_Fibonacci.cs 展示了完整的动态群聊由admin、coder、reviewer、runner四个 Agent 协作计算第 39 个斐波那契数期望结果63245986。官方文章 Group-chat 对同一示例有逐段拆解可作为延伸阅读。四个 Agent 的角色与职责adminOpenAIChatAgenttemperature: 0负责任务下达并在任务完成时终止对话。仅凭 admin 驱动的动态群聊中它就是唯一的调度者coder一个 dotnet 编码 Agent其系统提示词约束了代码风格单个csharp代码块、top-level statements、打印结果到控制台等reviewercode_reviewer代码审查 Agent通过一个类型安全的 Function 检查代码块是否满足四条规则runner代码执行 Agent基于 Dotnet Interactive 内核执行 coder 产出的代码并回传结果。coder 的系统提示词摘录示例文件 L58-L73展示了如何用提示词约束 Agent 输出格式var coder new OpenAIChatAgent( chatClient: client, name: coder, systemMessage: You act as dotnet coder, you write dotnet code to resolve task. Once you finish writing code, ask runner to run the code for you. Herere some rules to follow on writing dotnet code: - put code between csharp and - Avoid adding using keyword when creating disposable object. e.g var httpClient new HttpClient() - Try to use var instead of explicit type. - Try avoid using external library, use .NET Core library instead. - Use top level statement to write code. - Always print out the result to console. Dont write code that doesnt print out anything. If you need to install nuget packages, put nuget packages in the following format: nuget nuget_package_name If your code is incorrect, runner will tell you the error message. Fix the error and send the code again., temperature: 0.4f) .RegisterMessageConnector() .RegisterPrintMessage();runner 并非 LLM Agent而是DefaultReplyAgent加一段中间件从coder的最新消息中提取代码块交给内核执行并把结果包裹在[RUNNER_RESULT]标记中返回示例文件 L83-L121var runner new DefaultReplyAgent( name: runner, defaultReply: No code available.) .RegisterMiddleware(async (msgs, option, agent, _) { if (msgs.Any() || msgs.All(msg msg.From ! coder)) { return new TextMessage(Role.Assistant, No code available. Coder please write code); } else { var coderMsg msgs.Last(msg msg.From coder); if (coderMsg.ExtractCodeBlock(csharp, ) is string code) { var codeResult await kernel.RunSubmitCodeCommandAsync(code, csharp); codeResult $ [RUNNER_RESULT] {codeResult} ; return new TextMessage(Role.Assistant, codeResult) { From runner }; } else { return new TextMessage(Role.Assistant, No code available. Coder please write code); } } }) .RegisterPrintMessage();reviewer 则演示了“LLM 判断 函数校验 重试”的组合模式它注册了FunctionCallMiddleware携带ReviewCodeBlockFunctionContract当 LLM 未按约定发起工具调用时中间件会以maxRetry 3的上限反复提示其转换输出为函数参数最终将四项检查结果是否多个代码块、是否 top-level、是否 dotnet 代码、是否打印结果序列化为 JSON 并生成审查意见示例文件 L137-L227。用 Graph 定义流转规则示例的“带工作流”版本RunWorkflowAsync显式定义了四条边其中三条带条件谓词把“审查通过/驳回”“运行成功/报错”的分支逻辑编码进图里示例文件 L244-L299var admin2CoderTransition Transition.Create(admin, coder); var coder2ReviewerTransition Transition.Create(coder, reviewer); var reviewer2RunnerTransition Transition.Create( from: reviewer, to: runner, canTransitionAsync: async (from, to, messages) { var lastMessage messages.Last(); if (lastMessage is TextMessage textMessage textMessage.Content.ToLower().Contains(the code looks good, please ask runner to run the code for you.) is true) { // ask runner to run the code return true; } return false; }); var reviewer2CoderTransition Transition.Create( from: reviewer, to: coder, canTransitionAsync: async (from, to, messages) { var lastMessage messages.Last(); if (lastMessage is TextMessage textMessage textMessage.Content.ToLower().Contains(therere some comments from code reviewer, please fix these comments) is true) { // ask coder to fix the code based on reviewers comments return true; } return false; }); var runner2CoderTransition Transition.Create( from: runner, to: coder, canTransitionAsync: async (from, to, messages) { var lastMessage messages.Last(); if (lastMessage is TextMessage textMessage textMessage.Content.ToLower().Contains(error) is true) { // ask coder to fix the error return true; } return false; }); var runner2AdminTransition Transition.Create(runner, admin); var workflow new Graph( [ admin2CoderTransition, coder2ReviewerTransition, reviewer2RunnerTransition, reviewer2CoderTransition, runner2CoderTransition, runner2AdminTransition, ]);这条链路的流转语义是admin → coder → reviewer之后 reviewer 依据自己的结论文案在“回给 coder 修改”和“放行给 runner 执行”之间分支runner 再依据输出中是否含error在“回给 coder 修复”和“交还 admin”之间分支。注意谓词依赖 Agent 输出中的固定短语如the code looks good...这与 reviewer 中间件生成的文案严格对应——条件谓词与 Agent 的提示词/固定输出必须保持一致否则分支永远不会命中。创建群聊、注入介绍并驱动对话最终组装群聊并运行示例文件 L302-L329var groupChat new GroupChat( admin: admin, workflow: workflow, members: [ admin, coder, runner, reviewer, ]); admin.SendIntroduction(Welcome to my group, work together to resolve my task, groupChat); coder.SendIntroduction(I will write dotnet code to resolve task, groupChat); reviewer.SendIntroduction(I will review dotnet code, groupChat); runner.SendIntroduction(I will run dotnet code once the review is done, groupChat); var task Whats the 39th of fibonacci number?; var taskMessage new TextMessage(Role.User, task, from: admin.Name); await foreach (var message in groupChat.SendAsync([taskMessage], maxRound: 10)) { // teminate chat if message is from runner and run successfully if (message.From runner message.GetContent().Contains(the39thFibonacciNumber.ToString())) { Console.WriteLine($The 39th of fibonacci number is {the39thFibonacciNumber}); break; } }要点说明SendIntroduction是 GroupChatExtension 提供的扩展方法它把消息以Role.UserFrom agent.Name的形式追加到群聊的初始化消息列表作为对话历史的前缀——这些介绍会被ProcessConversationsForRolePlay一并格式化后送给 admin帮助其理解每个成员的角色SendAsync是IAsyncEnumerableIMessage异步流源码每产生一条新消息就yield一次因此宿主代码可以边接收边判断是否提前结束示例中以 runner 输出包含期望结果63245986作为终止条件break退出循环maxRound: 10限制了群聊最多运行 10 轮与CallAsync的默认值一致是防止 Agent 互相“空转”的兜底。示例还提供了“纯 admin 驱动”的版本RunAsyncL346-L375不传workflow仅由 admin 依据角色扮演提示选择下一位发言者。这正是文档建议采用更强模型的场景。群聊终止控制与消息管理群聊如何优雅结束由两类“控制消息”机制支撑定义在 GroupChatExtensionpublic const string TERMINATE [GROUPCHAT_TERMINATE]; public const string CLEAR_MESSAGES [GROUPCHAT_CLEAR_MESSAGES];终止IsGroupChatTerminateMessage检测消息内容是否包含[GROUPCHAT_TERMINATE]。GroupChat.CallAsync每轮发言后检查该标记命中即返回完整历史SendAsync同样在yield前检查并yield break。因此任何 Agent如示例中的 admin只要在自己的回复中输出该标记就能主动结束群聊清理历史MessageToKeep依据[GROUPCHAT_CLEAR_MESSAGES]标记从对话历史中截断旧消息——若存在多个清理标记仅保留倒数第二个标记之后的内容。这一机制让群聊在长对话中可以“阶段性遗忘”控制上下文长度注意ProcessConversationForAgent已标记为[Obsolete]但MessageToKeep与ProcessConversationsForRolePlay仍在 RolePlay 链路中实际使用。选型建议与使用限制结合文档建议与源码实现可以归纳如下选型原则流程固定→ 用RoundRobinGroupChat零 LLM 调度开销顺序可预期分支明确、可用规则表达→ 用GroupChatGraph无 admin走WorkflowOrchestrator。此时要保证同一节点在给定消息状态下至多只有一个合法后继否则运行时抛出“multiple available agents”异常需要语义级调度如“根据对话进展自由决定谁该说话”→ 传admin并遵循文档建议使用能力较强的模型Temperature 0、MaxToken 128、StopSequence [:]的参数组合意味着 admin 只需输出From 名称一行提示词设计应确保 Agent 名称简短、无歧义既要可控又要灵活→admin workflow组合工作流先收敛候选省调用、控边界admin 只在存在多个候选时介入裁决。使用限制方面所有成员 Agent 必须有非空且唯一的名称Validation强制校验workflow 中出现的所有 Agent 必须包含在members中SendAsync的maxRound与CallAsync的maxRound共同构成轮数上限admin 的裁决若返回非候选名称会直接抛异常生产环境中可考虑对 admin 模型或提示词做加固。完整的动态群聊拆解可进一步参考 Group-chat 文档 与 示例源码类型安全函数调用可查阅 Create type safe function call代码执行能力可查阅 Run dotnet code。【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogen创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考