AgentApplication in Microsoft 365 Agents SDK

AgentApplication ist die zentrale Komponente eines mit dem Agents SDK entwickelten Agents. AgentApplication ist der Einstiegspunkt für alle eingehenden Aktivitäten, einschließlich Nachrichten von Benutzern, Unterhaltungslebenszyklusereignisse, Interaktionen mit adaptiven Karten, OAuth-Rückrufe.

Ein Agent ist im Kern ein AgentApplication. Sie konfigurieren ihn mit Handlern, die beschreiben, was Ihr Agent tut. Das SDK kümmert sich um Routing, Zustandsmanagement und die Infrastruktur, die für den Betrieb erforderlich ist.

Funktionsweise von AgentApplication

Der Lebenszyklus eines jeden Agents beginnt, sobald ein Kanal (Microsoft Teams, ein Bot Service oder ein benutzerdefinierter Client) eine Aktivität an den Endpunkt Ihres Agents übermittelt. AgentApplication steht im Zentrum dieses Lebenszyklus:

Channel → Hosting layer → AgentApplication → Your handlers

Die Verarbeitungsschichten in einem Agent, der mit dem Agents SDK gebaut wurde, funktionieren wie folgt:

  1. Die Hosting-Schicht empfängt die HTTP-Anforderung und authentifiziert sie.
  2. Die AgentApplication verarbeitet die eingehende Aktivität durch ihre Pipeline.
  3. Ihre Handler werden basierend auf übereinstimmenden Routen aufgerufen.

Ihr Agent lädt den Zustand, bevor Ihre Handler laufen. Anschließend speichert der Agent den Dialogschrittstatus.

Wichtige Konzepte

Aktivitäten

Alles im Agents SDK fließt als eine Aktivität. Eine Aktivität ist eine strukturierte Nachricht, die etwas darstellt, das geschehen ist. Eine Aktivität hat einen Typ, zum Beispiel Message, Event, Aufruf, conversationUpdate und so weiter. Es trägt eine für diesen Typ relevante Nutzlast. AgentApplication nimmt Aktivitäten entgegen und leitet sie an den passenden Handler weiter.

Arbeitspläne

Eine Route verbindet einen Selektor mit einem Handler. Der Selektor bestimmt, ob eine Route mit der aktuellen Aktivität übereinstimmt. Der Handler führt Ihre Logik aus, wenn die Route übereinstimmt.

Registrieren Sie Routen, wenn Sie Ihren Agent konfigurieren. Folgendes kann abgeglichen werden:

  • Eine Nachricht, die einen bestimmten Text enthält oder mit einem regulären Ausdruck übereinstimmt
  • Jede Aktivität eines bestimmten Typs
  • Unterhaltungslebenszyklus-Ereignisse (Mitglied hinzugefügt, Mitglied entfernt)
  • Aktionen für adaptive Karten
  • Benutzerdefinierte Bedingungen

Wenn eine Aktivität empfangen wird, prüft das System die Routen der Reihe nach, bis es eine Übereinstimmung findet. Standardmäßig wird nur eine Route ausgeführt.

Turn-Zustand

AgentApplication verwaltet den Turn-Zustand—ein strukturierter Speicher, der in Umfänge aufgeteilt ist:

Bereichstyp Beschreibung des Dataflows
Unterhaltung Geteilt von allen Nutzern in einer Unterhaltung, über mehrere Runden hinweg gespeichert
Benutzer Gültig für einen einzelnen Nutzer über alle Unterhaltungen hinweg
Temp. Nur aktueller Dialogschritt – wird nie beibehalten

Das System lädt den Status automatisch, bevor Ihre Handler ausgeführt werden, und speichert ihn danach automatisch.

Durchlaufkontext

Wenn ein Handler ausführt, erhält er einen Zug-Kontext. Der Dialogschrittkontext ist eine Momentaufnahme der aktuellen Aktivität, der Adapterverbindung und der Hilfsprogramme zum Senden von Antworten. Der Dialogschrittkontext ist Ihre Schnittstelle zur aktuellen Interaktion.

Middleware

AgentApplication unterstützt eine Middleware-Pipeline. Middleware ist eine Kette von Komponenten, die jeden Dialogschritt vor und nach der Ausführung der Handler verarbeiten. Middleware kann den Aktivitätsfluss inspizieren, transformieren oder kurzschließen. Häufige Anwendungen sind Protokollierung, Authentifizierungsprüfungen und Normalisierung von Anfragen.

