首页
/ AutoGen .NET HelloAgent 样例实战:事件驱动 Agent、IHandle 处理链与本地/gRPC 两种 App Runtime 启动方式

AutoGen .NET HelloAgent 样例实战:事件驱动 Agent、IHandle 处理链与本地/gRPC 两种 App Runtime 启动方式

2026-09-05 22:00:56作者:冯梦姬Eddie

本文基于 AutoGen 仓库中的 HelloAgent 样例(dotnet/samples/Hello/HelloAgent/README.md),完整讲解 AutoGen 0.4 .NET SDK 的"Hello World":如何用 TypeSubscription 订阅主题、用 IHandle<T> 编写事件处理器、用 AgentsAppBuilder 在本地进程内或 gRPC 网关两种模式下启动 App Runtime,并通过发布 NewMessageReceivedConversationClosedShutdown 消息链驱动一次完整的会话。读完后,你将能够独立编写一个可监听事件、发布事件、自我关闭的最小 AutoGen .NET Agent 应用,并理解其底层反射式处理器注册与消息分发机制。

样例定位与前置条件

HelloAgent 是 AutoGen .NET(仓库 dotnet/ 目录)最小的完整示例,它演示了 AutoGen 0.4 分布式 Agent 模型的两个核心概念:

  • 事件驱动的 Agent:Agent 通过订阅某个 Topic(主题)来"等待"事件;收到事件后,运行时调用对应的处理器;
  • 可编程的 App Runtime:SDK 提供 Microsoft.AutoGen.Agents.App(源码中为 AgentsApp / AgentsAppBuilder)运行时,既可以在本地进程内启动(InProcess Runtime),也可以作为 gRPC Worker 连接到分布式网关。

运行该样例需要 .NET 8.0 或更高版本。从工程文件 HelloAgent.csproj 可以看到,项目目标框架为 net8.0,并引用了 Microsoft.Extensions.Hosting(宿主生命周期管理)、Google.ProtobufGrpc.Tools(gRPC 代码生成),同时通过 ProjectReference 依赖 SDK 的三个核心工程:

<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Contracts\Microsoft.AutoGen.Contracts.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Core.Grpc\Microsoft.AutoGen.Core.Grpc.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.AutoGen\Core\Microsoft.AutoGen.Core.csproj" />

并直接以 <Protobuf> 项引入了 SDK 内置的事件 proto 文件(注意该 proto 位于 SDK 源码目录,而非样例目录):

<Protobuf Include="..\..\..\src\Microsoft.AutoGen\Agents\protos\agent_events.proto" Link="protos\agent_events.proto" />

配置文件 appsettings.json 仅用于调整日志级别:

{
    "Logging": {
      "LogLevel": {
        "Default": "Warning",
        "Microsoft": "Information",
        "Microsoft.Orleans": "Warning"
      }
    }
  }

其中 Microsoft.Orleans 一项印证了从源码结构可推断的信息:gRPC 网关一侧的消息注册与订阅状态托管在 Orleans 之上(见 RuntimeGateway.Grpc/Services/Orleans)。

运行样例

dotnet/samples/Hello/HelloAgent/README.md 给出的步骤:

# Clone the repository
gh repo clone microsoft/autogen
cd dotnet/samples/Hello
dotnet run

即克隆仓库后进入 dotnet/samples/Hello/HelloAgent 执行 dotnet run(目录级说明见 dotnet/samples/Hello/README.md)。该样例还支持命令行参数,完整参数说明可从 Program.csPrintHelp 方法得到:

HelloAgent [--host <hostAddress>] [--nosend]
  --host    使用 <hostAddress> 处的 gRPC 网关;也可以改用 AGENT_HOST 环境变量
  --nosend  不发送初始消息。意味着 HelloAgent 会一直等待其他 Agent
            发送该消息。注意:搭配 InProcessRuntime 时此选项不可用。
  • 不传 --host(且未设置 AGENT_HOST 环境变量)时,使用进程内运行时,消息在本地直接投递;
  • 传入 --host <address> 或设置 AGENT_HOST 时,使用 gRPC 运行时,样例以 gRPC Worker 身份连接到分布式网关;
  • --nosend 用于分布式演示场景(本进程只等别人发消息),但在进程内运行时没有外部消息源,因此 Program.cs 会直接打印警告并以非零码退出,避免程序挂起。

