首页
/ AutoGen.NET 中使用 PrintMessageMiddleware 实现 Agent 消息的格式化控制台打印

AutoGen.NET 中使用 PrintMessageMiddleware 实现 Agent 消息的格式化控制台打印

2026-09-04 13:54:26作者:董宙帆

本文基于 AutoGen 的 .NET 实现,讲解内置中间件 PrintMessageMiddleware 的用法与原理。它可以将 Agent 的回复(包括流式更新)格式化为人类可读的文本并输出到控制台,是调试多 Agent 对话时最常用的观测手段。读完本文,你可以掌握:如何把打印中间件注册到任意 Agent 或流式 Agent 上、它支持哪些消息类型,以及从源码层面理解它对普通回复与流式回复分别走了哪条处理路径。

PrintMessageMiddleware 是什么

PrintMessageMiddlewareAutoGen.Core 中内置的中间件,定义在 PrintMessageMiddleware.cs。它的作用是"将 Agent 的回复漂亮地打印(pretty print)到控制台"。从源码看,该类直接实现的是 IStreamingMiddleware 接口(该接口位于 IStreamingMiddleware.cs),也就是说它同时覆盖了普通 Agent 与流式 Agent 两种场景。

根据官方文档说明(见 Print-message-middleware.md),它支持格式化输出的 IMessage 类型包括:

  • 普通消息:TextMessageMultiModalMessageToolCallMessageToolCallResultMessageMessage
  • 流式消息:TextMessageUpdate(流式文本更新)、ToolCallMessageUpdate(流式工具调用更新)

具体的格式化逻辑由扩展方法 IMessage.FormatMessage() 完成,该扩展方法定义在 MessageExtension.cs,内部按消息的具体类型(MessageTextMessageImageMessageToolCallMessage 等)分派到各自的格式化实现。

在 Agent 上注册打印中间件

注册打印中间件不需要手动 new PrintMessageMiddleware(),仓库提供了便捷扩展方法 RegisterPrintMessage,定义在 PrintMessageMiddlewareExtension.cs。仓库示例代码(PrintMessageMiddlewareCodeSnippet.cs)展示了典型用法:

var agent = new OpenAIChatAgent(gpt4o, "assistant", config.DeploymentName)
    .RegisterMessageConnector();

// 注册打印中间件,返回一个包装了中间件的 MiddlewareAgent
var agentWithPrintMessageMiddleware = agent
    .RegisterPrintMessage();

await agentWithPrintMessageMiddleware.SendAsync("write a long poem");

RegisterPrintMessage 提供了三个重载(对应源码中 L34、L47、L60 三处签名):

  1. 接收任意 TAgent : IAgent,返回 MiddlewareAgent<TAgent>
  2. 接收已经套了中间件的 MiddlewareAgent<TAgent>,可以再叠一层,返回新的 MiddlewareAgent<TAgent>
  3. 接收 MiddlewareStreamingAgent<TAgent>TAgent : IStreamingAgent),内部改调 UseStreaming(middleware),返回 MiddlewareStreamingAgent<TAgent>

三个重载的实现模式一致:new PrintMessageMiddleware() 实例化中间件 → 用 MiddlewareAgent<TAgent>(agent) 包装原 Agent → 调 Use(middleware)(流式场景为 UseStreaming)→ 返回新的中间件 Agent。注意注册不会修改原 Agent,而是返回一个新的包装实例,原 Agent 保持"纯净"可继续复用,这与 MiddlewareExtension.csRegisterMiddleware 系列方法的"返回新 Agent"设计一致。

注册后调用 SendAsync 发起对话,Agent 的回复就会被格式化并打印到控制台:

printMessage

提示:源码中还存在 RegisterPrintFormatMessageHook 系列旧 API,均被标注为 [Obsolete("This API will be removed in v0.1.0, Use RegisterPrintMessage instead.")],请直接使用 RegisterPrintMessage

源码解析:非流式与流式两条处理路径

PrintMessageMiddleware 实现了两个 InvokeAsync 方法,分别对应普通 Agent 与流式 Agent,这是理解其行为的关键。

