AgentApplication dans Microsoft 365 Agents SDK

AgentApplication est le bloc de construction central d’un agent créé avec le Kit de développement logiciel (SDK) Agents. AgentApplication est le point d’entrée de toutes les activités entrantes, y compris les messages des utilisateurs, les événements de cycle de vie des conversations, les interactions de carte adaptive, les rappels OAuth.

Un agent est, en son cœur, un AgentApplication. Vous la configurez avec des gestionnaires qui décrivent ce que fait votre agent. Le Kit de développement logiciel (SDK) s’occupe du routage, de la gestion de l’état et de l’infrastructure requise pour l’exécuter.

Fonctionnement de AgentApplication

Chaque agent a un cycle de vie qui commence lorsqu'un canal (Microsoft Teams, un service bot ou un client personnalisé) fournit une activité au point de terminaison de votre agent. AgentApplication se trouve au centre de ce cycle de vie :

Channel → Hosting layer → AgentApplication → Your handlers

Les couches de traitement dans un agent généré avec le Kit de développement logiciel (SDK) Agents fonctionnent comme suit :

  1. La couche d’hébergement reçoit la requête HTTP et l’authentifie.
  2. AgentApplication traite l’activité entrante via son pipeline.
  3. Vos gestionnaires sont appelés en fonction des itinéraires correspondants.

Votre agent charge l’état de tour avant l’exécution de vos gestionnaires. Ensuite, l’agent enregistre l’état du tour.

Concepts de base

Activités

Tout dans le SDK Agents fonctionne comme une activité. Une activité est un message structuré représentant quelque chose qui s’est produit. Une activité a un type, tel que message, événement, appel, conversationUpdate, etc. Il porte une charge utile pertinente pour ce type. AgentApplication reçoit des activités et les route vers le gestionnaire approprié.

Routes

Un itinéraire associe un sélecteur à un gestionnaire. Le sélecteur détermine si un itinéraire correspond à l’activité actuelle. Le gestionnaire exécute votre logique lorsque l’itinéraire correspond.

Inscrivez des itinéraires lorsque vous configurez votre agent. Ils peuvent correspondre :

  • Message contenant du texte spécifique ou correspondant à une expression régulière
  • Toute activité d’un type donné
  • Événements de cycle de vie des conversations (membre ajouté, membre supprimé)
  • Actions adaptatives de la carte
  • Conditions personnalisées

Lorsqu’une activité arrive, le système évalue les itinéraires dans l’ordre jusqu’à ce qu’il trouve une correspondance. Par défaut, une seule route s’exécute.

État de tour

AgentApplication gère l'état des tours : stockage structuré partitionné en contextes :

Type d'étendue Description
Conversation Partagé entre tous les utilisateurs dans une conversation, conservé entre les intervalles
Utilisateur Limité à un utilisateur dans toutes les conversations
Temp Tour actuel uniquement - jamais conservé

Le système charge automatiquement l’état avant que vos gestionnaires ne s'exécutent et l’enregistre automatiquement par la suite.

Changer de contexte

Lorsqu’un gestionnaire s’exécute, il reçoit un contexte de tour. Le contexte de tour est un instantané de l’activité actuelle, de la connexion de l’adaptateur et des utilitaires pour l’envoi de réponses. Le contexte de tour est votre interface vers l’interaction actuelle.

Intergiciel

AgentApplication prend en charge un pipeline d’intergiciels. Middleware est une chaîne de composants qui traitent chaque tour avant et après l’exécution de vos gestionnaires. L’intergiciel peut inspecter, transformer ou court-circuiter le flux d’activité. Les utilisations courantes incluent la journalisation, les vérifications d’authentification et la normalisation des demandes.

Créer un agent

Créer une sous-classe de AgentApplication et enregistrez vos gestionnaires dans le constructeur. L’infrastructure d’hébergement injecte AgentApplicationOptionsautomatiquement .

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

Inscrivez votre agent dans 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();

Inscrire des gestionnaires d’activités

Gérer les messages

Faire correspondre les messages par texte identique (insensible à la casse) :

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

Mettre en correspondance les messages à l’aide d’une expression régulière :

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

Gérer les mises à jour de conversation

Inscrivez des gestionnaires pour les événements de cycle de vie des conversations, tels que les membres qui rejoignent ou quittent.

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

Gérer n’importe quel type d’activité

Faire correspondre toute activité par son type pour un contrôle total du routage.

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

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

Utilisez ActivityTypes des constantes au lieu de chaînes codées en dur.

Contrôle de l’ordre d’évaluation de l’itinéraire

Le système trie les itinéraires dans un ordre d’évaluation fixe lorsque vous les inscrivez, et non au moment de l’exécution. Le tri utilise deux niveaux :

  1. Type de route : le système regroupe les itinéraires par type et évalue toujours les types de priorité supérieure avant les types de priorité inférieure, quel que soit le rang :

    Priorité Type d’itinéraire
    1 (plus élevé) Itinéraires d’invocation par agents
    2 Appeler des itinéraires (actions de carte adaptative, rappels OAuth et autres appels sensibles au temps)
    3 Itinéraires agentiques
    4 (plus bas) Tous les autres itinéraires
  2. Classement : dans chaque groupe de types de routage, le système commande les itinéraires par leur valeur de classement. Les valeurs numériques inférieures sont évaluées en premier.

Utilisez RouteRank des constantes pour définir le classement lors de l’inscription d’un gestionnaire :

Constante Value Signification
RouteRank.First 0 Évalué avant tous les autres itinéraires de son groupe
RouteRank.Unspecified 32767 Valeur par défaut lorsqu’aucun classement n’est spécifié
RouteRank.Last 65535 Évalué après tous les autres itinéraires de son groupe

Par défaut, l’évaluation s’arrête au premier itinéraire correspondant. Utilisez RouteRank.Last pour un secours catch-all qui gère tout ce qui n’est pas mis en correspondance par un itinéraire plus spécifique.

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

Activer les hooks de cycle de vie

Définissez la logique qui s’exécute à chaque itération, avant ou après la correspondance de routage. Ces hooks sont utiles pour la journalisation, les préoccupations transversales et la gestion des erreurs.

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

Lorsque OnBeforeTurn retourne false, le tour est annulé et aucun itinéraire ne s'exécute. Lorsque OnAfterTurn retourne false, l’état de tour n’est pas enregistré.

Utiliser l'état de cycle

L’agent charge automatiquement l’état de tour avant l’exécution de vos gestionnaires et l’enregistre ensuite. L’objet d’état de tour transmis à vos gestionnaires vous permet d’accéder aux différentes étendues afin de pouvoir lire et écrire des données qui persistent à travers les tours ou qui sont éphémères pour le tour actuel :

  • Étendue de la conversation : Pour les données partagées dans tous les tours d'une conversation
  • Étendue de l’utilisateur : pour les données par utilisateur
  • Étendue temporaire : Pour les données qui doivent exister uniquement pendant le tour actuel
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

Utiliser MemoryStorage pour le développement et les tests locaux. Pour les déploiements de production, en particulier les déploiements s’exécutant sur plusieurs instances, utilisez un fournisseur de stockage persistant tel que Azure Cosmos DB ou Stockage Blob Azure. Consultez Utiliser des fournisseurs de stockage dans votre agent.

Étapes suivantes