除单进程直接运行外,仓库还提供了基于 .NET Aspire 的 App Host(dotnet/samples/Hello/Hello.AppHost),可一并启动 Agent 与后端服务并在 Aspire Dashboard 中查看遥测与日志;此外还有 dotnet/samples/GettingStartedGrpc/ 演示了如何把 Agent 部署为 gRPC Worker 并与 Host 协作。

完整事件流:一条消息链驱动"问候—告别—关闭"

HelloAgent 的整体行为可以概括为一条三段式消息链,对应 HelloAgent.cs 中的三个处理器:

%%{init: {'theme':'forest'}}%%
graph LR;
    A["Program.cs<br/>PublishMessageAsync(NewMessageReceived('Hello World!'))"] --> B{"HandleAsync(NewMessageReceived, MessageContext)"}
    B --> |"PublishMessageAsync(ConversationClosed('Goodbye'))"| C{"HandleAsync(ConversationClosed, MessageContext)"}
    C --> |"PublishMessageAsync(Shutdown())<br/>(未设置 STAY_ALIVE_ON_GOODBYE 时)"| D{"HandleAsync(Shutdown, MessageContext)"}
    D --> E{"IHostApplicationLifetime.StopApplication()"}

各环节的具体行为:

  1. 入口Program.cs 构建并启动 App 后,向主题 HelloTopic 发布 NewMessageReceived { Message = "Hello World!" }Program.cs L58-L62);
  2. 收到消息HelloAgentHandleAsync(NewMessageReceived, ...) 将消息打印到控制台,随后向同一主题发布 ConversationClosed,其中 UserId 取自 this.Id.TypeUserMessage = "Goodbye"HelloAgent.cs L23-L33);
  3. 收到告别:第二个处理器打印 "{userId} said Goodbye";除非设置了环境变量 STAY_ALIVE_ON_GOODBYE=true(用于让分布式演示中进程保持存活),否则继续发布 Shutdown 消息(HelloAgent.cs L34-L43);
  4. 收到关闭:第三个处理器调用注入的 IHostApplicationLifetime.StopApplication() 停止宿主,Program.cs 中的 WaitForShutdownAsync() 随即返回,进程退出(HelloAgent.cs L45-L49Program.cs L69)。

值得注意的是,第 2、3 步发布的消息最终都由 HelloAgent 自己接收——这正是 Program.cs 中进程内运行时配置 deliverToSelf: true 的作用:允许 Agent 投递消息给自身(Program.cs L52)。

编写事件处理器:TypeSubscription 与 IHandle 注册机制

README 指出:"AutoGen 应用的核心是事件处理器。Agent 选择一个 TopicSubscription 来监听某个主题上的事件;当事件到达时,Agent 的处理器被调用;处理器内部还可以 emit(发布)新事件,交给事件总线供其他 Agent 处理"。消息类型(EventTypes)是以 gRPC ProtoBuf 消息定义的,默认消息集位于 Microsoft.AutoGen.Contracts 命名空间,proto 定义见 dotnet/src/Microsoft.AutoGen/Agents/protos/agent_events.proto 与根目录 protos/agent_worker.protocloudevents 协议)。

当前样例中,订阅通过类上的 [TypeSubscription] 特性声明,处理器通过实现 IHandle<T> 接口注册(注意:README 早期版本写作 TopicSubscription,当前代码为 TypeSubscription):

