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 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 de virada antes de executar os manipuladores. Depois, o agente salva o estado de 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 certo.

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 de cartões adaptáveis
  • Condições personalizadas

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

Estado de ativação

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
Temp 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 handlers. 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 ingressando 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 OAuth e outras invocações sensíveis ao tempo)
    3 Rotas agentic
    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 genérico 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);

** Ativar ganchos de ciclo de vida

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 de turno

O agente carrega automaticamente o estado de turno antes que seus manipuladores sejam executados e salve-o posteriormente. O objeto de estado de turno passado para seus tratadores 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 todas as 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