首页
/ AutoGen.NET 实战:用 OpenAIChatRequestMessageConnector 让 OpenAIChatAgent 支持 TextMessage、ImageMessage 等内置消息类型

AutoGen.NET 实战:用 OpenAIChatRequestMessageConnector 让 OpenAIChatAgent 支持 TextMessage、ImageMessage 等内置消息类型

2026-09-06 15:53:47作者:宣聪麟

本文介绍 AutoGen.NET 中 OpenAIChatAgent 默认消息类型的限制,以及如何通过 OpenAIChatRequestMessageConnector 这个消息连接器中间件,让 OpenAI Chat Agent 能够收发 AutoGen 内置消息类型(TextMessageImageMessageMultiModalMessageToolCallMessage 等),并结合源码说明其类型映射规则、strictMode 行为与流式回复处理机制,帮助你写出跨模型、跨 Agent 可复用的消息代码。

1. 默认限制:OpenAIChatAgent 只认 OpenAI 客户端的原始消息类型

在 AutoGen.NET(即 .NET 版 AutoGen,代码位于 dotnet 目录)中,OpenAIChatAgent 是一个直接对接 OpenAI 聊天客户端的 Agent。如果不做任何额外注册,它默认只支持 IMessage<T> 这一种消息形式,其中 T 是 OpenAI 客户端库(如 Azure.AI.OpenAI / OpenAI)返回的原始请求或响应消息类型。也就是说,你发给它的消息是 MessageEnvelope.Create(new UserChatMessage(...)) 这样的原始 SDK 消息,收到的回复则是 MessageEnvelope<ChatCompletion>

示例代码中对这一默认行为的注释写得很明确:

// OpenAIChatAgent supports the following message types:
// - IMessage<ChatRequestMessage> where ChatRequestMessage is from Azure.AI.OpenAI

var helloMessage = new UserChatMessage("Hello");

// Use MessageEnvelope.Create to create an IMessage<ChatRequestMessage>
var chatMessageContent = MessageEnvelope.Create(helloMessage);
var reply = await openAIChatAgent.SendAsync(chatMessageContent);

// The type of reply is MessageEnvelope<ChatCompletion> where ChatResponseMessage is from Azure.AI.OpenAI
reply.Should().BeOfType<MessageEnvelope<ChatCompletion>>();

这种设计的好处是与 OpenAI SDK 零阻抗对接,但缺点也很直接:一旦你的系统里存在其他 Agent(如基于 Semantic Kernel、Anthropic、Gemini 的 Agent)或基于 AutoGen 核心消息类型的编排逻辑,消息类型就无法互通。AutoGen 在 AutoGen.Core 中定义了一套与具体 LLM 提供商无关的内置消息类型,包括:

要让 OpenAIChatAgent 也接受并产出这些内置类型,就需要注册 OpenAIChatRequestMessageConnector

2. OpenAIChatRequestMessageConnector:双向消息转换器

OpenAIChatRequestMessageConnector源码)实现了 IMiddlewareIStreamingMiddleware 两个接口,因此它可以同时处理普通回复和流式回复。从源码结构看,它在 Agent 调用的中间件链中完成两件事:

  1. 入站转换:调用 agent.GenerateReplyAsync 之前,先通过 ProcessIncomingMessages 把 AutoGen 内置消息逐条转换为 OpenAI 的 ChatMessage 列表;
  2. 出站转换:Agent 返回 IMessage<ChatCompletion> 后,通过 PostProcessMessageChatCompletion 还原为 AutoGen 内置消息(TextMessageToolCallMessage)。

构造函数只暴露一个参数:

/// <param name="strictMode">If true, the connector will throw an InvalidOperationException
/// when the message type is not supported. If false, it will ignore the unsupported message type.</param>
public OpenAIChatRequestMessageConnector(bool strictMode = false)

strictMode 默认为 false:遇到无法识别的消息类型时直接忽略(不出现在发给模型的请求中);设为 true 时则抛出 InvalidOperationException,方便在开发阶段尽早暴露类型问题。

3. 注册连接器:一行扩展方法即可启用

官方文档给出的用法非常简洁——导入命名空间后调用 RegisterMessageConnector() 扩展方法即可。完整代码摘自 OpenAICodeSnippet.cs(对应文档中 using_statementregister_openai_chat_message_connector 两个代码块):

using AutoGen.Core;
using AutoGen.OpenAI;
using AutoGen.OpenAI.Extension;

// ... 先创建一个 OpenAIChatAgent(略,见上文 create_openai_chat_agent 代码块)...