[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>
{
    // 捕获 Program.cs 中发布的消息
    public async ValueTask HandleAsync(NewMessageReceived item, MessageContext messageContext)
    {
        Console.Out.WriteLine(item.Message); // 打印到控制台
        ConversationClosed goodbye = new ConversationClosed
        {
            UserId = this.Id.Type,
            UserMessage = "Goodbye"
        };
        // 发布 ConversationClosed,将由自身的 ConversationClosed 处理器处理
        await this.PublishMessageAsync(goodbye, new TopicId("HelloTopic"));
    }
    // ... ConversationClosed 与 Shutdown 处理器同理
}

处理器是如何被自动发现和分发的

IHandle<T> 接口定义在 dotnet/src/Microsoft.AutoGen/Contracts/IHandle.cs,存在两种重载:

public interface IHandle<in T>
{
    ValueTask HandleAsync(T item, MessageContext messageContext);
}

public interface IHandle<in InT, OutT>
{
    ValueTask<OutT> HandleAsync(InT item, MessageContext messageContext);
}

所谓"在构造函数/类型声明中用 IHandle 注册事件类型",其底层实现在基类 BaseAgent 中:构造函数调用 ReflectInvokers(),通过反射扫描当前 Agent 类型实现的所有 IHandle<> / IHandle<,> 泛型接口,取出每个接口的 HandleAsync 方法并包装为 HandlerInvoker,建立起 "消息类型 → 处理器" 的字典(BaseAgent.cs L60-L81)。当运行时把消息投递给 Agent 时,OnMessageAsync 按消息的运行时类型查表并调用对应处理器,查不到则静默返回 nullBaseAgent.cs L83-L93)。这意味着:

  • 你只需声明接口 + 实现方法,无需任何显式注册代码;
  • 一个 Agent 可以同时处理任意多种消息类型,互不干扰;
  • 未处理的消息类型不会报错,而是被忽略——这对事件总线场景是有意为之的设计。

发布消息同样由基类统一提供:PublishMessageAsync(object message, TopicId topic, ...) 转发给 IAgentRuntime.PublishMessageAsyncSendMessageAsync 则面向指定收件 Agent 做点对点投递(BaseAgent.cs L95-L104)。

继承与组合:复用基类能力

README 在"Inheritance and Composition"一节强调,该样例同时展示了 AutoGen 的继承机制(早期版本中 HelloAgent 继承自提供 WriteConsole 方法的 ConsoleAgent;当前仓库代码中则直接继承 BaseAgent,控制台打印逻辑内联在处理器中)。无论继承链如何演变,设计意图一致:业务 Agent 通过继承基类获得"发布消息、点对点发送、元数据、日志"等横切能力,自身只专注事件语义

从源码结构看,BaseAgent 还暴露了 Metadata(由 AgentId.TypeAgentId.KeyDescription 组成)和 ActivitySource(用于 OpenTelemetry 链路追踪),这些都会随 Agent 注册到运行时(参见 Contracts/AgentMetadata.cs)。HelloAgent 通过主构造函数把 AgentIdIAgentRuntimeIHostApplicationLifetimeLogger 全部以依赖注入方式传入——由 AddAgent<HelloAgent>("HelloAgent") 时由宿主解析构造。SDK 的 Agents 包 还预置了 InferenceAgent(AI 推理)、IHandleConsole / IHandleFileIO(IO 处理)等可组合构件,体现了同一套"基类 + 接口组合"思路。

启动 App Runtime:进程内与 gRPC 两种模式

Program.cs 展示了如何用 AgentsAppBuilder 一条链完成"选运行时 → 注册 Agent → 构建 → 启动 → 发消息 → 等关闭":

var appBuilder = new AgentsAppBuilder(); // 创建 app builder
bool usingGrpc = false;
if (hostAddress is string agentHost)
{
    // 分布式模式:连接 AGENT_HOST / --host 指定的 gRPC 网关
    usingGrpc = true;
    appBuilder.AddGrpcAgentWorker(agentHost)
        .AddAgent<HelloAgent>("HelloAgent");
}
else
{
    // 进程内运行时:允许 Agent 给自己投递消息,并注册 HelloAgent
    appBuilder.UseInProcessRuntime(deliverToSelf: true).AddAgent<HelloAgent>("HelloAgent");
}
var app = await appBuilder.BuildAsync(); // 构建 app
await app.StartAsync();