Einen Agent erstellen

Leiten Sie AgentApplication ab und registrieren Sie Ihre Handler im Konstruktor. Das Hosting-Framework injiziert AgentApplicationOptions automatisch.

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

Registrieren Sie Ihren Agent 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();

Aktivitätshandler registrieren

Nachrichten verarbeiten

Nachrichten anhand des exakten Texts (groß-/kleinschreibungsunabhängig) abgleichen:

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

Abgleichen von Nachrichten mithilfe eines regulären Ausdrucks:

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

Unterhaltungsaktualisierungen handhaben

Registrieren Sie Handler für Unterhaltungslebenszyklus-Ereignisse wie das Ein- oder Austreten von Mitgliedern.

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

Jede Aktivitätsart behandeln

Aktivitäten anhand ihrer Typ-Zeichenfolge abgleichen, um vollständige Kontrolle über das Routing zu erhalten.

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

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

Verwenden Sie ActivityTypes-Konstanten statt fest kodierter Zeichenfolgen.

Bewertungsreihenfolge der Steuerrouten

Das System sortiert die Routen bei der Registrierung in eine feste Auswertungsreihenfolge, nicht zur Laufzeit. Die Sortierung verwendet zwei Ebenen:

  1. Routentyp: Das System gruppiert Routen nach Typ und wertet immer höher priorisierte Typen vor niedriger priorisierten Typen aus, unabhängig vom Rang:

    Priorität Routentyp
    1 (höchste) Agentische Aufrufrouten
    2 Aufrufrouten (adaptive Kartenaktionen, OAuth-Rückrufe und andere zeitabhängige Aufrufe)
    3 Agentische Routen
    4 (niedrigste) Alle anderen Routen
  2. Rang: Innerhalb jeder Routentypgruppe ordnet das System die Routen nach ihrem Rangwert. Niedrigere numerische Werte werden zuerst geprüft.

Verwenden Sie RouteRank-Konstanten, um den Rang beim Registrieren eines Handlers festzulegen:

Konstante Wert Bedeutung
RouteRank.First 0 Ausgewertet vor allen anderen Routen in seiner Gruppe
RouteRank.Unspecified 32767 Standard, wenn kein Rang angegeben ist
RouteRank.Last 65535 Ausgewertet nach allen anderen Routen in der Gruppe

Standardmäßig endet die Verarbeitung bei der ersten passenden Route. Verwenden Sie RouteRank.Last als Auffangroute, die alles übernimmt, was nicht von einer spezifischeren Route abgedeckt wird.

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

Dialogschrittlebenszyklus-Hooks

Logik registrieren, die bei jedem Zug ausgeführt wird, vor oder nach dem Routenabgleich. Diese Hooks sind nützlich zur Protokollierung, Querschnittsthemen und Fehlerbehandlung.

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

Wenn OnBeforeTurn das Ergebnis false zurückgibt, wird der Dialogschritt abgebrochen, und es werden keine Routen ausgeführt. Wenn OnAfterTurnfalse zurückgibt, wird der Zug-Zustand nicht gespeichert.

Turn-Zustand verwenden

Der Agent lädt den Dialogschrittstatus automatisch, bevor die Handler ausgeführt werden, und speichert ihn danach. Das Turn State-Objekt, das an Ihre Handler übergeben wird, ermöglicht Ihnen den Zugriff auf die verschiedenen Umfänge, sodass Sie Daten lesen und schreiben können, die entweder über mehrere Turns hinweg bestehen bleiben oder nur für den aktuellen Turn verfügbar sind.

  • Unterhaltungsbereich: Für Daten, die über alle Dialogschritte einer Unterhaltung verfügbar sind
  • Benutzerumfang: Für Daten pro Benutzer
  • Temp. Bereich: Für Daten, die nur während des aktuellen Dialogschritts existieren müssen
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);
});

Anmerkung

Verwenden Sie MemoryStorage zur lokalen Entwicklung und Tests Für den Produktivbetrieb, insbesondere bei Bereitstellungen mit mehreren Instanzen, verwenden Sie einen persistenten Speicheranbieter wie Azure Cosmos DB oder Azure Blob Storage. Siehe Speicheranbieter in Ihrem Agent verwenden.

Nächste Schritte,