AutoGen .NET 集成 Semantic Kernel 实战:两个 Agent、两个中间件与消息桥接原理
本文围绕 AutoGen .NET(AutoGen.Net)的 AutoGen.SemanticKernel 包展开,系统讲解 SemanticKernelAgent 与 SemanticKernelChatCompletionAgent 两类封装 Agent 的构造参数、行为差异,以及 SemanticKernelChatMessageContentConnector、KernelPluginMiddleware 两个中间件的桥接机制,并结合仓库中的示例与测试用例给出可直接运行的接入代码。读完本文,你可以把 Semantic Kernel 的 Kernel/ChatCompletionAgent 无缝嵌入 AutoGen 的消息体系与编排框架,甚至把 Kernel 插件函数挂载到 OpenAIChatAgent 这类非 SK 系 Agent 上。
包定位与组件总览
AutoGen.SemanticKernel 是 AutoGen.Net 与 Semantic Kernel 之间的官方集成包,对应 安装指南 中列出的“provides the integration agents over semantic kernel”的包之一。从工程配置看,该包依赖 Microsoft.SemanticKernel、Microsoft.SemanticKernel.Agents.Core 与 Microsoft.SemanticKernel.Connectors.AzureOpenAI,并引用 AutoGen.Core(见 AutoGen.SemanticKernel.csproj),因此它复用的是 AutoGen 核心消息与中间件抽象,而非 AutoGen 全家桶。
按照 AutoGen.SemanticKernel Overview,该包提供 2 个 Agent 与 2 个中间件:
| 组件 | 类型 | 作用 |
|---|---|---|
SemanticKernelAgent |
Agent | 对 Semantic Kernel Kernel 的瘦封装,仅原生支持 IMessage<ChatMessageContent>;配合 Connector 中间件后可支持 AutoGen 内置消息类型 |
SemanticKernelChatCompletionAgent |
Agent | 对 Microsoft.SemanticKernel.Agents.ChatCompletionAgent 的瘦封装 |
SemanticKernelChatMessageContentConnector |
中间件 | 在 AutoGen 内置消息类型(TextMessage/ImageMessage/MultiModalMessage)与 ChatMessageContent 之间双向转换;目前不支持 ToolCallMessage、ToolCallResultMessage 等函数调用消息 |
KernelPluginMiddleware |
中间件 | 让你在其他 AutoGen Agent(如 OpenAIChatAgent)中使用 Semantic Kernel 插件函数 |
所有类型均位于 AutoGen.SemanticKernel 命名空间,源码集中在 dotnet/src/AutoGen.SemanticKernel 目录下。
SemanticKernelAgent:基于 Kernel 的流式 Agent
SemanticKernelAgent 是文档中推荐的核心入口,其构造函数签名与参数语义如下(见 SemanticKernelAgent.cs):
public SemanticKernelAgent(
Kernel kernel,
string name,
string systemMessage = "You are a helpful AI assistant",
string? modelServiceId = null,
PromptExecutionSettings? settings = null)
kernel:承载模型连接与插件的 Semantic Kernel 实例;name:Agent 名称,同时作为回复消息的from字段;systemMessage:系统提示词,默认"You are a helpful AI assistant";modelServiceId:可选的 keyed service id,用于在 Kernel 注册了多个IChatCompletionService时按 id 选取;settings:可选的PromptExecutionSettings。
从源码结构看,它实现了 IStreamingAgent 接口,同时支持 GenerateReplyAsync 与 GenerateStreamingReplyAsync。几个关键实现细节值得注意:
- 系统消息自动补齐:
BuildChatHistory会先转换历史消息,若其中不存在 System 角色消息,则把systemMessage自动拼接到历史开头(SemanticKernelAgent.cs)。也就是说,你既可以在构造时传systemMessage,也可以在消息序列里显式携带一条 System 消息,二者取其一即可,不会重复注入。 - 执行参数默认值:若未提供
settings,BuildOption会构造OpenAIPromptExecutionSettings,默认Temperature = 0.7、MaxTokens = 1024,并透传options.StopSequence;同时强制设置ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions(SemanticKernelAgent.cs)。这意味着挂在 Kernel 上的插件函数(如 Bing 搜索插件)会被自动调用,无需额外配置。 - 消息类型约束:
ProcessMessage只接受IMessage<ChatMessageContent>,其余类型直接抛ArgumentException(SemanticKernelAgent.cs)。这是“瘦封装”的体现——要使用TextMessage等内置类型,必须注册 Connector 中间件。 - 单结果约束:无论流式还是非流式,
ResultsPerPrompt大于 1 或流式响应中出现ChoiceIndex > 0都会抛InvalidOperationException。
另外,包内还提供了一个便捷扩展方法 ToSemanticKernelAgent(KernelExtension.cs),可以在 Kernel 实例上一行完成 Agent 创建:
using AutoGen.SemanticKernel.Extension;
var skAgent = kernel.ToSemanticKernelAgent(name: "assistant");
最小可运行示例
仓库示例 Create_Semantic_Kernel_Agent.cs 展示了完整的接入流程:
using AutoGen.Core;
using AutoGen.SemanticKernel.Extension;
using Microsoft.SemanticKernel;
var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new Exception("Please set OPENAI_API_KEY environment variable.");
var modelId = "gpt-3.5-turbo";
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(modelId: modelId, apiKey: openAIKey)
.Build();
var skAgent = new SemanticKernelAgent(
kernel: kernel,
name: "assistant",
systemMessage: "You are a helpful AI assistant")
.RegisterMessageConnector() // 注册消息桥接中间件,支持 TextMessage 等 AutoGen 内置类型
.RegisterPrintMessage(); // 控制台打印消息
await skAgent.SendAsync("Hey tell me a long tedious joke");
其中 RegisterMessageConnector 是包内扩展方法(SemanticKernelAgentExtension.cs),若未传入 connector 实例则自动 new SemanticKernelChatMessageContentConnector(),最终注册为流式中间件,因此 skAgent 的类型变为 MiddlewareStreamingAgent<SemanticKernelAgent>。
结合 Kernel 插件:以 Bing 搜索为例
SemanticKernelAgent 的最大优势在于直接复用 Kernel 的插件体系。仓库示例 Use_Bing_Search_With_Semantic_Kernel_Agent.cs 演示了把 WebSearchEnginePlugin 挂进 Kernel 后,Agent 自动具备联网搜索能力:
var bingApiKey = Environment.GetEnvironmentVariable("BING_API_KEY")
?? throw new Exception("BING_API_KEY environment variable is not set");
var bingSearch = new BingConnector(bingApiKey);
var webSearchPlugin = new WebSearchEnginePlugin(bingSearch);
var kernelBuilder = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(modelId: "gpt-3.5-turbo", apiKey: openAIKey);
kernelBuilder.Plugins.AddFromObject(webSearchPlugin);
var kernel = kernelBuilder.Build();
var skAgent = new SemanticKernelAgent(kernel, "assistant", "You are a helpful AI assistant")
.RegisterMessageConnector()
.RegisterPrintMessage();
await skAgent.SendAsync("Tell me more about gpt-4-o");
这里没有手动写任何函数调用逻辑——如前所述,默认执行参数中的 ToolCallBehavior.AutoInvokeKernelFunctions 会让 SK 自动完成“模型请求插件 → 执行插件 → 回传结果”的闭环。仓库测试 SemanticKernelAgentTest.cs 中的 SemanticKernelPluginTestAsync 用天气函数验证了同样路径:向 builder.Plugins.AddFromFunctions 注册 GetWeatherAsync 后,SendAsync("What is the weather in Seattle?") 的回复断言包含 "seattle" 与 "sunny",证实插件调用确实发生。
modelServiceId 参数也有对应测试佐证:BasicConversationTestWithKeyedServiceAsync(SemanticKernelAgentTest.cs)通过 AddAzureOpenAIChatCompletion(deploymentName, endpoint, key, modelServiceId) 注册 keyed service,再用 new SemanticKernelAgent(kernel, "assistant", modelServiceId: modelServiceId) 精确选中该服务,适用于一个 Kernel 中并存多个模型服务(例如不同部署或不同供应商)的场景。
SemanticKernelChatCompletionAgent:封装 SK 官方 Agent
第二个 Agent SemanticKernelChatCompletionAgent 是对 Microsoft.SemanticKernel.Agents.ChatCompletionAgent 的封装(SemanticKernelChatCompletionAgent.cs)。与 SemanticKernelAgent 不同,它只实现了 IAgent 接口(不支持流式),且构造时不接收 name——Name 直接取自被包装 Agent 的 Name 属性,若为空则抛 ArgumentNullException(SemanticKernelChatCompletionAgent.cs)。
其回复流程是:把历史消息转成 ChatHistory,包装为 ChatHistoryAgentThread 后调用底层 InvokeAsync,再把首条结果包成 MessageEnvelope<ChatMessageContent> 返回;同样保留 ResultsPerPrompt > 1 抛异常的单结果约束(SemanticKernelChatCompletionAgent.cs)。输入消息约束与 SemanticKernelAgent 一致:仅接受 IMessage<ChatMessageContent>,其余类型抛 ArgumentException。
示例代码来自 Create_Semantic_Kernel_Chat_Agent.cs:
using AutoGen.Core;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(modelId: "gpt-3.5-turbo", apiKey: openAIKey)
.Build();
// Semantic Kernel 内置的 ChatCompletionAgent
var chatAgent = new ChatCompletionAgent()
{
Kernel = kernel,
Name = "assistant",
Description = "You are a helpful AI assistant",
};
var messageConnector = new SemanticKernelChatMessageContentConnector();
var skAgent = new SemanticKernelChatCompletionAgent(chatAgent)
.RegisterMiddleware(messageConnector) // 支持 TextMessage 等 AutoGen 内置类型
.RegisterPrintMessage();
await skAgent.SendAsync("Hey tell me a long tedious joke");
测试 SemanticKernelAgentTest.cs 中还有两个值得参考的写法:一是直接发送 ChatMessageContent 包裹消息的“零中间件”用法(BasicSkChatCompletionAgentConversationTestAsync);二是在底层 ChatCompletionAgent 的 Arguments 中显式设置 OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions } 来启用插件(SkChatCompletionAgentPluginTestAsync)——这是它区别于 SemanticKernelAgent 的一点:后者默认就开启了自动调用,前者需要你在创建 SK Agent 时自行配置。
两个 Agent 的选型可以概括为:需要流式输出、想沿用 AutoGen 的 GenerateReplyOptions 语义(温度、最大 token、停止序列),用 SemanticKernelAgent;已经持有 SK 官方 ChatCompletionAgent 实例(例如配置了 SK 侧的指令模板、运行参数),用 SemanticKernelChatCompletionAgent 包一层即可。
SemanticKernelChatMessageContentConnector:消息桥接中间件
这是文档明确点名的关键中间件。两个 SK Agent 原生只认 ChatMessageContent,而 AutoGen 生态的消息(TextMessage、ImageMessage、MultiModalMessage)需要通过它在进入 Agent 前转换、在返回后反向转换。从 SemanticKernelChatMessageContentConnector.cs 的 XML 文档注释与实现可以确认其能力边界:
- 支持的消息类型:输入侧支持
TextMessage、ImageMessage、MultiModalMessage以及已废弃的Message类型;输出侧同样映射回这三类(多条内容时合成MultiModalMessage),流式输出则统一转为TextMessageUpdate; - 不支持:
ToolCallMessage、ToolCallResultMessage等函数调用类消息——这与 Overview 文档中“Function call message type … are not supported yet”的描述一致,源码中对应分支会抛InvalidOperationException(如 SemanticKernelChatMessageContentConnector.cs 对函数调用消息的处理)。
它的角色区分逻辑比较精巧,直接决定了转换出的 SK 角色:
- 来自 Agent 自身的消息(
m.From == agent.Name):TextMessage中 System 角色转为AuthorRole.System,其余一律转为AuthorRole.Assistant;自身的MultiModalMessage则抛出“not supported if it's from self”的异常(SemanticKernelChatMessageContentConnector.cs); - 来自其他参与者的消息:System 角色转
AuthorRole.System,TextMessage转AuthorRole.User,ImageMessage按优先级取Url或BuildDataUri()构造ImageContent(SemanticKernelChatMessageContentConnector.cs),MultiModalMessage逐项展开为TextContent/ImageContent集合。
这一设计的意义在于:AutoGen 里 Role.Assistant 标记的是“某个 Agent 的历史发言”,但在 SK 的 ChatHistory 语义中需要严格区分 System/User/Assistant 三角色。Connector 按“是否本 Agent 所发”来还原正确的 SK 角色,使多 Agent 对话历史能无失真地喂给 SK。
反向转换(PostProcessMessage)同样有规则:SK 返回的 TextContent 转为 TextMessage,ImageContent 分别按 Uri 或二进制数据转为 ImageMessage;单条内容直接返回对应消息,多条则组装为 MultiModalMessage(SemanticKernelChatMessageContentConnector.cs)。流式场景下,每个 StreamingChatMessageContent 片段转为一条 TextMessageUpdate,并强制校验 ChoiceIndex == 0。
测试 SemanticKernelAgentTest.cs 的 SemanticKernelChatMessageContentConnectorTestAsync 覆盖了这条链路:对 ChatMessageContent、TextMessage、MultiModalMessage 三种输入分别断言回复为 TextMessage 且 From == "assistant",并对流式路径断言产出 TextMessageUpdate。
KernelPluginMiddleware:让非 SK Agent 使用 Kernel 插件
KernelPluginMiddleware 解决了另一个方向的集成问题:Agent 不是 SK 的,但想用 SK 的插件。Overview 文档的原话是“allows you to use semantic kernel plugins in other AutoGen agents like OpenAIChatAgent”。
其实现(KernelPluginMiddleware.cs)相当直接:
public KernelPluginMiddleware(Kernel kernel, KernelPlugin kernelPlugin)
{
_kernelPlugin = kernelPlugin;
var functionContracts = kernelPlugin.Select(k => k.Metadata.ToFunctionContract());
var functionMap = kernelPlugin.ToDictionary(kv => kv.Metadata.Name, kv => InvokeFunctionPartial(kernel, kv));
_functionCallMiddleware = new FunctionCallMiddleware(functionContracts, functionMap, Name);
}
可以看到它把 KernelPlugin 内每个 KernelFunction 的元数据通过 ToFunctionContract(KernelExtension.cs)翻译成 AutoGen 的 FunctionContract,再把“解析 JSON 参数 → 补默认值 → 校验必填参数 → 调用 KernelFunction”的回调注册进 FunctionCallMiddleware。因此它的本质是适配层:函数调用协议由 AutoGen 侧驱动,执行时再落到 Kernel 上。参数解析细节上,InvokeFunctionAsync 会把模型给出的 JSON 字符串反序列化后按 KernelParameterMetadata 声明的类型逐项转换,缺失参数走 DefaultValue,缺必填参数则抛 ArgumentException(KernelPluginMiddleware.cs)。
仓库示例 Use_Kernel_Functions_With_Other_Agent.cs 展示了典型用法——插件来自 Kernel,Agent 却是 OpenAIChatAgent:
using AutoGen.Core;
using AutoGen.OpenAI;
using AutoGen.OpenAI.Extension;
using Microsoft.SemanticKernel;
using OpenAI;
// 1. 创建 Kernel 插件(这里只是普通 Kernel,无需模型服务)
var kernel = Kernel.CreateBuilder().Build();
var getWeatherFunction = KernelFunctionFactory.CreateFromMethod(
method: (string location) => $"The weather in {location} is 75 degrees Fahrenheit.",
functionName: "GetWeather",
description: "Get the weather for a location.");
var plugin = kernel.CreatePluginFromFunctions("my_plugin", [getWeatherFunction]);
// 2. 用插件构造中间件
var kernelPluginMiddleware = new KernelPluginMiddleware(kernel, plugin);
// 3. 注册到任意 AutoGen Agent 上
var openAIAgent = new OpenAIChatAgent(
chatClient: openAIClient.GetChatClient("gpt-4o-mini"),
name: "assistant")
.RegisterMessageConnector()
.RegisterMiddleware(kernelPluginMiddleware) // 关键:插件挂载在这里
.RegisterPrintMessage();
// 4. 发送消息,第一轮返回聚合的工具调用消息
var toolAggregateMessage = await openAIAgent.SendAsync("Tell me the weather in Seattle");
// 5. 把聚合消息回灌给 Agent,生成最终自然语言回复
var finalReply = await openAIAgent.SendAsync(toolAggregateMessage);
注意示例中的两步 SendAsync:第一次请求时 Agent 返回的是聚合了 ToolCallMessage 与 ToolCallResultMessage 的消息(示例注释说明了这一点),把它再发回 Agent 才会由 LLM 生成最终答复。这反映了 AutoGen 侧函数调用消息流的通用形态,与 SemanticKernelAgent 内部由 SK 自动闭环调用插件的模式形成对照。
安装与版本
按照 AutoGen.SemanticKernel Overview 的入门指引,接入分两步:
- 先按 安装指南 配置 NuGet feed(稳定版走 NuGet 官方源,nightly 构建需添加 AutoGen 专用 feed 到
NuGet.config); - 在项目中引用包:
<ItemGroup>
<PackageReference Include="AutoGen.SemanticKernel" Version="AUTOGEN_VERSION" />
</ItemGroup>
Version 需替换为你要使用的实际版本号(文档中以 AUTOGEN_VERSION 占位)。若你只想引入 AutoGen 核心抽象并自行实现 Agent,可以只装 AutoGen.Core;而 AutoGen 全家桶包本身就依赖了 AutoGen.SemanticKernel(见 Installation.md 中的包说明),安装全家桶时无需重复添加。
小结:组件能力速查
| 场景 | 推荐组件 | 关键注意点 |
|---|---|---|
快速接入 SK Kernel,需要流式 |
SemanticKernelAgent + RegisterMessageConnector() |
默认 Temperature=0.7、MaxTokens=1024,插件自动调用已开启 |
已持有 SK ChatCompletionAgent 实例 |
SemanticKernelChatCompletionAgent |
仅 IAgent,无流式;Name 取自被包装 Agent |
使用 TextMessage/ImageMessage/MultiModalMessage |
SemanticKernelChatMessageContentConnector |
不支持函数调用类消息;自身 MultiModalMessage 不被接受 |
让 OpenAIChatAgent 等使用 Kernel 插件 |
KernelPluginMiddleware |
函数调用由 AutoGen 侧驱动,需自行回灌工具结果消息 |
| 一个 Kernel 多个模型服务 | SemanticKernelAgent 的 modelServiceId 参数 |
注册时用同 id 的 keyed service |
综合来看,AutoGen.SemanticKernel 包的设计思路是“双向桥接”:向外,把 AutoGen 的内置消息体系与中间件框架引入 SK Agent(Connector 负责消息转换,KernelPluginMiddleware 负责反向输出函数能力);向内,让 SK 的 Kernel、插件与官方 ChatCompletionAgent 都能成为 AutoGen 编排(单 Agent、多 Agent、群聊)中的一等公民。当前阶段的边界也很明确——函数调用消息暂不参与 Connector 的双向转换,SK 内部插件调用则依赖 AutoInvokeKernelFunctions 自动闭环。对于希望以 Semantic Kernel 作为模型与插件底座、同时享受 AutoGen 消息抽象与编排能力的 .NET 团队,这是当前仓库提供的标准集成路径。
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