AgentApplication in SDK per agenti Microsoft 365

AgentApplication è il componente principale di un agente costruito con l'SDK di Agenti. AgentApplication è il punto di ingresso per tutte le attività in arrivo, inclusi messaggi dagli utenti, eventi del ciclo di vita delle conversazioni, interazioni con le schede adattive, callback OAuth

Un agente è, nella sua essenza, un AgentApplication. Lo configuri con gestori che definiscono il comportamento del tuo agente. L'SDK si occupa del routing, della gestione dello stato e dell'infrastruttura necessaria per il suo funzionamento.

Come funziona AgentApplication

Ogni agente ha un ciclo di vita che inizia quando un canale (Microsoft Teams, un servizio Bot o un client personalizzato) consegna un'attività all'endpoint del tuo agente. AgentApplication è al centro di quel ciclo di vita:

Channel → Hosting layer → AgentApplication → Your handlers

I livelli di elaborazione in un agente costruito con l'SDK per agenti funzionano come segue:

  1. Il livello di hosting riceve la richiesta HTTP e la autentica.
  2. AgentApplication elabora l'attività in arrivo attraverso la sua pipeline.
  3. I gestori vengono chiamati in base alle distribuzioni corrispondenti.

L'agente carica la modifica dello stato prima dell'esecuzione delle funzioni di gestione. Successivamente, l'agente salva lo stato di turno.

Concetti principali

Attività

Tutto nell'SDK per agenti avviene come un'attività. Un'attività è un messaggio strutturato che rappresenta qualcosa che è accaduto. Un'attività ha un tipo, come message, event, invoke, conversationUpdate e così via. Trasporta un payload pertinente a quel tipo. AgentApplication riceve le attività e le indirizza al gestore giusto.

Cicli di lavorazione

Un percorso abbina un selettore a un gestore. Il selettore determina se un percorso corrisponde all'attività corrente. Il gestore esegue la tua logica quando il percorso corrisponde.

Registra le distribuzioni quando configuri l'agente. Possono corrispondere a:

  • Un messaggio contenente testo specifico o che corrisponde a un'espressione regolare
  • Qualsiasi attività di un dato tipo
  • Eventi del ciclo di vita della conversazione (membro aggiunto, membro rimosso)
  • Azioni della scheda adattiva
  • Condizioni personalizzate

Quando arriva un'attività, il sistema valuta i percorsi in ordine finché non trova una corrispondenza. Per impostazione predefinita, esiste un solo percorso.

Stato del turno

AgentApplication gestisce _turn stato — archiviazione strutturata suddivisa in ambiti:

Tipi di ambito Descrizione
Conversazione Condiviso tra tutti gli utenti in una conversazione, persistente tra un turno e l'altro
Utente Associato a un singolo utente attraverso tutte le conversazioni
Temp Solo turno corrente: mai persistente

Il sistema carica automaticamente lo stato prima dell'esecuzione dei gestori e lo salva automaticamente in seguito.

Contesto di turno

Quando viene eseguito un gestore, riceve un contesto di turno. Il contesto del turno è una snapshot dell'attività corrente, della connessione dell'adattatore e degli strumenti per inviare risposte. Il contesto del turno è l'interfaccia dell'interazione corrente.

Middleware

AgentApplicationSupporta una pipeline middleware. Il middleware è una catena di componenti che elaborano ogni operazione prima e dopo l'esecuzione dei gestori. Il middleware può ispezionare, trasformare o interrompere il flusso di attività. Gli usi comuni includono la registrazione, i controlli di autenticazione e la normalizzazione delle richieste.

Creare un agente

Crea una sottoclasse di AgentApplication e registra i tuoi gestori nel costruttore. Il framework di hosting inserisce automaticamente 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);
    }
}

Registra l'agente in 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();

Registra i gestori di attività

Gestire i messaggi

Abbina i messaggi per testo esatto (senza distinzione tra maiuscole e minuscole):

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

Abbina i messaggi usando un'espressione regolare:

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

Gestisci gli aggiornamenti delle conversazioni

Registra i gestori per eventi del ciclo di vita delle conversazioni, come l'ingresso o l'uscita dei membri.

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

Gestire qualsiasi tipo di attività

Confronta qualsiasi attività in base al tipo di stringa per un controllo completo sull'instradamento.

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

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

Usa ActivityTypes costanti invece di stringhe hardcoded.

Controlla l'ordine di valutazione dei percorsi

Il sistema ordina i percorsi in un ordine di valutazione fisso quando le registri, non durante l'esecuzione. L'ordinamento utilizza due livelli:

  1. Tipo di percorso: il sistema raggruppa i percorsi per tipo e valuta sempre i tipi di priorità superiore prima di quelli di priorità inferiore, indipendentemente dalla classificazione:

    Priorità Tipo di percorso
    1 (massimo) Distribuzioni di invocazione agentiche
    2 Distribuzioni di invocazioni (azioni della scheda adattiva, callback OAuth e altre invocazioni a tempo)
    3 Distribuzioni agentiche
    4 (minimo) Tutti gli altri percorsi
  2. Classificazione: All'interno di ciascun gruppo di tipo di percorso, il sistema ordina i percorsi in base al loro valore di classificazione. I valori numerici più bassi vengono valutati per primi.

Usare le costanti RouteRank per impostare la classificazione durante la registrazione di un gestore:

Costante Value Significato
RouteRank.First 0 Valutato prima di tutte le altre distribuzioni del suo gruppo
RouteRank.Unspecified 32767 Predefinito quando non viene specificata alcuna classificazione
RouteRank.Last 65535 Valutato dopo tutte le altre distribuzioni del suo gruppo

Per impostazione predefinita, la valutazione si interrompe al primo percorso di corrispondenza. Usare RouteRank.Last per un fallback onnicomprensivo che gestisce tutto ciò che non corrisponde a una distribuzione più specifica.

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

Hook del ciclo di vita dei turni

Registra la logica che viene eseguita a ogni passaggio, prima o dopo la corrispondenza del percorso. Questi hook sono utili per registrazione, aspetti trasversali e gestione degli errori.

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 restituisce false, il turno viene annullato e nessun percorso viene eseguito. Quando OnAfterTurn restituisce false, lo stato del turno non viene salvato.

Utilizzare lo stato del turno

L'agente carica automaticamente lo stato di turno prima dell'esecuzione dei gestori e lo salva in seguito. L'oggetto dello stato del turno passato ai tuoi gestori ti dà accesso ai diversi ambiti, così puoi leggere e scrivere dati che persistono tra i turni o sono temporanei per il turno corrente:

  • Ambito della conversazione: per i dati condivisi tra tutti i turni in una conversazione
  • Ambito utente: per dati per utente
  • Ambito temporaneo: per dati temporanei che esistono solo nel turno corrente
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);
});

Nota

Utilizza MemoryStorage per sviluppo e test locale. Per le distribuzioni della produzione, specialmente quelle in esecuzione su più istanze, utilizza un provider di archiviazione come Azure Cosmos DB o Archiviazione BLOB di Azure. Vedi Usare i provider di archiviazione nell'agente.

Passaggi successivi