if (sendHello)
{
    var message = new NewMessageReceived { Message = "Hello World!" };
    await app.PublishMessageAsync(message, new TopicId("HelloTopic")).ConfigureAwait(false);
}
await app.WaitForShutdownAsync().ConfigureAwait(false); // 等待 Agent 触发关闭

Program.cs L37-L69

两条分支的关键差异:

维度 UseInProcessRuntime(deliverToSelf: true) AddGrpcAgentWorker(address)
部署形态 单机单进程,消息在内存总线中直接投递 以 gRPC Worker 身份连接分布式网关
消息投递 可投递给自己(deliverToSelf),支撑"自我事件链" 经由网关的事件总线跨进程投递
适用场景 本地开发、单元测试 多 Agent 分布式编排(配合 RuntimeGateway)

gRPC 分支的实现位于 AgentsAppBuilderExtensions.cs:它注册 AgentRpc.AgentRpcClient,地址解析顺序为显式参数 > AGENT_HOST 配置项 > 默认 http://localhost:53071,并把 GrpcAgentRuntime 作为 IAgentRuntimeIHostedService 注入(即 Worker 连接随宿主生命周期自动连接/断开),同时配置了带退避重试(最多 5 次、初始 1s、最大 5s、1.5 倍递增)的 gRPC 通道,应对网关瞬时不可用。

关于消息的封装,README 说明"消息遵循 CloudEvents 规范包装后发送到事件总线":SDK 在网关侧将 Protobuf 消息包装为 CloudEvent(见 Core.Grpc/CloudEventExtensions.cs 及根目录 protos/cloudevent.proto),这也是消息能够跨进程、跨语言(Python/.NET 互操作)传递的基础。

定义自定义消息:proto + csproj 配置

AutoGen 中可发布的消息集合由 proto 文件定义,gRPC 工具将其编译为 C# 类。样例内置了 NewMessageReceivedConversationClosedShutdown 等消息(agent_events.proto),例如:

message NewMessageReceived {
    string message = 1;
}
message ConversationClosed {
    string user_id = 1;
    string user_message = 2;
}
message Shutdown {
    string message = 1;
}

若要定义自己的消息类型,只需新建 .proto 文件并在工程中引入 gRPC 工具。README 给出的示例(字段取自 dev-team 样例的消息集,真实定义见 dotnet/samples/dev-team/Protos/messages.proto):

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;
}

对应 .csproj 配置(HelloAgent 自身的 HelloAgent.csproj L17-L31 正是这种写法的实例):

<ItemGroup>
    <PackageReference Include="Google.Protobuf" />
    <PackageReference Include="Grpc.Tools" PrivateAssets="All" />
    <Protobuf Include="..\Protos\messages.proto" Link="Protos\messages.proto" />
</ItemGroup>

其中 PrivateAssets="All" 表示 Grpc.Tools 仅参与本工程的代码生成、不随包传递;<Protobuf Include> 的相对路径可指向任意位置,Link 只影响解决方案资源管理器中的展示路径。定义完成后,新的消息类型即可像 NewMessageReceived 一样被 PublishMessageAsync 发布、被 IHandle<T> 处理。

小结

HelloAgent 样例用不到百行代码完整覆盖了 AutoGen .NET SDK 的最小闭环:[TypeSubscription] 声明主题订阅、IHandle<T> 声明事件处理器(由 BaseAgent 的反射机制自动注册)、BaseAgent.PublishMessageAsync 发布事件、AgentsAppBuilder 在进程内/gRPC 两种模式下启动运行时,并以消息驱动的方式优雅关闭宿主。理解这个"Hello World"之后,替换掉 Console.WriteLine、接入 InferenceAgent 或 IO Agent,就可以按同一套模式扩展出真正的分布式多 Agent 应用;仓库中的 dotnet/samples/GettingStartedGrpc/dotnet/samples/dev-team/ 分别演示了 gRPC Worker 部署和完整的团队级 Agent 后端,可作为下一步的进阶材料。

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