AutoGen.NET GroupChat 动态群聊:从 Admin 智能选角到 Graph 工作流编排
2026/9/5 20:29:57 网站建设 项目流程

AutoGen.NET GroupChat 动态群聊:从 Admin 智能选角到 Graph 工作流编排

【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogen

在 AutoGen.NET 中,AutoGen.Core.GroupChat提供了让多个智能体协作解决复杂任务的群聊容器:它既可以完全依赖 admin 智能体基于对话上下文“角色扮演”式地决定下一个发言者,也可以结合AutoGen.Core.Graph工作流来约束和控制对话走向,是“动态”与“可控”兼顾的下一步发言者选择方案。读完本文,你能掌握GroupChat的三种编排模式及其源码级实现原理,并能完整复现官方“代码解释器”示例——由 admin、coder、reviewer、runner 四个智能体协作计算第 39 个 Fibonacci 数。

一、GroupChat 的三种编排模式

GroupChat的构造函数(见 GroupChat.cs)决定了群聊由哪个编排器(IOrchestrator)负责挑选下一个发言者。从源码结构看,选择逻辑非常明确:

传入参数组合使用的编排器下一步发言者如何确定
传了admin(可选同时传workflowRolePlayOrchestratoradmin 通过角色扮演提示词从候选中选择
只传workflowWorkflowOrchestrator完全由 Graph 中的转移条件决定
两者都不传RoundRobinOrchestrator按成员列表顺序轮询

对应的核心构造逻辑如下:

// dotnet/src/AutoGen.Core/GroupChat/GroupChat.cs(节选) 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(); }

构造函数同时做了三项校验(GroupChat.cs):所有智能体必须有名字、名字必须唯一、且工作流中出现的智能体必须都在群聊成员之中。

Admin 的“角色扮演”选角机制

RolePlayOrchestrator(RolePlayOrchestrator.cs)的选择流程分两步:

  1. 候选收窄:如果配置了workflow,先调用workflow.TransitToNextAvailableAgentsAsync(currentSpeaker, chatHistory)从当前发言者出发得到可达智能体列表。若只剩 1 个候选,直接返回,不调用 admin;若超过 1 个,才进入第 2 步。
  2. admin 角色扮演:把候选名字拼进如下系统提示词,并把整段对话历史改写为From {名字}: ... <eof_msg> round # {i}的形式发给 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//.

请求参数被刻意收紧为确定性输出:Temperature = 0MaxToken = 128StopSequence = [":"](RolePlayOrchestrator.cs),并要求 admin 的回复形如From coder:,编排器随后截取冒号后的名字(name.Substring(5))在候选中做大小写不敏感匹配;匹配失败会抛出带详细信息的ArgumentException

官方文档特别提示:当群聊由 admin 决定下一个发言者(没有 Graph 约束)时,建议使用能力更强的模型(如gpt-4)以获得最佳体验,因为此时选角质量完全依赖 admin 的上下文理解能力。

Graph 工作流:Transition 与转移条件

GraphTransition定义在 Graph.cs 中。Graph本质是一个转移边集合,TransitToNextAvailableAgentsAsync(fromAgent, messages)会筛出所有From == fromAgent的边,并逐条调用其CanTransitionAsync(messages)谓词,把满足条件的目标智能体作为“下一可达候选”返回(Graph.cs)。

Transition.Create提供三个重载(Graph.cs):

// 1. 无条件转移(恒为 true) Transition.Create(admin, coder); // 2. 带转移条件:根据消息历史返回 true/false Transition.Create( from: reviewer, to: runner, canTransitionAsync: async (from, to, messages) => { var lastMessage = messages.Last(); return lastMessage is TextMessage t && t.Content.ToLower().Contains("the code looks good"); }); // 3. 额外支持 CancellationToken 的重载 Transition.Create(from, to, (from, to, messages, ct) => ...);

这意味着工作流是“条件化”的:同一个 from 智能体可以连出多条边(例如 reviewer 既能转 runner 也能转 coder),由边上的谓词依据最近消息内容决定实际走哪条路。

WorkflowOrchestrator(WorkflowOrchestrator.cs)则是纯工作流模式:它取最后一条消息的发送者为当前发言者,查 Graph 得到下一可达集合;若集合为空则返回null(群聊终止),若恰好 1 个则直接返回,若出现多于 1 个候选会抛出异常——即纯工作流模式下,每个状态点必须能唯一确定下一跳。

二、群聊的运行循环与终止信号

群聊主循环在GroupChat.CallAsync(GroupChat.cs)中:每一轮先由编排器选出下一发言者,调用其GenerateReplyAsync(conversationHistory),把回复追加进历史;若回复是群聊终止消息则立即返回完整历史,否则消耗maxRound(默认 10)配额。

终止与清理信号由 GroupChatExtension.cs 定义:消息内容包含TERMINATE即判定为终止消息(IsGroupChatTerminateMessage),包含CLEAR_MESSAGES时由MessageToKeep截断历史。对外使用的流式扩展方法SendAsync会循环调用CallAsync(..., maxRound: 1),逐条yield return最新消息,直到终止消息或轮次耗尽,这让调用方可以在消费消息流的过程中随时检测任务完成并提前break(下面示例正是这么做的)。

此外还有介绍消息扩展(GroupChatExtension.cs):

// 为智能体发送自我介绍,作为群聊的初始上下文(initializeMessages) public static void SendIntroduction(this IAgent agent, string message, IGroupChat groupChat);

官方文档提示:用SendIntroduction为群聊设置初始上下文,能帮助 admin 更好地组织对话流程。

三、实战示例:四智能体“代码解释器”群聊

官方示例 Example07_Dynamic_GroupChat_Calculate_Fibonacci.cs 构建了一个计算“第 39 个 Fibonacci 数”(正确答案 63245986)的动态群聊,成员及职责如下:

  • admin:为群组创建任务,并在任务完成时终止对话;
  • coder:会写 dotnet 代码的智能体;
  • reviewer:代码审查者,检查 coder 的代码是否满足:只有一个 C# 代码块、使用顶层语句、是 dotnet 代码、把结果打印到控制台;
  • runner:代码执行者,运行 coder 写出的代码并打印结果。

对话结构可概括为:

3.1 创建动态群聊(纯 admin 驱动)

示例的RunAsync中创建群聊的核心代码(源码create_group_chat区域,Example07...cs):

var reviewer = await CreateReviewerAgentAsync(gpt4o); var coder = await CreateCoderAgentAsync(gpt4o); var runner = await CreateRunnerAgentAsync(kernel); var admin = await CreateAdminAsync(gpt4o); var groupChat = new GroupChat( admin: admin, members: [ coder, runner, reviewer, ]); 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 = "What's the 39th of fibonacci number?"; var taskMessage = new TextMessage(Role.User, task); await foreach (var message in groupChat.SendAsync([taskMessage], maxRound: 10)) { // 当 runner 的消息中包含答案时终止群聊 if (message.From == "runner" && message.GetContent().Contains(the39thFibonacciNumber.ToString())) { Console.WriteLine($"The 39th of fibonacci number is {the39thFibonacciNumber}"); break; } }

注意这里没有传workflow,因此按前文的构造逻辑,群聊完全由 admin 通过角色扮演提示词驱动——这也正是官方建议使用强模型的典型场景。

3.2 四个智能体的实现拆解

admincreate_admin区域,L123-L135):就是一个temperature: 0OpenAIChatAgent,叠加RegisterMessageConnector()(把 AutoGen 消息转换为 Chat Completions 格式)和RegisterPrintMessage()(打印消息)两个扩展。

codercreate_coder区域,L52-L80):temperature: 0.4fOpenAIChatAgent,其系统提示词把“代码规范”写成硬约束——代码放在```csharp围栏中、使用顶层语句、避免using可释放对象、结果必须打印到控制台、需要 NuGet 包时用```nuget块声明、出错后根据 runner 的报错修复代码重发:

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. Here're 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. Don't write code that doesn't print out anything. ... 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();