// register message connector to support more message types
var agentWithConnector = openAIChatAgent
    .RegisterMessageConnector();

// now the agentWithConnector supports more message types
var messages = new IMessage[]
{
    MessageEnvelope.Create(new UserChatMessage("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)
{
    reply = await agentWithConnector.SendAsync(message);

    reply.Should().BeOfType<TextMessage>();
    reply.As<TextMessage>().From.Should().Be("assistant");
}

注册后的 Agent 返回类型从 MessageEnvelope<ChatCompletion> 变成了 AutoGen 的 TextMessage,且 From 字段被设置为 Agent 名(示例中断言为 "assistant")。

扩展方法定义在 OpenAIAgentExtension.cs,它有两个重载,分别接受 OpenAIChatAgentMiddlewareStreamingAgent<OpenAIChatAgent>(后者允许你在已经挂过一层中间件的 Agent 上再注册连接器)。其内部实现只是:

public static MiddlewareStreamingAgent<OpenAIChatAgent> RegisterMessageConnector(
    this OpenAIChatAgent agent, OpenAIChatRequestMessageConnector? connector = null)
{
    if (connector == null)
    {
        connector = new OpenAIChatRequestMessageConnector();
    }

    return agent.RegisterStreamingMiddleware(connector);
}

即默认创建一个 strictMode = false 的连接器,并以流式中间件形式注册。你也可以自行 new OpenAIChatRequestMessageConnector(strictMode: true) 传入,获得严格模式行为。返回类型为 MiddlewareStreamingAgent<OpenAIChatAgent>,意味着它同时保留了 SendAsync(非流式)和 GenerateStreamingReplyAsync(流式)两条链路。

4. 入站转换规则详解:哪些内置类型会被转成什么

ProcessIncomingMessagesOpenAIChatRequestMessageConnector.cs)中支持的类型及转换规则如下,这部分是从源码逐条确认的实现事实:

AutoGen 内置消息 附加条件 转换结果
IMessage<ChatMessage> 任意 原样透传(短路,不再转换)
TextMessage 任意 按 Role 与 From 映射(见下文)
ImageMessage From 为空或 From != agent.Name 单图 UserChatMessage(含图片内容项)
MultiModalMessage From 为空或 From != agent.Name 图文混合 UserChatMessage
ToolCallMessage From 为空或 From == agent.Name 携带 ToolCalls 的 AssistantChatMessage
ToolCallResultMessage 任意 每个非空结果一条 ToolChatMessage
AggregateMessage<ToolCallMessage, ToolCallResultMessage> 任意 见下文说明
其他类型 strictMode == false 忽略(不进入请求)
其他类型 strictMode == true 抛出 InvalidOperationException

几条值得注意的规则:

TextMessage 的角色映射ProcessTextMessage):

  • Role == SystemSystemChatMessage,并把 From 写入 ParticipantName
  • From == agent.Name(即消息来自 Agent 自己)→ AssistantChatMessage
  • From 为空时按 Role 决定是 UserChatMessage 还是 AssistantChatMessage
  • 其余情况一律转为 UserChatMessage,并带上 ParticipantName

图片与多模态消息的来源约束ProcessImageMessage / ProcessMultiModalMessage 中都显式检查 agent.Name == message.From,若图片消息声称来自 Agent 自己会抛出 ArgumentException("ImageMessage is not supported when message.From is same with agent")。从源码结构看,这与 OpenAI 接口的现实约束一致——模型侧不产出图片。MultiModalMessage 内部只接受 TextMessageImageMessage 两种子项,其他子类型会抛 NotImplementedException。图片内容项支持两种构造方式:仅有 Url 时创建 URL 图片项,否则用 Data 字节流加 MediaTypeCreateChatMessageImageContentItemFromImageMessage)。

工具消息的配对处理ToolCallMessage 只有在 From 为空或等于 Agent 名时才被转换(其他来源会抛 ArgumentException),说明它代表的是"本 Agent 发出的调用";ToolCallResultMessage 中每个 Result 非空的工具调用都会被转换为一条 ToolChatMessage;而 AggregateMessage<ToolCallMessage, ToolCallResultMessage>(函数调用中间件产出的"调用+结果"聚合消息)来自 Agent 自身时拆分为"Assistant 工具调用消息 + 若干工具结果消息",来自其他 Agent 时则降级为携带结果文本的 UserChatMessageProcessFunctionCallMiddlewareMessage)。

