AutoGen.NET OpenAIChatAgent 实战:创建 OpenAI 聊天 Agent 并完成简单对话与流式回复
本文基于 AutoGen 仓库中 dotnet/website/articles/OpenAIChatAgent-simple-chat.md 教程展开,讲解如何在 .NET 端使用 AutoGen.OpenAI.OpenAIChatAgent 完成一次完整的“创建 Agent → 发送消息 → 接收回复 → 流式接收回复”流程。读完后你将掌握:OpenAIChatAgent 的构造参数与默认值、消息封包(MessageEnvelope)机制、SendAsync 与 GenerateStreamingReplyAsync 两条调用链的源码级实现细节,以及配套测试用例对行为的验证方式。
1. 依赖与适用前提
简单对话示例依赖以下 NuGet 包(见 AutoGen.OpenAI.csproj):
AutoGen.OpenAI:提供OpenAIChatAgent,项目同时引用了AutoGen.Core(Agent 核心抽象与消息类型)和AutoGen.SourceGenerator(函数调用契约的源生成器,仅在涉及 function call 时发挥作用)。OpenAISDK(v2,包内以$(OpenAISDKVersion)统一版本):OpenAIChatAgent是OpenAIClient/ChatClient的薄封装,消息类型ChatMessage、ChatCompletion、StreamingChatCompletionUpdate均来自该 SDK 的OpenAI.Chat命名空间。
一个重要的适用前提写在 csproj 的包描述中:如果你的项目仍依赖旧版 Azure.AI.OpenAI v1 SDK,应改用 AutoGen.OpenAI.V1 包(见 dotnet/src/AutoGen.OpenAI.V1),本文示例使用的是新版 OpenAI SDK 的 ChatClient。
运行示例前需要设置环境变量 OPENAI_API_KEY,示例代码在缺少该变量时会直接抛出异常:
var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new Exception("Please set OPENAI_API_KEY environment variable.");
2. 引入命名空间
教程给出的第一步是导入所需命名空间(来自 OpenAICodeSnippet.cs 的 using_statement 代码段):
using AutoGen.Core;
using AutoGen.OpenAI;
using AutoGen.OpenAI.Extension;
三个命名空间分别承担:AutoGen.Core 提供 IAgent、IMessage、MessageEnvelope、TextMessage 等核心抽象;AutoGen.OpenAI 提供 OpenAIChatAgent 本体;AutoGen.OpenAI.Extension 提供 RegisterMessageConnector() 等扩展方法。
3. 创建 OpenAIChatAgent 并发起第一次对话
完整的建 Agent + 对话示例(同上文件 create_openai_chat_agent 代码段):
var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new Exception("Please set OPENAI_API_KEY environment variable.");
var modelId = "gpt-4o-mini";
var openAIClient = new OpenAIClient(openAIKey);
// 创建 OpenAI 聊天 Agent
var openAIChatAgent = new OpenAIChatAgent(
chatClient: openAIClient.GetChatClient(modelId),
name: "assistant",
systemMessage: "You are an assistant that help user to do some tasks.");
// 构造用户消息并封装为 MessageEnvelope
var helloMessage = new UserChatMessage("Hello");
var chatMessageContent = MessageEnvelope.Create(helloMessage);
// 发送消息并接收回复
var reply = await openAIChatAgent.SendAsync(chatMessageContent);
// 回复类型是 MessageEnvelope<ChatCompletion>
ChatCompletion response = reply.As<MessageEnvelope<ChatCompletion>>().Content;
3.1 构造参数与默认值
从 OpenAIChatAgent.cs 的主构造函数签名看,参数如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
chatClient |
ChatClient(OpenAI SDK v2) |
必填 | 实际调用 LLM 的客户端,由 openAIClient.GetChatClient(modelId) 绑定模型 |
name |
string |
必填 | Agent 名称,会写入回复消息封包的 from 字段 |
systemMessage |
string? |
"You are a helpful AI assistant" |
系统提示词 |
temperature |
float? |
构造选项时默认 0.7f |
采样温度,见 OpenAIChatAgent.cs 的 CreateChatCompletionOptions |
maxTokens |
int? |
构造选项时默认 1024 |
单次生成最大 token 数 |
seed |
int? |
null |
设置后可获得确定性输出 |
responseFormat |
ChatResponseFormat? |
null |
设置为 JSON 格式时可启用 json mode |
functions |
IEnumerable<ChatTool>? |
null |
预注册的 ChatTool 集合 |
另外还有一个重载构造函数,接受完整的 ChatCompletionOptions 对象(OpenAIChatAgent.cs),可以一次性传入 TopP、PresencePenalty、FrequencyPenalty、StopSequences、EndUserId 等全部完成选项,适合需要精细控制的场景。
3.2 支持的消息类型
类注释(OpenAIChatAgent.cs)明确了输入输出契约:
- 输入:
MessageEnvelope<T>且T为ChatMessage(OpenAI SDK 的聊天消息类型,如UserChatMessage、SystemChatMessage); - 输出:非流式返回
MessageEnvelope<ChatCompletion>;流式返回MessageEnvelope<StreamingChatCompletionUpdate>。
3.3 SendAsync 的底层调用链
SendAsync 并不是 OpenAIChatAgent 自己的方法,而是 AgentExtension.cs 中定义在 IAgent 上的扩展方法:
- 传入
IMessage时,SendAsync(agent, message, chatHistory)先把可选的chatHistory追加进消息列表,再把message放在末尾,最后调用agent.GenerateReplyAsync(messages); - 传入
string时,会自动包装成new TextMessage(Role.User, message)再走同一流程,这是多 Agent 对话(如agent.SendAsync(receiver, "hello"))能直接收发字符串的原因。
GenerateReplyAsync 的实现(OpenAIChatAgent.cs)分为两步:
CreateChatMessages(OpenAIChatAgent.cs):把IMessage序列转换为 SDK 的ChatMessage序列,遇到非ChatMessage类型直接抛ArgumentException;如果消息列表中没有任何SystemChatMessage,会自动把构造时传入的systemMessage拼到最前面——这就是为什么示例里只需传一条 "Hello" 用户消息,系统提示词也会生效;CreateChatCompletionsOptions(OpenAIChatAgent.cs):合并构造时的options与调用时传入的GenerateReplyOptions(含Temperature、MaxToken、StopSequence、Functions、OutputSchema,定义见 IAgent.cs),其中Temperature、MaxToken遵循“调用时参数优先于构造时参数”的覆盖规则,OutputSchema会被转换为ChatResponseFormat.CreateJsonSchemaFormat以启用结构化输出。
最终调用 chatClient.CompleteChatAsync(chatHistory, settings, cancellationToken),并把结果包装为 new MessageEnvelope<ChatCompletion>(reply.Value, from: this.Name) 返回。
4. 使用 GenerateStreamingReplyAsync 进行流式聊天
教程指出的第二个能力:OpenAIChatAgent 通过 IAgent.GenerateStreamingReplyAsync(实际签名来自 IStreamingAgent,见 IStreamingAgent.cs)支持流式回复。示例代码(create_openai_chat_agent_streaming 代码段):
var streamingReply = openAIChatAgent.GenerateStreamingReplyAsync(new[] { chatMessageContent });
await foreach (var streamingMessage in streamingReply)
{
// 每个流式片段是 MessageEnvelope<StreamingChatCompletionUpdate>
streamingMessage.As<MessageEnvelope<StreamingChatCompletionUpdate>>().Content
.Role.Should().Be(ChatMessageRole.Assistant);
}
实现细节(OpenAIChatAgent.cs)值得注意两点:
- 底层走
chatClient.CompleteChatStreamingAsync,每个StreamingChatCompletionUpdate被包装成MessageEnvelope<StreamingChatCompletionUpdate>逐个yield return; - 流式响应只支持单 choice:若某个 update 的
ContentUpdate.Count > 1,会抛出InvalidOperationException("Only one choice is supported in streaming response")。因此创建ChatClient时不要依赖多 choice 的流式返回。
5. 测试用例对行为的验证
仓库中的 OpenAIChatAgentTest.cs 用真实的(带条件跳过机制的)集成测试验证了本文描述的全部路径:
- 第 51 行
openAIChatAgent.SendAsync(chatMessageContent)验证非流式回复类型为MessageEnvelope<ChatCompletion>; - 第 59 行
openAIChatAgent.GenerateStreamingReplyAsync(new[] { chatMessageContent })验证流式回复类型为MessageEnvelope<StreamingChatCompletionUpdate>; - 第 254 行还演示了字符串重载
await openAIChatAgent.SendAsync("hello")的简化写法。
测试与 OpenAICodeSnippet.cs 使用完全一致的 API 序列,可作为“最小可运行单元”的交叉印证。
6. 小结与常见坑
- 消息类型必须匹配:
OpenAIChatAgent只接受ChatMessage家族的封包消息;如果你希望直接收发TextMessage、MultiModalMessage等 AutoGen 通用消息类型,需要在 Agent 上注册消息连接器(RegisterMessageConnector()扩展,见 OpenAIAgentExtension.cs),这属于仓库中另一篇进阶文档的主题,本文不展开。 - 默认值要心里有数:不传
temperature时实际生效的是0.7,maxTokens是1024(由CreateChatCompletionOptions静态方法注入),而构造函数参数本身默认是null;两者差异在 OpenAIChatAgent.cs 与 OpenAIChatAgent.cs 中可以对照阅读。 - 系统消息自动注入:只有当消息序列中不含
SystemChatMessage时才会自动拼接systemMessage,如果你想在多轮对话中临时替换系统提示词,直接在历史消息里放一条SystemChatMessage即可跳过自动注入逻辑。 - 多 Agent 场景:基于
IAgent的SendAsync/InitiateChatAsync扩展(AgentExtension.cs),本文创建的单个OpenAIChatAgent可以直接参与agent.SendAsync(receiver, "message")形式的双 Agent 对话,无需改动构造方式。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00