AutoGen.SemanticKernel 实战:用 SemanticKernelChatCompletionAgent 将 Semantic Kernel 对话代理接入 AutoGen 消息体系
本文以 AutoGen .NET 的 AutoGen.SemanticKernel 组件为主线,完整讲解如何用五步创建一个基于 Semantic Kernel ChatCompletionAgent 的 SemanticKernelChatCompletionAgent,并与其进行对话。读完本文,你将掌握:Kernel 与 ChatCompletionAgent 的构建方式、SemanticKernelChatMessageContentConnector 中间件的作用机制,以及从源码层面理解 AutoGen 内置消息类型(TextMessage、ImageMessage、MultiModalMessage)与 Semantic Kernel ChatMessageContent 之间的双向转换规则与限制边界。
一、为什么需要 SemanticKernelChatCompletionAgent
如果你已经在项目中使用了 Microsoft Semantic Kernel,并希望把它现有的 ChatCompletionAgent 直接纳入 AutoGen 的多代理编排(如 GroupChat、Orchestrator),AutoGen.SemanticKernel 提供了内建支持:通过 SemanticKernelChatCompletionAgent 将 Semantic Kernel 的 ChatCompletionAgent 包装为 AutoGen 的 IAgent,从而复用 AutoGen 的消息、中间件与编排能力。
但有一个关键前提需要理解:默认情况下,SemanticKernelChatCompletionAgent 仅支持原始的 ChatMessageContent 类型,即要求消息为 IMessage<ChatMessageContent>。这一点从源码可以直接确认——在 SemanticKernelChatCompletionAgent.cs 的 ProcessMessage 方法中,凡是不满足 IMessage<ChatMessageContent> 的消息都会被抛出 ArgumentException("Invalid message type"):
private IEnumerable<ChatMessageContent> ProcessMessage(IEnumerable<IMessage> messages)
{
return messages.Select(m => m switch
{
IMessage<ChatMessageContent> cmc => cmc.Content,
_ => throw new ArgumentException("Invalid message type")
});
}
因此,若要支持 AutoGen 的内置消息类型,如 TextMessage、ImageMessage、MultiModalMessage,就需要把该代理注册到 SemanticKernelChatMessageContentConnector 之下。该 Connector 是一个中间件(同时实现 IMiddleware 与 IStreamingMiddleware),负责:
- 在调用底层代理前,把 AutoGen 内置消息类型转换为
ChatMessageContent; - 在收到回复后,把
ChatMessageContent反向转换回 AutoGen 内置消息类型。
二、五步创建并对话 SemanticKernelChatCompletionAgent
完整的示例代码位于 Create_Semantic_Kernel_Chat_Agent.cs,以下按官方文档的步骤逐一拆解。
Step 1:添加 using 语句
using AutoGen.Core;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
AutoGen.Core提供IAgent、TextMessage、SendAsync/RegisterMiddleware等扩展能力;Microsoft.SemanticKernel提供Kernel构建器与ChatMessageContent;Microsoft.SemanticKernel.Agents提供 Semantic Kernel 的ChatCompletionAgent。
Step 2:创建 Kernel
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();
示例从环境变量 OPENAI_API_KEY 读取密钥,并注册 gpt-3.5-turbo 作为 OpenAI 聊天模型。这里适用前提是已安装 Semantic Kernel 的 OpenAI 连接器包(Microsoft.SemanticKernel.Connectors.OpenAI)并设置好密钥环境变量;换用其他模型时,只需替换 AddXxxChatCompletion 对应的注册方法与 modelId。
Step 3:创建 Semantic Kernel 的 ChatCompletionAgent
// The built-in ChatCompletionAgent from semantic kernel.
var chatAgent = new ChatCompletionAgent()
{
Kernel = kernel,
Name = "assistant",
Description = "You are a helpful AI assistant",
};
注意 Name 并非可选装饰:从 SemanticKernelChatCompletionAgent.cs 的构造函数可以看到,若传入的 chatCompletionAgent.Name 为 null 会直接抛出 ArgumentNullException,因为包装后 AutoGen 侧的 Name 属性直接取自它:
public SemanticKernelChatCompletionAgent(ChatCompletionAgent chatCompletionAgent)
{
this.Name = chatCompletionAgent.Name ?? throw new ArgumentNullException(nameof(chatCompletionAgent.Name));
this._chatCompletionAgent = chatCompletionAgent;
}
Step 4:创建 SemanticKernelChatCompletionAgent 并注册消息 Connector
var messageConnector = new SemanticKernelChatMessageContentConnector();
var skAgent = new SemanticKernelChatCompletionAgent(chatAgent)
.RegisterMiddleware(messageConnector) // register message connector so it support AutoGen built-in message types like TextMessage.
.RegisterPrintMessage(); // pretty print the message to the console
这一步是本文的核心:
new SemanticKernelChatCompletionAgent(chatAgent)完成包装;.RegisterMiddleware(messageConnector)注册SemanticKernelChatMessageContentConnector,使代理能够收发 AutoGen 内置消息类型(如TextMessage);.RegisterPrintMessage()是 AutoGen 的通用中间件,把消息以美观格式打印到控制台。
Step 5:与代理对话
await skAgent.SendAsync("Hey tell me a long tedious joke");
SendAsync 是 IAgent 扩展方法,会向该代理发送一条用户文本消息并等待回复。由于注册了 RegisterPrintMessage(),请求与回复都会打印到控制台。
三、源码级解析:GenerateReplyAsync 的实际调用链
包装代理的回复流程在 SemanticKernelChatCompletionAgent.cs 中一目了然:
public async Task<IMessage> GenerateReplyAsync(IEnumerable<IMessage> messages, GenerateReplyOptions? options = null,
CancellationToken cancellationToken = default)
{
var agentThread = new ChatHistoryAgentThread(BuildChatHistory(messages));
var reply = await _chatCompletionAgent
.InvokeAsync(agentThread, cancellationToken: cancellationToken)
.ToArrayAsync(cancellationToken: cancellationToken);
return reply.Length > 1
? throw new InvalidOperationException("ResultsPerPrompt greater than 1 is not supported in this semantic kernel agent")
: new MessageEnvelope<ChatMessageContent>(reply[0], from: this.Name);
}
从源码结构看,有三点值得注意:
- 历史重建方式:每次调用都会把 AutoGen 传入的
messages序列化为 Semantic Kernel 的ChatHistory,再放入ChatHistoryAgentThread中驱动InvokeAsync。也就是说,对话上下文由 AutoGen 侧的消息列表完整承载; - 只支持单结果:若 Semantic Kernel 侧配置导致单次提示返回多于一条结果(即
ResultsPerPrompt > 1),会抛出InvalidOperationException; - 回复类型固定:返回值始终是
MessageEnvelope<ChatMessageContent>,From为代理名。这也是为什么默认只能处理IMessage<ChatMessageContent>的根因——而 Connector 正是为打通这一类型边界而设计。
四、SemanticKernelChatMessageContentConnector 的转换规则详解
Connector 实现位于 SemanticKernelChatMessageContentConnector.cs。它同时实现 IMiddleware(非流式)与 IStreamingMiddleware(流式)两个接口,双向转换逻辑可以概括为:
入站方向(AutoGen 消息 → ChatMessageContent)
在 ProcessMessage 方法中,消息按来源分为两条路径(见 L111-L128):
| 消息来源 | 角色映射规则 | 说明 |
|---|---|---|
IMessage<ChatMessageContent> |
直接透传 | 已经是 SK 原生类型,无需转换 |
来自代理自身(m.From == agent.Name) |
System 保留为 System;其余映射为 Assistant |
即"自己说过话"的文本被视为助手侧历史 |
| 来自其他方 | System 保留为 System;TextMessage 映射为 User |
第三方文本一律视为用户侧输入 |
对图片类消息,ProcessMessageForOthers 要求 ImageMessage 必须携带 Url 或可构造的 DataUri,否则抛出 "ImageMessage must have Url or DataUri"(见 L181-L198)。
出站方向(ChatMessageContent → AutoGen 消息)
在 PostProcessMessage 中(见 L80-L99):
TextContent→TextMessage;ImageContent(携带Uri或ReadOnlyMemory<byte>数据)→ImageMessage;- 若回复只包含单个内容项,直接返回该消息;若包含多项,则聚合为一个
MultiModalMessage返回。
流式场景下,StreamingChatMessageContent 被转换为 TextMessageUpdate,且仅支持 ChoiceIndex == 0 的单选择响应(多 choice 会抛出异常)。
明确的限制边界(均可以直接在源码中验证):
MultiModalMessage若来自代理自身(self),会抛出InvalidOperationException,即 Semantic Kernel 侧不支持"来自自己的多模态历史";- 函数调用类消息(
FunctionName/FunctionArguments)不被支持,会抛出异常; - 以上限制在 Connector 的 XML 文档注释中也被明确列出:支持的输入/回复类型为
TextMessage、ImageMessage、MultiModalMessage(以及流式的TextMessageUpdate)。
五、运行示例与注意事项
- 示例工程位于 AutoGen.SemanticKernel.Sample.csproj,其中每个示例类(如
Create_Semantic_Kernel_Chat_Agent)都暴露了RunAsync()入口。从当前的 Program.cs 看,程序入口默认执行的是Use_Kernel_Functions_With_Other_Agent.RunAsync();如果你想单独跑本文示例,把入口改为await Create_Semantic_Kernel_Chat_Agent.RunAsync();即可(在本地环境操作,不需要修改本仓库)。 - 运行前置条件:设置
OPENAI_API_KEY环境变量;模型gpt-3.5-turbo仅为示例取值,可按需替换。 - 若只需要与 Semantic Kernel 的原生
ChatMessageContent交互、无需 AutoGen 内置消息类型,也可以不注册 Connector,直接使用IMessage<ChatMessageContent>与该代理通信。 - 相关测试与周边实现可参考:SemanticKernelAgentTest.cs、包装的另一条路线 SemanticKernelAgent.cs,以及组件概览文档 AutoGen-SemanticKernel-Overview.md。
六、小结
SemanticKernelChatCompletionAgent 让你可以用不到二十行代码,把 Semantic Kernel 中现成的 ChatCompletionAgent 接入 AutoGen 的代理体系;而 SemanticKernelChatMessageContentConnector 是打通两套消息类型的关键中间件。理解了它的角色映射规则(自身→Assistant、他方→User、System 保留)与不支持项(self 侧 MultiModalMessage、函数调用、多结果响应),你就能在实际工程中准确判断它适用于哪些场景,避免踩到类型转换的边界异常。
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