非流式路径(普通 IAgent)

PrintMessageMiddleware.cs

public async Task<IMessage> InvokeAsync(MiddlewareContext context, IAgent agent, CancellationToken cancellationToken = default)
{
    if (agent is IStreamingAgent streamingAgent)
    {
        // 若底层 Agent 实际支持流式,转交给流式版本处理
        ...
    }
    else
    {
        var reply = await agent.GenerateReplyAsync(context.Messages, context.Options, cancellationToken);

        var formattedMessages = reply.FormatMessage();

        Console.WriteLine(formattedMessages);

        return reply;
    }
}

逻辑非常直接:先调用底层 Agent 的 GenerateReplyAsync 拿到完整回复,再调用 reply.FormatMessage() 得到人类可读的格式化文本,Console.WriteLine 输出后原样把 reply 返回给上层——中间件只"旁观"打印,不修改消息内容,因此不会改变对话语义。

一个值得注意的细节:非流式入口里先判断 agent is IStreamingAgent,如果是,就把处理委托给流式版本的 InvokeAsync。这意味着即使你按普通 Agent 方式调用,只要底层 Agent 支持流式,打印行为也会走流式路径,最终仍会打印格式化后的完整消息。

流式路径(IStreamingAgent)

流式版本是一个 IAsyncEnumerable<IMessage> 迭代器(见 PrintMessageMiddleware.cs),它逐段消费 agent.GenerateStreamingReplyAsync 产出的增量消息,按类型分三种处理:

  • TextMessageUpdate(流式文本增量)
    • 首次收到时,先打印 from: {agentName} 头(标识消息来源),随后用 Console.Write(不换行)追加文本内容,让文字"打字机式"逐字出现;内部同时把增量累积进一个 TextMessage
    • 后续增量继续 Console.Write 追加,并通过 recentTextMessage.Update(textMessageUpdate) 更新累积消息。
  • ToolCallMessageUpdate(流式工具调用增量):不打印任何内容,只把增量累积到 ToolCallMessage 中。
  • 完整 IMessage:直接记录为 recentUpdate 并透传。

流结束后统一做收尾:打印一个换行,若最终消息不是 TextMessage(例如 ToolCallMessage,因为流式期间没打印过),就补一行 FormatMessage() 的完整格式化输出;最后 yield return 最终的完整消息。这样下游拿到的始终是一个完整的 IMessage,而控制台看到的是增量式体验。

流式消息支持的实际效果

PrintMessageMiddleware 注册到实现了 IStreamingAgent 的 Agent 上,流式回复就会边生成边打印。仓库示例(PrintMessageMiddlewareCodeSnippet.cs)中,OpenAIChatAgentRegisterMessageConnector() 转换后即为流式 Agent,直接链式追加 RegisterPrintMessage()

var streamingAgent = new OpenAIChatAgent(gpt4o, "assistant")
    .RegisterMessageConnector()
    .RegisterPrintMessage();

await streamingAgent.SendAsync("write a long poem");

运行时控制台先出现 from: assistant,随后文本增量逐字刷新;对于 ToolCallMessageUpdate,流式期间控制台保持安静,流结束后会打印格式化后的完整工具调用消息。

streamingoutput

小结与使用建议

  • PrintMessageMiddlewareAutoGen.Core 内置的观测型中间件,通过扩展方法 RegisterPrintMessage 一行代码即可注册,返回的 MiddlewareAgent<TAgent> / MiddlewareStreamingAgent<TAgent> 可继续链式叠加其他中间件;
  • 它同时支持普通消息(TextMessageMultiModalMessageToolCallMessageToolCallResultMessageMessage)与流式消息(TextMessageUpdateToolCallMessageUpdate),格式化能力由 IMessage.FormatMessage() 扩展方法提供;
  • 该中间件只读不改消息,适合在调试 Agent 对话、观察多 Agent 消息流转时直接挂到任意 Agent 上使用;生产环境若不希望控制台输出,取消注册即可,不影响 Agent 本身的对话行为。

参考文件:

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384