reviewercreate_reviewer区域,L137-L227):这是示例中最有技术含量的部分,展示了 AutoGen.NET 的类型安全函数调用机制。先用[Function]特性标注一个普通方法(配套的reviewer_function区域,L17-L50):

public struct CodeReviewResult { public bool HasMultipleCodeBlocks { get; set; } public bool IsTopLevelStatement { get; set; } public bool IsDotnetCodeBlock { get; set; } public bool IsPrintResultToConsole { get; set; } } [Function] public async Task<string> ReviewCodeBlock( bool hasMultipleCodeBlocks, bool isTopLevelStatement, bool isDotnetCodeBlock, bool isPrintResultToConsole) { var obj = new CodeReviewResult { ... }; return JsonSerializer.Serialize(obj); }

[Function]特性由 AutoGen.SourceGenerator 在编译期生成ReviewCodeBlockFunctionContract(函数契约,供模型侧使用)与ReviewCodeBlockWrapper(参数序列化/反序列化的调用包装),详见 Create-type-safe-function-call.md。reviewer 用FunctionCallMiddleware把契约挂给模型:

var functionCallMiddleware = new FunctionCallMiddleware( functions: [functions.ReviewCodeBlockFunctionContract], functionMap: new Dictionary<string, Func<string, Task<string>>>() { { nameof(functions.ReviewCodeBlock), functions.ReviewCodeBlockWrapper }, }); var reviewer = new OpenAIChatAgent(chatClient: chatClient, name: "code_reviewer", systemMessage: @"You review code block from coder") .RegisterMessageConnector() .RegisterStreamingMiddleware(functionCallMiddleware) .RegisterMiddleware(async (msgs, option, innerAgent, ct) => { // 最多重试 3 次,直到模型真正发起 ReviewCodeBlock 工具调用 var maxRetry = 3; var reply = await innerAgent.GenerateReplyAsync(msgs, option, ct); while (maxRetry-- > 0) { if (reply.GetToolCalls() is var toolCalls && toolCalls.Count == 1 && toolCalls[0].FunctionName == nameof(ReviewCodeBlock)) { var reviewResultObj = JsonSerializer.Deserialize<CodeReviewResult>(reply.GetContent()); // 逐项检查:多代码块 / 非 dotnet / 非顶层语句 / 未打印结果 // 有问题则汇总成 "There're some comments from code reviewer, please fix these comments" // 无问题则返回 "The code looks good, please ask runner to run the code for you." } else { // 模型没走函数调用:提示词引导它把内容转成函数参数,再试一次 reply = await innerAgent.SendAsync(prompt, msgs, ct); } } throw new Exception("Failed to review code block"); }) .RegisterPrintMessage();

可以看到 reviewer 通过**中间件(middleware)**在 LLM 调用外层实现了“校验—重试—给出结构化审查意见”的确定性控制:审查结论是布尔结构体而非自由文本,四条检查项分别对应不同的修复提示,这使 coder 的修复方向明确可预期。

runnercreate_runner区域,L82-L121):不是 LLM 智能体,而是DefaultReplyAgent+ 自定义中间件构成的“工具型智能体”。它只关心来自coder的最后一条消息,用ExtractCodeBlock("```csharp", "```")抽出代码,交给 AutoGen.DotnetInteractive 的 dotnet interactive kernel 执行(内置执行能力说明见 Run-dotnet-code.md):

var 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"); return new TextMessage(Role.Assistant, $""" [RUNNER_RESULT] {codeResult} """) { From = "runner" }; } ... } }) .RegisterPrintMessage();

执行结果被包上[RUNNER_RESULT]标记返回,失败时错误信息会直接成为 coder 修复代码的输入,形成 coder ↔ reviewer ↔ runner 的闭环。

3.3 进阶:用 Graph 工作流控制对话流程

同一个示例还提供了RunWorkflowAsync(L229-L330),展示如何用Graph把隐式流程变成显式状态机。转移边定义(create_workflow区域)如下:

var 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(); // reviewer 说“代码没问题”才允许进入 runner return lastMessage is TextMessage textMessage && textMessage.Content.ToLower().Contains("the code looks good, please ask runner to run the code for you."); }); var reviewer2CoderTransition = Transition.Create( from: reviewer, to: coder, canTransitionAsync: async (from, to, messages) => { var lastMessage = messages.Last(); // reviewer 有审查意见时回到 coder 修复 return lastMessage is TextMessage textMessage && textMessage.Content.ToLower().Contains("there're some comments from code reviewer, please fix these comments"); }); var runner2CoderTransition = Transition.Create( from: runner, to: coder, canTransitionAsync: async (from, to, messages) => { var lastMessage = messages.Last(); // 运行报错时回到 coder return lastMessage is TextMessage textMessage && textMessage.Content.ToLower().Contains("error"); }); var runner2AdminTransition = Transition.Create(runner, admin); var workflow = new Graph( [ admin2CoderTransition, coder2ReviewerTransition, reviewer2RunnerTransition, reviewer2CoderTransition, runner2CoderTransition, runner2AdminTransition, ]);

然后把workflowadmin同时传给GroupChatcreate_group_chat_with_workflow区域):

var groupChat = new GroupChat( admin: admin, workflow: workflow, members: [admin, coder, runner, reviewer]);

这种“admin + workflow”组合对应RolePlayOrchestrator的完整行为:优先按 Graph 收窄候选(本例中每条转移条件互斥,几乎总能唯一确定下一跳,因此很少真正调用 admin);只有当 Graph 给出多个候选时才交给 admin 仲裁。相比纯 admin 模式,这种写法把“reviewer 通过 → runner”“reviewer 打回 → coder”“运行报错 → coder”等关键流转固化成了代码,对话走向更可预测、可测试。

四、关键要点回顾

  • GroupChat构造时按admin/workflow的组合自动选择编排器:admin →RolePlayOrchestrator(角色扮演提示词,Temperature=0MaxToken=128、以:停止、要求输出From 名字:格式);仅 workflow →WorkflowOrchestrator(唯一下一跳,多候选直接报错);都不传 →RoundRobinOrchestrator(轮询)。相关实现见 GroupChat.cs、Orchestrator 目录。
  • 纯 admin 驱动时建议用强模型(官方 NOTE 建议如gpt-4);示例当前使用LLMConfiguration.GetOpenAIGPT4o_mini()
  • SendIntroduction提供群聊初始上下文;SendAsync流式返回消息,配合TERMINATE终止信号可提前结束群聊。
  • Transition.CreatecanTransitionAsync谓词让工作流“条件化”,Graph.TransitToNextAvailableAgentsAsync负责按当前发言者求下一可达集合。
  • 示例展示了完整的工程组合拳:类型安全函数调用([Function]+ Source Generator)、中间件实现的审查重试逻辑、dotnet interactive 内核执行代码片段。
  • 群聊/编排器的单元测试可参考 Orchestrator 测试 与 GroupChat 测试。

相关文档:Group-chat-overview.md、Roundrobin-chat.md、Use-graph-in-group-chat.md、Create-type-safe-function-call.md、Run-dotnet-code.md。

【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogen

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询