AutoGen.NET:通过 SemanticKernelChatMessageContentConnector 让 SemanticKernelAgent 支持更多内置消息类型
在 AutoGen.NET(.NET 版 AutoGen)中,SemanticKernelAgent 原生的消息通道只认一种类型:来自 Semantic Kernel 的 ChatMessageContent(即 IMessage<ChatMessageContent>)。当你的对话系统中同时存在 AutoGen 内置消息(如 TextMessage、ImageMessage、MultiModalMessage)时,如何让它们与 Semantic Kernel Agent 无缝互通?本文基于官方文档 SemanticKernelAgent-support-more-messages 并结合仓库源码,完整讲解如何通过注册 SemanticKernelChatMessageContentConnector 中间件扩展 Agent 的消息兼容性:包括支持的消息类型清单、完整可运行代码、双向转换的底层实现细节,以及当前版本尚不支持的消息类型与验证方式。
一、SemanticKernelAgent 的默认消息限制
SemanticKernelAgent 是基于 Microsoft.SemanticKernel 的 Kernel 对象构建的流式 Agent(实现 IStreamingAgent 接口)。它接收 IEnumerable<IMessage> 作为输入,内部通过 BuildChatHistory 将消息列表拼装为 Semantic Kernel 的 ChatHistory。
关键限制体现在它的消息处理逻辑中。从 SemanticKernelAgent.cs 的 ProcessMessage 方法可以看到:
private IEnumerable<ChatMessageContent> ProcessMessage(IEnumerable<IMessage> messages)
{
return messages.Select(m => m switch
{
IMessage<ChatMessageContent> cmc => cmc.Content,
_ => throw new ArgumentException("Invalid message type")
});
}
也就是说,只有 IMessage<ChatMessageContent> 能通过类型匹配,任何 AutoGen 内置消息类型(TextMessage、ImageMessage 等)传入都会直接抛出 ArgumentException: Invalid message type。Agent 类注释(SemanticKernelAgent.cs)也明确说明:入站/回复消息均为 IMessage<ChatMessageContent>,流式回复为 IMessage<StreamingChatMessageContent>;"要支持更多 AutoGen 内置 IMessage,请注册 SemanticKernelChatMessageContentConnector"。
默认用法下,你需要用 MessageEnvelope.Create 把 ChatMessageContent 包装成 IMessage<ChatMessageContent> 再发送,回复同样以 MessageEnvelope<ChatMessageContent> 形式返回。这是 AutoGen 与 Semantic Kernel 之间的"窄通道"。
二、SemanticKernelChatMessageContentConnector:双向消息转换器
SemanticKernelChatMessageContentConnector 是解决上述限制的核心组件,位于 Middleware/SemanticKernelChatMessageContentConnector.cs。它的职责是双向转换:
- 入站(Inbound):把调用方发来的 AutoGen 内置消息转换为
ChatMessageContent,再包上MessageEnvelope<ChatMessageContent>传给底层 Agent; - 出站(Outbound):把 Agent 返回的
ChatMessageContent(流式场景为StreamingChatMessageContent)转换回 AutoGen 内置消息类型返回给调用方。
从类型声明看,它同时实现了 IMiddleware 与 IStreamingMiddleware 两个接口(SemanticKernelChatMessageContentConnector.cs),因此同步 SendAsync 与流式 GenerateStreamingReplyAsync 两条链路都能走转换逻辑——这是它与普通单一中间件的重要区别。
支持的消息类型清单
根据文档与中间件源码,当前阶段转换器的支持范围如下:
| 方向 | 支持的消息类型 | 说明 |
|---|---|---|
| 入站 | TextMessage |
按 Role 映射为 System/User/Assistant |
| 入站 | ImageMessage |
需带 URL 或可构造 Data URI 的二进制数据 |
| 入站 | MultiModalMessage |
内部元素仅支持 TextMessage 与 ImageMessage |
| 出站(非流式) | TextMessage / ImageMessage / MultiModalMessage |
单内容项返回单条消息,多内容项打包为 MultiModalMessage |
| 出站(流式) | TextMessageUpdate |
流式增量文本更新 |
| 不支持 | ToolCallMessage / ToolCallResultMessage |
函数调用类消息,当前版本尚未支持 |
此外,IMessage<ChatMessageContent> 本身会被直接透传(原样解包),因此注册连接器后,原有 Semantic Kernel 风格的用法依然兼容。
三、注册方式与完整可运行示例
注册操作通过扩展方法 RegisterMessageConnector() 完成,定义在 Extension/SemanticKernelAgentExtension.cs:
public static MiddlewareStreamingAgent<SemanticKernelAgent> RegisterMessageConnector(
this SemanticKernelAgent agent, SemanticKernelChatMessageContentConnector? connector = null)
{
if (connector == null)
{
connector = new SemanticKernelChatMessageContentConnector();
}
return agent.RegisterStreamingMiddleware(connector);
}
该扩展方法有两个重载(分别接收 SemanticKernelAgent 与 MiddlewareStreamingAgent<SemanticKernelAgent>),连接器实例可以缺省——不传时自动 new 一个默认实例;返回值是注册了流式中间件的新 Agent 实例(AutoGen 的中间件机制采用"注册即返回新 Agent"的不可变风格)。
下面是官方示例工程 SemanticKernelCodeSnippet.cs 中的完整代码(对应文档引用的 register_semantic_kernel_chat_message_content_connector 代码块):
var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new Exception("Please set OPENAI_API_KEY environment variable.");
var modelId = "gpt-3.5-turbo";
var builder = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(modelId: modelId, apiKey: openAIKey);
var kernel = builder.Build();
// create a semantic kernel agent
var semanticKernelAgent = new SemanticKernelAgent(
kernel: kernel,
name: "assistant",
systemMessage: "You are an assistant that help user to do some tasks.");
// Register the connector middleware to the kernel agent
var semanticKernelAgentWithConnector = semanticKernelAgent
.RegisterMessageConnector();
// now semanticKernelAgentWithConnector supports more message types
IMessage[] messages = [
MessageEnvelope.Create(new ChatMessageContent(AuthorRole.User, "Hello")),
new TextMessage(Role.Assistant, "Hello", from: "user"),
new MultiModalMessage(Role.Assistant,
[
new TextMessage(Role.Assistant, "Hello", from: "user"),
],
from: "user"),
];
foreach (var message in messages)
{
var reply = await semanticKernelAgentWithConnector.SendAsync(message);
// SemanticKernelChatMessageContentConnector will convert the reply message to TextMessage
reply.Should().BeOfType<TextMessage>();
}
运行前提:需要设置 OPENAI_API_KEY 环境变量;项目需引用 AutoGen.SemanticKernel、Microsoft.SemanticKernel 等 NuGet 包(仓库 Directory.Packages.props 中当前锁定的 Semantic Kernel 稳定版为 1.45.0)。这段代码展示了三种入站消息混用:ChatMessageContent 信封消息(透传)、AutoGen TextMessage、AutoGen MultiModalMessage,且回复统一被转换器还原为 AutoGen 的 TextMessage。
对比:未注册连接器时的基线用法
同一示例文件中的 CreateSemanticKernelAgentAsync(SemanticKernelCodeSnippet.cs)演示了不注册连接器时的用法:只能发送 IMessage<ChatMessageContent>,回复需用 reply.As<MessageEnvelope<ChatMessageContent>>().Content 解包;流式则通过 GenerateStreamingReplyAsync 拿到 MessageEnvelope<StreamingChatMessageContent>。两段示例放在一起,可以直观看到连接器带来的能力差异。
四、转换逻辑源码剖析
4.1 入站转换:区分"自己发的"与"别人发的"
中间件的 ProcessMessage(SemanticKernelChatMessageContentConnector.cs)按 m.From == agent.Name 把消息分为两类分别处理,这决定了 AutoGen 的 Role 到 Semantic Kernel AuthorRole 的映射规则:
- 来自 Agent 自身(
ProcessMessageForSelf,即消息在对话历史中是 Agent 自己之前的回复):Role.System→AuthorRole.System,其余一律 →AuthorRole.Assistant; - 来自其他参与者(
ProcessMessageForOthers,即用户或第三方 Agent):Role.System→AuthorRole.System,其余一律 →AuthorRole.User; ImageMessage(他人):优先使用message.Url构造ImageContent(new Uri(...));若无 URL 但持有二进制Data,则调用BuildDataUri()生成 base64 Data URI 再包装为ImageContent;两者皆无时抛出InvalidOperationException: ImageMessage must have Url or DataUri。ImageMessage的 Data URI 构造与 MIME 类型推断逻辑见 ImageMessage.cs(支持 png/jpg/jpeg/gif/bmp/webp/svg 等扩展名自动推断);MultiModalMessage(他人):遍历内部Content,逐项转换为TextContent或ImageContent后装入ChatMessageContentItemCollection,打包为单条AuthorRole.User消息;MultiModalMessage(自己):明确抛出InvalidOperationException("MultiModalMessage is not supported in the semantic kernel if it's from self.")——即 Agent 自己历史中的多模态消息暂不支持回放;- 旧版
Message类型(已标记[Obsolete])仍有兼容分支,但其中携带函数调用字段(FunctionName/FunctionArguments)的消息会抛出 "Function call is not supported" 异常。
不支持的类型一律抛 InvalidOperationException("unsupported message type, only support TextMessage, ImageMessage, MultiModalMessage and Message."),错误信息直接给出了支持清单,排错成本很低。
4.2 出站转换:按内容项数量选择消息类型
回复方向由 PostProcessMessage(IMessage<ChatMessageContent>)(SemanticKernelChatMessageContentConnector.cs)完成:
TextContent→TextMessage(Role.Assistant, ...);ImageContent(带Uri或ReadOnlyMemory<byte>二进制)→ImageMessage;- 转换后的内容项若只有 1 个,直接返回该单条消息;否则打包为
MultiModalMessage(Role.Assistant, items, ...)——这解释了为何多模态回复会自动升级类型; - 遇到其他
KernelContent子类型抛Unsupported content type。
流式回复走 PostProcessMessage(IMessage<StreamingChatMessageContent>):校验 ChoiceIndex 必须为 0(多 choice 抛异常),然后把每个增量块包装为 TextMessageUpdate(Role.Assistant, content, from)。TextMessageUpdate 的定义见 TextMessage.cs,它与 TextMessage 的区别在于 Content 可为空且用于增量累积。
4.3 为什么这样设计
AutoGen 的消息模型以 IMessage 接口为统一抽象,各后端(OpenAI、Ollama、Gemini、Semantic Kernel 等)有自己的原生消息类型。连接器中间件本质上是"适配器":让上层编排代码(如 GroupChat)只操作 AutoGen 原生消息,而由中间件屏蔽后端的类型差异。这一"注册中间件即扩展能力"的模式在 AutoGen.NET 的中间件体系中是通用设计,SemanticKernelChatMessageContentConnector 只是 Semantic Kernel 后端的具体实现。
五、测试用例中的行为验证
SemanticKernelAgentTest.cs 提供了对上述能力的端到端验证(基于 Azure OpenAI,需 AZURE_OPENAI_API_KEY、AZURE_OPENAI_ENDPOINT、AZURE_OPENAI_DEPLOY_NAME 环境变量):
SemanticKernelChatMessageContentConnectorTestAsync(测试代码):注册连接器后,对ChatMessageContent信封、TextMessage、MultiModalMessage三种入站消息逐一SendAsync,断言回复均为TextMessage且From == "assistant";随后用同样的消息列表验证流式路径,断言每个增量块都是TextMessageUpdate。这与本文第三节的示例完全对应;SemanticKernelPluginTestAsync还验证了注册连接器后 Kernel 插件(函数)仍可正常工作:向 Kernel 注入GetWeatherAsync插件函数并发送"What is the weather in Seattle?",断言回复包含 "seattle" 与 "sunny"——说明连接器只转换消息通道,不影响 Kernel 自身通过ToolCallBehavior.AutoInvokeKernelFunctions(见 SemanticKernelAgent.cs 的默认设置)完成的函数调用闭环;SkChatCompletionAgentChatMessageContentConnectorTestAsync则展示了同一连接器也可用于SemanticKernelChatCompletionAgent(包装 Semantic KernelChatCompletionAgent的 另一种封装),通过.RegisterMiddleware(new SemanticKernelChatMessageContentConnector())注册,行为一致。
需要说明:这些测试标注了 [ApiKeyFact(...)],属于依赖密钥的集成测试;仓库只读环境下你主要参考其断言逻辑即可。
六、限制与注意事项
- 函数调用消息暂不支持:
ToolCallMessage与ToolCallResultMessage无法通过该连接器传递,携带函数调用信息的旧式Message也会抛出 "Function call is not supported" 异常。如果你需要在 Semantic Kernel Agent 上使用 AutoGen 侧的函数调用编排,当前应从源码结构看只能依赖 Kernel 原生插件机制(ToolCallBehavior.AutoInvokeKernelFunctions),而非 AutoGen 的ToolCallMessage通道; - 多模态消息不能来自 Agent 自身:对话历史中 Agent 自己产生的
MultiModalMessage会直接抛异常,回放多轮多模态历史时需注意这一点; - 仅支持单一 choice:非流式场景
ResultsPerPrompt > 1与流式场景ChoiceIndex > 0均会抛异常,这与 Semantic Kernel 后端配置相关; - 注册返回新实例:
RegisterMessageConnector()基于 AutoGen 的中间件机制返回新的MiddlewareStreamingAgent<SemanticKernelAgent>,原 Agent 实例的消息行为不变,请在后续代码中使用返回的新实例。
七、小结
当 Semantic Kernel Agent 需要融入 AutoGen 的多 Agent 编排体系时,注册 SemanticKernelChatMessageContentConnector 是打通消息模型的唯一推荐路径:一行 RegisterMessageConnector() 即可让 TextMessage、ImageMessage、MultiModalMessage 等 AutoGen 内置消息在入站时转为 ChatMessageContent、出站时还原为 AutoGen 消息(流式场景还原为 TextMessageUpdate),且同步/流式两条链路同时生效。实现细节可在 SemanticKernelChatMessageContentConnector.cs 中逐方法核对,行为验证可参考 SemanticKernelAgentTest.cs,完整可运行示例见 SemanticKernelCodeSnippet.cs。
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