Microsoft 365 智能体 SDK 中的 AgentApplication

AgentApplication 是用智能体 SDK 构建的智能体的核心构建基块。 AgentApplication 是所有传入活动的入口点,包括来自用户的消息、会话生命周期事件、自适应卡片交互、OAuth 回调。

智能体的核心本质是 AgentApplication。 你通过配置处理器来定义智能体的功能。 SDK 会处理路由、状态管理,以及支持其运行的基础设施。

AgentApplication 的工作原理

每个智能体都有一个生命周期,生命周期始于某个渠道(如 Microsoft Teams、机器人服务或自定义客户端)向你的智能体终结点传递活动。 AgentApplication 处于这一生命周期的核心:

Channel → Hosting layer → AgentApplication → Your handlers

使用智能体 SDK 构建的智能体处理层如下:

  1. 托管层接收 HTTP 请求并进行身份验证。
  2. AgentApplication 通过其管道处理传入活动。
  3. 您的处理程序会根据匹配的路由被调用。

在处理程序运行之前,智能体会加载回合状态。 之后,智能体会保存回合状态。

核心概念

活动

在智能体 SDK 中,所有流程都以活动的形式进行。 活动是一条结构化消息,用于代表发生过的某件事。 活动有类型,如消息、事件、调用、会话更新等。 它携带与该类型相关的有效载荷。 AgentApplication 接收活动并将其路由到合适的处理程序。

工艺路线

路由将选择器处理程序配对。 选择器用于确定路由是否与当前活动匹配。 当路由匹配时,处理器会执行你的逻辑。

请在配置智能体时注册路由。 路由可以匹配:

  • 包含特定文本或匹配正则表达式的消息
  • 任何指定类型的活动
  • 对话生命周期事件(添加成员、删除成员)
  • 自适应卡片操作
  • 自定义条件

当活动到达时,系统会按顺序评估路由,直到找到匹配项。 默认情况下,只会运行一个路由。

回合状态

AgentApplication manages _turn 状态——一种按作用域划分的结构化存储:

作用域类型 描述
对话 在对话中所有用户之间共享,并在回合之间持久化
用户 针对单个用户,适用于所有对话
温度 仅限当前回合——从不持久化

系统会在处理程序运行前自动加载状态,并在运行后自动保存状态。

轮次上下文

当处理器运行时,它会收到一个回合上下文。 回合上下文是当前活动、适配器连接以及用于发送响应的实用程序的快照。 回合上下文是当前交互的界面。

中间件

AgentApplication 支持中间件管道。 中间件是一系列组件,在处理程序运行前后处理每个回合。 中间件可以检查、转换或截断活动流。 常见用途包括日志记录、身份验证检查和请求规范化。

创建智能体

继承 AgentApplication 并将其处理程序注册在构造函数中。 托管框架会自动注入 AgentApplicationOptions

public class MyAgent : AgentApplication
{
    public MyAgent(AgentApplicationOptions options) : base(options)
    {
        OnConversationUpdate(ConversationUpdateEvents.MembersAdded, WelcomeAsync);
        OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last);
    }

    private async Task WelcomeAsync(ITurnContext context, ITurnState state, CancellationToken ct)
    {
        foreach (var member in context.Activity.MembersAdded)
        {
            if (member.Id != context.Activity.Recipient.Id)
            {
                await context.SendActivityAsync("Hello! How can I help you?", cancellationToken: ct);
            }
        }
    }

    private async Task OnMessageAsync(ITurnContext context, ITurnState state, CancellationToken ct)
    {
        await context.SendActivityAsync($"You said: {context.Activity.Text}", cancellationToken: ct);
    }
}

Program.cs 中注册您的智能体:

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient();
builder.Services.AddSingleton<IStorage, MemoryStorage>();
builder.Services.AddAgent<MyAgent>();
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);

WebApplication app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
app.MapAgentApplicationEndpoints(requireAuth: !app.Environment.IsDevelopment());

app.Run();

注册活动处理程序

消息处理

按精确文本匹配消息(不区分大小写):

OnMessage("help", async (context, state, ct) =>
{
    await context.SendActivityAsync("Here's what I can do...", cancellationToken: ct);
});

用正则表达式匹配消息:

OnMessage(new Regex(@"^order\s+\d+$", RegexOptions.IgnoreCase), async (context, state, ct) =>
{
    await context.SendActivityAsync("Looking up your order...", cancellationToken: ct);
});

