AutoGen 0.4 .NET HelloAgent 入门:从事件订阅、gRPC 消息契约到跨语言运行时编排
本文基于 AutoGen 仓库中 Microsoft.AutoGen.Integration.Tests.AppHosts/HelloAgentTests 目录的官方示例文档,系统讲解 AutoGen 0.4 .NET 编程模型的最小完整闭环:如何定义一个订阅主题的 Agent、编写事件处理程序、启动进程内或 gRPC 分布式运行时、发布 protobuf 消息,并通过 Aspire AppHost 将 .NET Agent 与 Python Agent 编排在同一事件总线中进行跨语言(xLang)通信验证。读完本文,你可以复刻仓库中的 Hello World 示例,并理解每个关键类、注解和命令参数在源码中的真实落点。
一、示例定位与前置条件
dotnet/test/Microsoft.AutoGen.Integration.Tests.AppHosts/HelloAgentTests/ 目录下的文档 README 将其描述为:“一个演示如何创建简单的 .NET 控制台应用,监听事件并对事件进行一系列动作编排的示例”。它同时承担两个角色:
- 教学示例:演示 AutoGen 0.4 自定义 Agent 的最小可用形态(继承基类 Agent、订阅事件、发布消息、等待关闭);
- 集成测试资源:它是 Aspire 分布式应用
XlangTests.AppHost中的一个可部署资源,被 HelloAppHostIntegrationTests.cs 用来验证 .NET 与 Python Agent 之间的消息投递。
前置条件(原文档明确列出):
- .NET 8.0 或更高版本;
- 推荐安装 GitHub CLI 用于克隆仓库。
项目文件 HelloAgentTests.csproj 印证了这些依赖:<TargetFramework>net8.0</TargetFramework>,并引用 Microsoft.Extensions.Hosting、Google.Protobuf、Grpc.Tools(PrivateAssets="All",仅用于构建期生成代码),以及四个源码工程的 ProjectReference:
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Contracts\Microsoft.AutoGen.Contracts.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Core\Microsoft.AutoGen.Core.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Agents\Microsoft.AutoGen.Agents.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Core.Grpc\Microsoft.AutoGen.Core.Grpc.csproj" />
这四个工程分别对应消息契约(Contracts)、运行时核心(Core)、Agent 抽象(Agents)与 gRPC 网关客户端(Core.Grpc),是理解本文所有 API 的来源。
二、运行示例
原文档给出的运行方式:
# Clone the repository
gh repo clone microsoft/autogen
cd dotnet/samples/Hello
dotnet run
需要注意仓库中存在两处形态几乎一致的 Hello 示例,可按需选择:
- 测试资源版:
dotnet/test/Microsoft.AutoGen.Integration.Tests.AppHosts/HelloAgentTests,其 Program.cs 在构建时自动判断运行模式; - 示例集版:dotnet/samples/Hello,包含
HelloAgent、HelloAIAgents、HelloAgentState、Hello.AppHost等子项目,其中 HelloAgent/Program.cs 额外提供了命令行参数:--host <hostAddress>:连接指定地址的 gRPC 网关(也可通过环境变量AGENT_HOST设置);--nosend:不发送初始消息,等待其他 Agent 触发(文档明确提示:使用进程内运行时该参数会导致挂起,程序会直接终止并提示)。
配置层面,appsettings.json 将日志级别设为 Warning 起步,并为 Grpc、Microsoft.Hosting.Lifetime 等命名空间单独开放 Information 级别,同时声明 Kestrel 端点默认使用 Http2 协议——这是 gRPC over HTTP/2 的前提。
三、核心概念:事件驱动的 Agent 流程
原文档给出的事件流图(Mermaid)完整描述了一次对话的生命周期:
%%{init: {'theme':'forest'}}%%
graph LR;
A[Main] --> |"PublishEventAsync(NewMessage('World'))"| B{"Handle(NewMessageReceived item, CancellationToken cancellationToken = default)"}
B --> |"PublishEventAsync(Output('***Hello, World***'))"| C[ConsoleAgent]
C --> D{"WriteConsole()"}
B --> |"PublishEventAsync(ConversationClosed('Goodbye'))"| E{"Handle(ConversationClosed item, CancellationToken cancellationToken = default)"}
B --> |"PublishEventAsync(Output('***Goodbye***'))"| C
E --> F{"Shutdown()"}
结合 HelloAgent.cs 的当前实现,这条链路的实际执行过程是:
- 主程序向主题
HelloTopic发布NewMessageReceived { Message = "Hello World!" }; HelloAgent的HandleAsync(NewMessageReceived, ...)打印消息,并继续向同一主题发布ConversationClosed(UserMessage = "Goodbye");HandleAsync(ConversationClosed, ...)打印告别语,若环境变量STAY_ALIVE_ON_GOODBYE不为true,则发布Shutdown;HandleAsync(Shutdown, ...)调用hostApplicationLifetime.StopApplication()终止宿主,Program.cs中的WaitForShutdownAsync()返回,进程退出。
STAY_ALIVE_ON_GOODBYE 是跨语言编排场景的关键开关:在 Aspire 测试中由 XLangTests.AppHost/Program.cs 统一注入为 true,保证 .NET/Python 两个 Agent 在收发完一轮消息后继续保持存活,等待测试框架采集日志。
四、编写事件处理程序:TypeSubscription 与 IHandle
原文档指出:“AutoGen 应用的核心是事件处理程序。Agent 通过 TopicSubscription 选择要监听的主题,收到事件时其事件处理程序被调用;处理程序内部可以可选地发布新事件,交由事件总线分发给其他 Agent。事件类型是声明为 gRPC ProtoBuf 消息的 schema,通过 IHandle 接口在 Agent 中注册。”
对照当前源码 HelloAgent.cs,一个完整的 Agent 定义如下:
[TypeSubscription("HelloTopic")]
public class HelloAgent(
IHostApplicationLifetime hostApplicationLifetime,
AgentId id,
IAgentRuntime runtime,
Logger<BaseAgent>? logger = null) : BaseAgent(id, runtime, "Hello Agent", logger),
IHandle<NewMessageReceived>,
IHandle<ConversationClosed>,
IHandle<Shutdown>, IHandleConsole
{
// This will capture the message sent in Program.cs
public async ValueTask HandleAsync(NewMessageReceived item, MessageContext messageContext)
{
Console.Out.WriteLine(item.Message); // Print message to console
ConversationClosed goodbye = new ConversationClosed
{
UserId = this.Id.Type,
UserMessage = "Goodbye"
};
await this.PublishMessageAsync(goodbye, new TopicId("HelloTopic"));
}
// ... ConversationClosed / Shutdown 处理程序同理
}
关键点逐一拆解:
- [TypeSubscription("HelloTopic")](第 12 行):声明该 Agent 订阅的主题标识。运行时据此把总线上该主题的消息路由到本 Agent;
- 继承 BaseAgent:
Microsoft.AutoGen.Agents命名空间下的 Agent 基类,构造参数为AgentId、IAgentRuntime(消息发布/订阅的运行时抽象)、描述与可选 Logger; - 实现 IHandle<T>:每个
IHandle<T>接口对应一个HandleAsync(T item, MessageContext messageContext)方法,即处理程序契约。本例订阅了NewMessageReceived、ConversationClosed、Shutdown三种内置消息类型; - IHandleConsole:提供控制台输出能力,是“继承与组合”思想的一部分(见下一节);
- PublishMessageAsync:在处理程序中继续向
TopicId发布新消息,形成链式编排。
说明:原文档示例片段(
ConsoleAgent、PublishEventAsync、ToCloudEvent、ISayHello等)反映的是该文档编写时的 API 形态;当前源码树中对应能力已由BaseAgent+IHandle<T>+PublishMessageAsync承载,且文档中提到的dotnet/src/Microsoft.AutoGen/Agents/AgentWorker.cs在当前工程中已不存在,等价职责由Microsoft.AutoGen.Agents工程(csproj 中可见)中的BaseAgent与运行时组件承担。阅读文档时应以 HelloAgent.cs 与 Program.cs 的当前实现为准。
五、继承与组合
原文档专节强调:“本示例也展示了 AutoGen 中的继承。HelloAgent 继承自提供 WriteConsole 方法的基类。”
在当前源码结构中可以印证这一设计:Agent 的通用能力被拆分为基类 + 接口两层——
- 基类
BaseAgent提供身份(Id)、运行时句柄(PublishMessageAsync)与生命周期; - 行为接口(
IHandle<T>、IHandleConsole)按需组合。dotnet/samples/Hello/HelloAgent/HelloAgent.cs 的变体只实现三个IHandle,而测试资源版额外加了IHandleConsole,两者共享同一套处理逻辑,说明能力扩展走“组合接口”而非“加深继承链”的路线; - 更丰富的组合示例见 HelloAgentState 与 HelloAIAgents(后者演示了接入 LLM 的 AI Agent 变体,同一事件流可驱动不同能力组合的 Agent)。
六、启动 Application Runtime:进程内与 gRPC 双模式
原文档说明:“AutoGen 提供灵活的运行时,可以以多种方式启动。Program.cs 演示了如何本地启动运行时并一次性向 Agent 发送消息。”
当前 Program.cs 的完整逻辑仅 20 余行,是理解双模式切换的最佳入口:
var appBuilder = new AgentsAppBuilder(); // Create app builder
// if we are using distributed, we need the AGENT_HOST var defined and then we will use the grpc runtime
if (Environment.GetEnvironmentVariable("AGENT_HOST") != null)
{
appBuilder.AddGrpcAgentWorker(
Environment.GetEnvironmentVariable("AGENT_HOST"))
.AddAgent<HelloAgent>("HelloAgent");
}
else
{
// Set up app builder for in-process runtime, allow message delivery to self, and add the Hello agent
appBuilder.UseInProcessRuntime(deliverToSelf: true).AddAgent<HelloAgent>("HelloAgent");
}
var app = await appBuilder.BuildAsync(); // Build the app
// Create a custom message type from proto and define message
var message = new NewMessageReceived { Message = "Hello World!" };
await app.PublishMessageAsync(message, new TopicId("HelloTopic", "HelloAgents/dotnet")).ConfigureAwait(false);
await app.WaitForShutdownAsync().ConfigureAwait(false); // Wait for shutdown from agent
三个要点:
- 构建器模式:
AgentsAppBuilder负责装配运行时(UseInProcessRuntime或AddGrpcAgentWorker)与 Agent 注册(AddAgent<T>("HelloAgent")),BuildAsync()后得到一个可StartAsync/WaitForShutdownAsync的应用对象; - 单命令双模式:未设置
AGENT_HOST时走进程内运行时(deliverToSelf: true允许 Agent 接收自己发布的消息,这正是 Hello → Goodbye → Shutdown 自驱动闭环成立的必要条件);设置了AGENT_HOST时,Agent 通过 gRPC 连接到外部 AgentHost 网关,成为分布式拓扑中的一员; - 消息源标识:本例发布消息时使用的
TopicId为("HelloTopic", "HelloAgents/dotnet")——主题键之外还带有source后缀,这正是跨语言测试断言"source": "HelloAgents/dotnet"的来源(见 HelloAppHostIntegrationTests.cs)。
原文档中给出的 App.PublishMessageAsync("HelloAgents", ..., local: true) 一站式写法属于文档时期的静态入口形态,其语义(本地启动运行时 + 首发一条消息)与上面的构建器写法一一对应。
七、定义与发送消息:protobuf 契约与 CloudEvents
原文档“Sending Messages”一节的要点:消息集合由 gRPC ProtoBuf 规范定义,经 gRPC 工具生成 C# 类;用户可通过新增 .proto 文件并在 .csproj 中引入工具来定义自己的消息类型。
原文档自带的自定义消息示例(来自 DevTeam 场景):
syntax = "proto3";
package devteam;
option csharp_namespace = "DevTeam.Shared";
message NewAsk {
string org = 1;
string repo = 2;
string ask = 3;
int64 issue_number = 4;
}
message ReadmeRequested {
string org = 1;
string repo = 2;
int64 issue_number = 3;
string ask = 4;
}
对应的工程配置(原文档给出,且与 HelloAgentTests.csproj 中实际使用的包引用一致):
<ItemGroup>
<PackageReference Include="Google.Protobuf" />
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
<Protobuf Include="..\Protos\messages.proto" Link="Protos\messages.proto" />
</ItemGroup>
仓库中可直接查看到的真实契约文件:
- 内置 Agent 事件类型:dotnet/src/Microsoft.AutoGen/Agents/protos/agent_events.proto、dotnet/samples/Hello/protos/agent_events.proto、dotnet/test/Microsoft.AutoGen.Integration.Tests.AppHosts/protos/agent_events.proto——
NewMessageReceived、ConversationClosed、Shutdown等消息即由这类 proto 生成; - 跨语言网关契约:protos/agent_worker.proto 定义 Agent Worker 的服务接口,protos/cloudevent.proto 定义 CloudEvents 封装结构。
原文档同时指出:消息以 CloudEvents 规范 封装后发送到事件总线。跨语言测试 HelloAppHostIntegrationTests.cs 中的断言字符串 "INFO:autogen_core:Received a message from host: cloudEvent {" 直接证明了 Python 侧收到的正是 CloudEvents 封装的消息体。
八、作为集成测试资源:xLang 跨语言编排验证
HelloAgentTests 放在 Microsoft.AutoGen.Integration.Tests.AppHosts 下并非偶然。XLangTests.AppHost/Program.cs 用 Aspire 将其编排进一个分布式应用:
var builder = DistributedApplication.CreateBuilder(args);
var backend = builder.AddProject<Projects.Microsoft_AutoGen_AgentHost>("AgentHost").WithExternalHttpEndpoints();
// ...
dotnet = builder.AddProject<Projects.HelloAgentTests>("HelloAgentTestsDotNET")
.WithReference(backend)
.WithEnvironment("AGENT_HOST", backend.GetEndpoint("https"))
.WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true")
.WaitFor(backend);
// Python 侧
python = builder.AddPythonApp("HelloAgentTestsPython", pythonHelloAgentPath, pythonHelloAgentPy, pythonVEnv)
.WithReference(backend)
.WithEnvironment("AGENT_HOST", backend.GetEndpoint("http"))
.WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true")
// ...
拓扑为:AgentHost(运行时网关)为中枢,.NET HelloAgent 走 HTTPS gRPC 接入,Python hello_python_agent.py(见 core_xlang_hello_python_agent)走 HTTP 接入,三者经同一事件总线互通。XLANG_TEST_NO_DOTNET / XLANG_TEST_NO_PYTHON 环境变量用于在测试中裁剪单边资源。
HelloAppHostIntegrationTests.cs 中的用例逐一验证了消息路由,例如:
Test_Dotnet_Sends_AgentHost_Delivers_and_Python_Receives:等待 Python 资源日志中出现"Hello World!",证明 .NET → 网关 → Python 的投递;Test_Dotnet_Agent_Sends_And_AgentHost_Delivers_Back_To_It:断言 .NET 资源日志中先出现"Hello World!"后出现"HelloAgent said Goodbye"——后者正是第四节中ConversationClosed处理程序的打印格式$"{item.UserId} said {item.UserMessage}",把示例行为与测试断言严丝合缝地对应起来;Test_Python_Agent_Sends_And_AgentHost_Receives:断言网关日志中出现"source": "HelloAgents/python",与 .NET 侧的"HelloAgents/dotnet"形成对称验证。
九、小结
这篇 Hello World 文档及其配套源码,浓缩了 AutoGen 0.4 .NET 编程模型的四个支柱:
| 概念 | 文档表述 | 源码落点 |
|---|---|---|
| 事件订阅 | TopicSubscription 监听主题事件 |
HelloAgent.cs 的 [TypeSubscription("HelloTopic")] |
| 事件处理程序 | 通过 IHandle 注册,可继续发布事件 |
HandleAsync 三实现 + PublishMessageAsync(HelloAgent.cs) |
| 运行时启动 | 本地进程内或连接网关,一条消息触发全链路 | Program.cs 的 AGENT_HOST 分支 |
| 消息契约 | protobuf 定义 schema,CloudEvents 封装投递 | agent_events.proto、cloudevent.proto |
掌握这条“订阅—处理—发布—关闭”的最小闭环后,即可沿仓库继续深入:dotnet/samples/Hello/HelloAgentState 演示为 Agent 增加状态,dotnet/samples/Hello/HelloAIAgents 演示接入 LLM 的 AI Agent,而 Microsoft.AutoGen.Integration.Tests 则展示了同一套 Agent 代码如何零修改地成为分布式跨语言系统中的一个节点。
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 StartedRust0623
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