AutoGen .NET 创建助手智能体实战:AssistantAgent 与 OpenAIChatAgent 的完整使用指南
本文以 AutoGen .NET 仓库中的文档 Create-an-agent.md 为核心,讲解在 C# 中创建“助手智能体(Assistant Agent)”的两条主流路径:基于 ConversableAgentConfig 的 AssistantAgent,以及基于 OpenAI V1 SDK 的 OpenAIChatAgent。读完后你将能够独立完成:读取 API Key、配置 LLM 参数、创建助手智能体、发起对话并携带会话历史、以及为智能体注册函数调用(function calling)能力。
一、AutoGen .NET 中的“助手智能体”是什么
官方文档对 AssistantAgent 的定义是:它是 AutoGen 内置的一个 AI 助手型智能体,使用 LLM 对用户的输入生成回复;如果底层 LLM 模型支持函数调用(文档举例 gpt-3.5-turbo-0613),它也支持 function call。
从源码结构看,AutoGen .NET 中存在两代助手智能体 API:
AssistantAgent(位于 AssistantAgent.cs):它只是一个薄封装,直接继承自ConversableAgent,通过ConversableAgentConfig描述 LLM 配置(模型、温度、函数契约等);OpenAIChatAgent(位于 OpenAIChatAgent.cs):直接基于 OpenAI .NET SDK 的ChatClient构建,是后续官方示例(如 Example01_AssistantAgent.cs)采用的方式,支持更多消息类型、流式响应和细粒度参数(seed、JSON mode 等)。
两者都能完成“创建助手并回答用户”的任务,本文按官方文档顺序分别讲解。
二、使用 OpenAI 模型创建 AssistantAgent
官方文档给出的第一个代码片段(来源:CreateAnAgent.cs 中 code_snippet_1 区域)如下:
// get OpenAI Key and create config
var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new Exception("Please set OPENAI_API_KEY environment variable.");
var llmConfig = new OpenAIConfig(openAIKey, "gpt-3.5-turbo");
// create assistant agent
var assistantAgent = new AssistantAgent(
name: "assistant",
systemMessage: "You are an assistant that help user to do some tasks.",
llmConfig: new ConversableAgentConfig
{
Temperature = 0,
ConfigList = new[] { llmConfig },
});
前置条件
- 设置环境变量
OPENAI_API_KEY; - 项目引用
AutoGen(核心包)与AutoGen.OpenAI相关包,示例位于 AutoGen.Basic.Sample.csproj。
关键参数说明
OpenAIConfig(源码:OpenAIConfig.cs)只有两个构造参数:
| 参数 | 类型 | 说明 |
|---|---|---|
apiKey |
string |
OpenAI API Key,示例中从环境变量 OPENAI_API_KEY 读取 |
modelId |
string |
模型 ID,示例使用 gpt-3.5-turbo |
其内部通过 CreateChatClient() 方法用 new OpenAIClient(this.ApiKey).GetChatClient(this.ModelId) 创建聊天客户端。
ConversableAgentConfig(源码:ConversableAgentConfig.cs)包含 4 个属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Temperature |
float? |
0.7f |
采样温度,示例中显式设为 0 以获得更确定的输出 |
ConfigList |
IEnumerable<ILLMConfig>? |
无 | 模型配置列表,支持多个配置组合 |
FunctionContracts |
IEnumerable<FunctionContract>? |
无 | 供该智能体调用的函数契约列表 |
Timeout |
int? |
无 | 超时时间(秒) |
源码层面发生了什么
AssistantAgent 的构造函数(AssistantAgent.cs)将 name、systemMessage、llmConfig 等参数原样透传给基类 ConversableAgent。在 ConversableAgent 的第二个构造函数中(ConversableAgent.cs),当 llmConfig.ConfigList 非空时,会调用 CreateInnerAgentFromConfigList 根据配置列表创建真正的 LLM 智能体。
从 CreateInnerAgentFromConfigList 的源码可以看到,当前内置支持三种配置类型:
AzureOpenAIConfig→ 创建OpenAIChatAgent(Azure OpenAI 后端);OpenAIConfig→ 创建OpenAIChatAgent(OpenAI 后端);LMStudioConfig→ 创建OpenAIChatAgent(LM Studio 后端,即 OpenAI 兼容接口);
其他类型会抛出 ArgumentException。如果 ConfigList 中包含多个配置,多个内部智能体之间通过中间件串联:当前智能体生成 null 回复时,会回退(fallback)到下一个智能体继续尝试。
此外,构造函数中还有几个值得注意的默认值:systemMessage 默认为 "You are a helpful AI assistant",AssistantAgent 的 humanInputMode 默认为 HumanInputMode.NEVER(从不提示人工输入),而 ConversableAgent 基类构造函数中该参数默认是 AUTO,使用时需注意二者差异。
三、使用 Azure OpenAI 模型创建助手智能体
官方文档的第二段标题为“使用 Azure OpenAI 模型创建 AssistantAgent”,对应 CreateAnAgent.cs 中 code_snippet_2 区域,实际示例代码如下:
// get OpenAI Key and create config
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
var model = "gpt-4o-mini";
var openAIClient = new OpenAIClient(apiKey);
// create assistant agent
var assistantAgent = new OpenAIChatAgent(
name: "assistant",
systemMessage: "You are an assistant that help user to do some tasks.",
chatClient: openAIClient.GetChatClient(model))
.RegisterMessageConnector()
.RegisterPrintMessage();
该示例先创建 OpenAI SDK 的 OpenAIClient,再通过 GetChatClient(model) 获得指定模型(gpt-4o-mini)的 ChatClient,最后以 chatClient 方式构造 OpenAIChatAgent。需要说明:OpenAIClient 支持通过 OpenAIClientOptions.Endpoint 指定自定义端点,因此同样的写法也可以指向 Azure OpenAI 或其他 OpenAI 兼容服务(如 Ollama、LM Studio)。仓库中 LLMConfiguration.cs 的 GetAzureOpenAIGPT3_5_Turbo 方法展示了 Azure OpenAI 场景下的环境变量约定:AZURE_OPENAI_API_KEY、AZURE_OPENAI_ENDPOINT 与 AZURE_OPENAI_DEPLOY_NAME。
OpenAIChatAgent 的完整构造参数
OpenAIChatAgent 提供两个构造函数(源码:OpenAIChatAgent.cs),常用重载的参数如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
openAIClient |
OpenAIClient |
必填 | OpenAI SDK 客户端 |
name |
string |
必填 | 智能体名称 |
modelName |
string |
必填 | 模型名,如 gpt-4o-mini |
systemMessage |
string |
"You are a helpful AI assistant" |
系统提示词 |
temperature |
float |
0.7f |
采样温度 |
maxTokens |
int |
1024 |
最大生成 token 数 |
seed |
int? |
null |
随机种子,设置后输出更具确定性 |
responseFormat |
ChatCompletionsResponseFormat? |
null |
设为 JsonObject 可启用 JSON mode |
functions |
IEnumerable<FunctionDefinition>? |
null |
智能体可调用函数 |
另一个构造函数接收 ChatCompletionsOptions 对象,源码中校验了 options.Messages 不能包含消息("Messages should not be provided in options"),因为消息由 GenerateReplyAsync 在运行时注入。
两个关键扩展方法
示例中的链式调用 .RegisterMessageConnector() 和 .RegisterPrintMessage() 分别注册两个中间件:
RegisterMessageConnector():定义在 OpenAIAgentExtension.cs,注册OpenAIChatRequestMessageConnector流式中间件,负责把 AutoGen 消息体系与 OpenAI 的ChatRequestMessage互相转换;RegisterPrintMessage():注册 PrintMessageMiddleware,在控制台打印智能体收发的消息,便于调试观察对话过程。
四、与助手智能体对话:SendAsync 与会话历史
创建智能体后,用 SendAsync 发起对话。Example01_AssistantAgent.cs 展示了完整的对话流程:
var gpt4oMini = LLMConfiguration.GetOpenAIGPT4o_mini();
var assistantAgent = new OpenAIChatAgent(
chatClient: gpt4oMini,
name: "assistant",
systemMessage: "You convert what user said to all uppercase.")
.RegisterMessageConnector()
.RegisterPrintMessage();
// talk to the assistant agent
var reply = await assistantAgent.SendAsync("hello world");
reply.Should().BeOfType<TextMessage>();
reply.GetContent().Should().Be("HELLO WORLD");
// to carry on the conversation, pass the previous conversation history to the next call
var conversationHistory = new List<IMessage>
{
new TextMessage(Role.User, "hello world"), // first message
reply, // reply from assistant agent
};
reply = await assistantAgent.SendAsync("hello world again", conversationHistory);
reply.Should().BeOfType<TextMessage>();
reply.GetContent().Should().Be("HELLO WORLD AGAIN");
要点:
- 智能体本身无状态:第二次调用必须把第一轮的用户消息和助手回复放进
conversationHistory一并传入,模型才能“记住”上下文; - 回复类型:
SendAsync返回IMessage,助手纯文本回复是TextMessage,可通过reply.GetContent()取出内容;若模型发起函数调用,则返回ToolCallMessage(见下一节)。
五、让助手支持函数调用(Function Calling)
官方文档指出 AssistantAgent 在底层模型支持时可以进行函数调用。示例工程 CreateAnAgent.cs 后半部分给出了函数调用的完整闭环:
1. 用 [Function] 特性声明函数
/// <summary>
/// convert input to upper case
/// </summary>
/// <param name="input">input</param>
[Function]
public async Task<string> UpperCase(string input)
{
var result = input.ToUpper();
return result;
}
AutoGen 的源生成器(FunctionCallGenerator.cs)会为标记 [Function] 的方法生成函数契约 UpperCaseFunctionContract,其中包含供 LLM 理解的 FunctionDefinition(名称、描述、参数 schema,来自方法的 XML 注释)。
2. 方式一:构造时直接传入 functions,模型返回 ToolCallMessage
var assistantAgent = new OpenAIChatAgent(
chatClient: openAIClient.GetChatClient(model),
name: "assistant",
systemMessage: "You are an assistant that convert user input to upper case.",
functions: [
this.UpperCaseFunctionContract.ToChatTool(), // The FunctionDefinition object for the UpperCase function
])
.RegisterMessageConnector()
.RegisterPrintMessage();
var response = await assistantAgent.SendAsync("hello");
response.Should().BeOfType<ToolCallMessage>();
var toolCallMessage = (ToolCallMessage)response;
toolCallMessage.ToolCalls.Count.Should().Be(1);
toolCallMessage.ToolCalls.First().FunctionName.Should().Be("UpperCase");
在这种模式下,SendAsync("hello") 得到的回复是 ToolCallMessage——即模型“想调用函数”但尚未执行,由宿主程序自行取出参数并执行。
3. 方式二:用 FunctionCallMiddleware 自动执行函数
var functionCallMiddleware = new FunctionCallMiddleware(
functions: [this.UpperCaseFunctionContract],
functionMap: new Dictionary<string, Func<string, Task<string>>>()
{
{ this.UpperCaseFunctionContract.Name, this.UpperCase },
});
var assistantAgent = new OpenAIChatAgent(
name: "assistant",
systemMessage: "You are an assistant that convert user input to upper case.",
chatClient: openAIClient.GetChatClient(model))
.RegisterMessageConnector()
.RegisterStreamingMiddleware(functionCallMiddleware);
var response = await assistantAgent.SendAsync("hello");
response.Should().BeOfType<TextMessage>();
response.From.Should().Be("assistant");
var textMessage = (TextMessage)response;
textMessage.Content.Should().Be("HELLO");
这里 FunctionCallMiddleware 通过 functions 向模型声明可用函数,并通过 functionMap 把函数名映射到实际实现。当模型返回工具调用时,中间件自动执行对应函数并把结果回填给模型,最终 SendAsync 直接得到执行完函数后的 TextMessage(内容为 "HELLO")。也就是说:方式一暴露“调用意图”给调用方,方式二在智能体内部闭环完成调用。
对于 AssistantAgent 旧 API,则通过构造参数的 functionMap 与 ConversableAgentConfig.FunctionContracts 组合实现同样的能力,见 AssistantAgent.cs 的构造函数签名。
六、原理剖析:AssistantAgent 的中间件处理链
理解 ConversableAgent.GenerateReplyAsync 就能理解 AssistantAgent 每次回复的完整处理流程。源码中的注释明确写出处理顺序:
// process order: function_call -> human_input -> inner_agent -> default_reply -> self_execute
// first in, last out
具体步骤:
- 系统消息注入:如果消息列表中没有 system 消息,自动把
systemMessage作为Role.System的TextMessage插到历史最前面; - 构建基础智能体:若
llmConfig配置了ConfigList,使用第二节介绍的内部 LLM 智能体;否则回退到DefaultReplyAgent,只返回defaultReply文本(未设置时提示"Default reply is not set. Please pass a default reply to assistant agent"); - HumanInputMiddleware:注册 HumanInputMiddleware,其行为由
HumanInputMode三档控制:NEVER(AssistantAgent默认):直接透传给下一层,不提示人工输入;ALWAYS:每次都打印提示并读取控制台输入作为该轮“回复”,输入exit时返回群聊终止消息;AUTO(ConversableAgent基类默认):仅当isTermination判定为非终止消息时提示人工输入;
- FunctionCallMiddleware:最外层注册函数调用中间件,负责解析
ToolCallMessage并执行functionMap中映射的函数; - 按“先进后出”的顺序执行整条中间件链。
这套设计意味着:创建助手智能体只是“填参数”,而消息转换、人工介入、函数执行等能力都通过中间件按需叠加,这也是 AutoGen .NET 与直接用 LLM SDK 调用的主要区别。
七、实践建议与验证路径
- 新项目优先用
OpenAIChatAgent:仓库的官方示例(如Example01_AssistantAgent.cs)均采用此 API,参数更完整(seed、JSON mode、maxTokens),且天然支持流式(IStreamingAgent);AssistantAgent适合需要ConversableAgentConfig多模型 fallback 或functionMap简写风格的场景; - 无状态设计:务必自己维护
conversationHistory,不要假设智能体记住了上一轮对话; - 函数调用二选一:需要宿主程序控制执行时机时用
functions构造参数(拿ToolCallMessage),需要智能体自主闭环时用FunctionCallMiddleware。
可参考的仓库内验证路径:
| 文件 | 作用 |
|---|---|
| CreateAnAgent.cs | 本文全部代码片段的来源(code_snippet_1 至 code_snippet_5) |
| Example01_AssistantAgent.cs | 助手智能体对话与会话历史的最小可运行示例 |
| AssistantAgent.cs | AssistantAgent 实现 |
| ConversableAgent.cs | 中间件处理链与多模型 fallback 逻辑 |
| OpenAIChatAgent.cs | OpenAIChatAgent 参数与消息转换实现 |
| FunctionCallMiddleware.cs | 函数调用自动执行中间件 |
| AutoGen.csproj | AssistantAgent/ConversableAgent 所在核心包 |
运行上述示例前,请先按各代码片段注释设置 OPENAI_API_KEY(以及 Azure 场景的 AZURE_OPENAI_API_KEY / AZURE_OPENAI_ENDPOINT)环境变量,并确认所用模型版本支持函数调用(文档示例为 gpt-3.5-turbo-0613 及之后的模型)。
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