处理对话更新

为对话生命周期事件(如成员加入或离开)注册处理程序。

OnConversationUpdate(ConversationUpdateEvents.MembersAdded, async (context, state, ct) =>
{
    foreach (var member in context.Activity.MembersAdded)
    {
        if (member.Id != context.Activity.Recipient.Id)
        {
            await context.SendActivityAsync("Welcome!", cancellationToken: ct);
        }
    }
});

OnConversationUpdate(ConversationUpdateEvents.MembersRemoved, async (context, state, ct) =>
{
    // Called when participants leave the conversation
});

处理任何活动类型

按类型字符串匹配任何活动,以完全控制路由。

OnActivity(ActivityTypes.Message, async (context, state, ct) =>
{
    // Handles all message activities
});

OnActivity(ActivityTypes.Event, async (context, state, ct) =>
{
    // Handles event activities
});

使用 ActivityTypes 常量,而不是硬编码字符串。

控制路由评估顺序

系统在您注册路由时(而非运行时)将其排序为固定的评估顺序。 排序使用两个层级:

  1. 路由类型:系统按类型分组路由,并且无论排名如何,始终优先评估高优先级类型,再评估低优先级类型。

    优先级 传递类型
    1(最高) 智能体式调用路由
    2 调用路由(自适应卡片操作、OAuth 回调及其他对时间敏感的调用)
    3 智能体式路由
    4(最低) 所有其他路由
  2. 排名:在每个路由类型组内,系统会根据排名值对路由进行排序。 系统会优先处理数值较低的路线。

注册处理程序时使用 RouteRank 常量设置排名:

定额 含义
RouteRank.First 0 在其所属组中优先于所有其他路由进行评估
RouteRank.Unspecified 32767 如果没有指定优先级,则使用默认值
RouteRank.Last 65535 在其所属组中晚于所有其他路由进行评估

系统默认在找到第一个匹配的路由后停止评估。 使用 RouteRank.Last 作为通配回退,用于处理未被更具体路由匹配的任何情况。

// Specific handlers use the default rank
OnMessage("status", HandleStatusAsync);
OnMessage("help", HandleHelpAsync);

// Catch-all — handles anything not matched above
OnActivity(ActivityTypes.Message, HandleUnknownMessageAsync, rank: RouteRank.Last);

Turn 生命周期钩子

注册在每个 Turn 执行时(路由匹配之前或之后)运行的逻辑。 这些钩子适用于日志记录、横切关注点和错误处理。

OnBeforeTurn(async (context, state, ct) =>
{
    logger.LogInformation("Turn started: {Type}", context.Activity.Type);
    return true; // Return false to abort the turn
});

OnAfterTurn(async (context, state, ct) =>
{
    logger.LogInformation("Turn completed");
    return true; // Return false to skip state saving
});

OnTurnError(async (context, state, exception, ct) =>
{
    logger.LogError(exception, "Turn error");
    await context.SendActivityAsync("Something went wrong. Please try again.", cancellationToken: ct);
});

OnBeforeTurn 返回 false 时,该轮次将被中止,且不会执行任何路由。 当 OnAfterTurn 返回 false 时,轮次状态不会被保存。

使用轮次状态

智能体会在处理程序运行前转变状态,并在运行后自动保存状态。 传递给处理程序的轮次状态对象允许您访问不同的作用域,从而读取和写入跨轮次持久的数据或仅在当前轮次中存在的临时数据:

  • 对话范围:指对话中所有回合共享的数据
  • 用户范围:针对每个用户的数据
  • 临时作用域:用于仅需在当前回合中存在的数据
OnActivity(ActivityTypes.Message, async (context, state, ct) =>
{
    // Conversation scope — persisted per conversation
    var count = state.Conversation.GetValue<int>("messageCount", () => 0);
    state.Conversation.SetValue("messageCount", count + 1);

    // User scope — persisted per user
    var name = state.User.GetValue<string>("displayName");

    // Temp scope — current turn only
    state.Temp.SetValue("parsedInput", context.Activity.Text?.Trim());

    await context.SendActivityAsync($"Message #{count + 1}: {context.Activity.Text}", cancellationToken: ct);
});

备注

使用 MemoryStorage 进行本地开发和测试。 对于生产部署,尤其是在多个实例上运行的部署,请使用持久性存储提供程序,例如 Azure Cosmos DB 或 Azure Blob 存储。 请参阅在智能体中使用存储提供程序

后续步骤