5. 出站转换:ChatCompletion 如何还原为 AutoGen 消息

回复侧由 PostProcessMessage 处理(源码):

  • 若返回内容是 IMessage<ChatCompletion>
    • FinishReason == ContentFilter → 抛出 InvalidOperationException(内容被风控过滤);
    • 返回多于一个 choice → 抛出异常;
    • ToolCalls 非空 → 生成 ToolCallMessage(保留文本内容到 Content 属性);
    • 内容为文本 → 生成 TextMessage(Role.Assistant, text, from),这正是示例中 reply.Should().BeOfType<TextMessage>() 能成立的原因;
    • 其他情况(如空响应)抛 InvalidOperationException
  • ChatCompletion 类型:strictMode == false 时原样透传,strictMode == true 时抛异常。

这一设计让上层代码拿到的是统一的 AutoGen 消息抽象:普通回答是 TextMessage,需要函数调用时是 ToolCallMessage,可以直接接入 FunctionCallMiddleware 或组聊(Group Chat)流程,而不必关心底层 OpenAI 响应结构。

6. 流式回复:文本增量与工具调用的流式聚合

由于连接器同时实现 IStreamingMiddleware,流式场景同样可用。从 流式 InvokeAsync 的实现 看,其行为是:

  1. 每个 StreamingChatCompletionUpdate 中,若增量是单个文本片段,立即以 TextMessageUpdate 形式 yield 给调用方——即流式输出对上层呈现为 AutoGen 的文本增量消息;
  2. 工具调用增量(ToolCallUpdates)不逐段直接透传,而是按 Index 缓冲拼接 FunctionNameFunctionArgumentsToolCallId,期间以 ToolCallMessageUpdate 持续上报累积状态;
  3. 流结束后,若存在工具调用,则把各工具调用合并为一条完整的 ToolCallMessageContent 为全部文本拼接结果)作为最后一个消息 yield。

这意味着即便底层是流式分片,消费端拿到的仍是完整的 AutoGen 消息语义:文本用 TextMessageUpdate 增量消费,工具调用最终收齐为一条 ToolCallMessage

7. 测试视角的验证

单元测试 OpenAIMessageTests.cs 对转换逻辑有系统性覆盖,可以用作行为对照:

  • BasicMessageTest 一次性喂入 TextMessage(System/User/Assistant 三种角色)、ImageMessage(URL 形式)、MultiModalMessage(文本+图片)、ToolCallMessageToolCallResultMessageAggregateMessage<ToolCallMessage, ToolCallResultMessage> 等全部支持类型,调用 ProcessIncomingMessages 并做审批测试(Approval Test)比对转换结果,确保转换行为稳定;
  • ItProcessUserTextMessageAsyncEchoAgent 包一层断言中间件,验证 new TextMessage(Role.User, "Hello", "user") 最终被转换为 UserChatMessageParticipantName"user"
  • ItShortcutChatRequestMessageAsync 验证 MessageEnvelope.Create(new UserChatMessage("hello")) 这类原始 SDK 消息会走短路路径,不再转换。

此外,函数调用示例展示了连接器与 FunctionCallMiddleware 的组合用法:先 .RegisterMessageConnector() 再注册函数调用中间件,随后直接 SendAsync("what is the weather in Seattle?") 即可触发工具调用并拿到文本结果——说明注册连接器后,Agent 可以直接用字符串/内置消息类型完成完整的函数调用闭环。

8. 小结

  • 默认情况下 OpenAIChatAgent 只收发 OpenAI 客户端库的原始消息类型(IMessage<T>),适合与 SDK 深度耦合的场景;
  • 需要与 AutoGen 其他组件互通时,调用 RegisterMessageConnector() 扩展方法,将 OpenAIChatRequestMessageConnector 作为流式中间件注册,即可无缝收发 TextMessageImageMessageMultiModalMessageToolCallMessageToolCallResultMessage 及聚合消息;
  • 转换遵循严格的来源(From)与角色(Role)规则,图片/多模态消息只接受非 Agent 来源,工具消息只接受 Agent 自身来源;
  • 通过 strictMode 控制未知消息类型是忽略还是抛异常;流式场景下文本增量以 TextMessageUpdate 输出,工具调用在流末聚合为一条 ToolCallMessage
  • 关键实现可参考 OpenAIChatRequestMessageConnector.csOpenAIAgentExtension.cs,行为验证可参考 OpenAIMessageTests.csOpenAICodeSnippet.cs
登录后查看全文
热门项目推荐
相关项目推荐