AgentApplication no SDK de Agentes do Microsoft 365

AgentApplication é o componente central de um agente criado com o Agents SDK. AgentApplication é o ponto de entrada para todas as atividades de entrada, incluindo mensagens de usuários, eventos de ciclo de vida de conversa, interações de cartão adaptável, retornos de chamada do OAuth.

Um agente é, em sua essência, um AgentApplication. Configure-o com manipuladores que descrevem o que seu agente faz. O SDK cuida do roteamento, do gerenciamento de estado e da infraestrutura necessária para executá-lo.

Como o AgentApplication funciona

Cada agente tem um ciclo de vida que começa quando um canal (Microsoft Teams, um serviço de bot ou um cliente personalizado) entrega uma atividade ao ponto de extremidade do agente. AgentApplication fica no centro desse ciclo de vida:

Channel → Hosting layer → AgentApplication → Your handlers

As camadas de processamento em um agente criado com o SDK de Agentes funcionam da seguinte maneira:

  1. A camada de hospedagem recebe a solicitação HTTP e a autentica.
  2. Processa AgentApplication a atividade de entrada por meio de seu pipeline.
  3. Seus controladores são chamados com base em rotas correspondentes.

O agente carrega o estado do turno antes de executar os manipuladores. Depois, o agente salva o estado do turno.

Conceitos principais

Atividades

Tudo no SDK de Agentes flui como uma atividade. Uma atividade é uma mensagem estruturada que representa algo que aconteceu. Uma atividade tem um tipo, como mensagem, evento, invocação, conversationUpdate e assim por diante. Ele transporta uma carga útil relevante para esse tipo de operação. AgentApplication recebe atividades e as roteia para o manipulador correto.

Routes

Uma rota emparelha um seletor com um manipulador. O seletor determina se uma rota corresponde à atividade atual. O controlador executa sua lógica quando a rota coincide.

Registre rotas ao configurar seu agente. Eles podem corresponder:

  • Uma mensagem que contém um texto específico ou uma expressão regular correspondente
  • Qualquer atividade de um determinado tipo
  • Eventos do ciclo de conversa (membro adicionado, membro removido)
  • Ações do cartão adaptável
  • Condições personalizadas

Quando uma atividade chega, o sistema avalia as rotas em ordem até encontrar uma correspondência. Por padrão, apenas uma rota é executada.

Estado do turno

AgentApplication gerencia o estado de turno—armazenamento estruturado particionado em âmbitos:

Tipo de escopo Description
Conversa Compartilhado entre todos os usuários em uma conversa, persistido entre turnos
Usuário Restringido a um usuário individual em todas as conversas
Temporário Somente turno atual – nunca persistido

O sistema carrega automaticamente o estado antes que seus manipuladores sejam executados e o salve automaticamente posteriormente.

Contexto de turno

Quando um manipulador é executado, ele recebe um contexto de turno. O contexto de turno é um resumo instantâneo da atividade atual, da conexão do adaptador e dos utilitários para enviar respostas. O contexto de turno é sua interface para a interação atual.

Middleware

AgentApplication dá suporte a um pipeline de middleware. Middleware é uma cadeia de componentes que processam cada fase antes e depois da execução dos manipuladores. O middleware pode inspecionar, transformar ou interromper o fluxo de atividade. Os usos comuns incluem registro em log, verificações de autenticação e normalização de solicitação.

Criar um agente

Subclasse AgentApplication e registre seus manipuladores no construtor. A estrutura de hospedagem injeta AgentApplicationOptionsautomaticamente .

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

Registre seu agente em 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();

Registrar manipuladores de atividade

Manipular mensagens

Corresponder mensagens por texto exato (não diferencia maiúsculas de minúsculas):

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

Combine mensagens usando uma expressão regular:

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

Gerenciar atualizações de conversação

Registre manipuladores para eventos de ciclo de vida de conversa, como membros entrando ou saindo.

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

Gerenciar qualquer tipo de atividade

Corresponda a qualquer atividade por sua cadeia de caracteres de tipo para controle completo sobre o roteamento.

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

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

Use ActivityTypes constantes em vez de cadeias de caracteres codificadas.

Controlar a ordem de avaliação das rotas

O sistema classifica as rotas em uma ordem de avaliação fixa no momento em que você as registra, e não em tempo de execução. A classificação usa dois níveis:

  1. Tipo de rota: o sistema agrupa rotas por tipo e sempre avalia tipos de prioridade mais alta antes dos tipos de prioridade mais baixa, independentemente da classificação:

    Prioridade Tipo de rota
    1 (mais alto) Rotas de invocação de agente
    2 Invocar rotas (ações de cartão adaptável, retornos de chamada de OAuth e outras invocações urgentes)
    3 Rotas do agente
    4 (mais baixo) Todas as outras rotas
  2. Classificação: dentro de cada grupo de tipos de rota, o sistema ordena rotas por seu valor de classificação. Valores numéricos mais baixos são avaliados primeiro.

Use RouteRank constantes para definir a classificação ao registrar um manipulador:

Constante Valor Significado
RouteRank.First 0 Avaliado antes de todas as outras rotas em seu grupo
RouteRank.Unspecified 32767 Padrão quando nenhuma classificação é especificada
RouteRank.Last 65535 Avaliado depois de todas as outras rotas do seu grupo

Por padrão, a avaliação é interrompida na primeira rota correspondente. Use RouteRank.Last para um fallback geral que manipula qualquer coisa que não corresponda a uma rota mais específica.

// 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);

Ganchos de ciclo de vida do turno

Registre a lógica que é executada em cada turno, antes ou depois da correspondência de rotas. Esses ganchos são úteis para registro em log, preocupações transversais e tratamento de erros.

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

Quando OnBeforeTurn retorna false, o ciclo é anulado e nenhuma rota é executada. Quando OnAfterTurn retorna false, o estado de turno não é salvo.

Usar o estado do turno

O agente carrega automaticamente o estado do turno antes que seus manipuladores sejam executados e salve-o posteriormente. O objeto de estado do turno passado para seus manipuladores fornece acesso aos diferentes escopos para que você possa ler e gravar dados que persistem entre turnos ou que sejam efêmeros para o turno atual.

  • Escopo da conversa: para dados compartilhados em todos os turnos de uma conversa
  • Escopo do usuário: para dados por usuário
  • Escopo temporário: para dados que só precisam existir durante o turno atual
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);
});

Note

Use MemoryStorage para desenvolvimento e teste locais. Para implantações de produção, especialmente implantações em execução em várias instâncias, use um provedor de armazenamento persistente, como Azure Cosmos DB ou Armazenamento de Blobs do Azure. Consulte Como usar provedores de armazenamento no seu agente.

Próximas etapas