Sdílet prostřednictvím


Vypršení platnosti konverzace

PLATÍ PRO: SDK v4

Robot někdy potřebuje restartovat konverzaci od začátku. Pokud například uživatel po určité době nereaguje. Tento článek popisuje dvě metody vypršení platnosti konverzace:

  • Sledujte čas posledního přijetí zprávy od uživatele a vymažte stav, pokud je čas větší než předkonfigurovaná délka při přijetí další zprávy od uživatele. Další informace najdete v části Vypršení platnosti interakce uživatele.
  • Pomocí funkce vrstvy úložiště, například TTL (Time To Live) služby Cosmos DB, můžete automaticky vymazat stav po předkonfigurované době. Další informace najdete v části Vypršení platnosti úložiště.

Poznámka:

Sady SDK služby Bot Framework JavaScript, C# a Python budou nadále podporovány, ale sada Java SDK se vyřazuje s konečnou dlouhodobou podporou končící v listopadu 2023.

Stávající roboti sestavení pomocí sady Java SDK budou i nadále fungovat.

Při vytváření nových robotů zvažte použití aplikace Microsoft Copilot Studio a přečtěte si o výběru správného řešení copilotu.

Další informace najdete v tématu Budoucnost vytváření robotů.

Požadavky

O této ukázce

Vzorový kód v tomto článku začíná strukturou vícenásobného robota a rozšiřuje funkčnost robota přidáním dalšího kódu (viz následující části). Tento rozšířený kód ukazuje, jak vymazat stav konverzace po uplynutí určitého časového období.

Vypršení platnosti interakce uživatele

Tento typ konverzace končící platností se provádí přidáním vlastnosti času posledního přístupu do stavu konverzace chatbota. Tato hodnota vlastnosti se pak porovná s aktuálním časem v obslužné rutině aktivity před zpracováním aktivit.

Poznámka:

Tento příklad používá 30sekundový časový limit pro snadné testování tohoto modelu.

appsettings.json

Nejprve přidejte ExpireAfterSeconds nastavení pro appsettings.json:

{
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "ExpireAfterSeconds": 30
}

Roboti\DialogBot.cs

Dále přidejte ExpireAfterSecondsdo třídy robota pole a LastAccessedTimePropertyDialogStateProperty inicializujete je v konstruktoru robota. IConfiguration Přidejte také parametr do konstruktoru, se kterým chcete načíst ExpireAfterSeconds hodnotu.

Místo vytvoření přístupového objektu vlastnosti stavu dialogového okna v OnMessageActivityAsync metodě vytváříte a nahráváte ho při inicializaci. Robot bude potřebovat přístup k vlastnosti stavu nejen ke spuštění dialogového okna, ale také k vymazání stavu dialogového okna.

protected readonly int ExpireAfterSeconds;
protected readonly IStatePropertyAccessor<DateTime> LastAccessedTimeProperty;
protected readonly IStatePropertyAccessor<DialogState> DialogStateProperty;

// Existing fields omitted...

public DialogBot(IConfiguration configuration, ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
{
    ConversationState = conversationState;
    UserState = userState;
    Dialog = dialog;
    Logger = logger;

    ExpireAfterSeconds = configuration.GetValue<int>("ExpireAfterSeconds");
    DialogStateProperty = ConversationState.CreateProperty<DialogState>(nameof(DialogState));
    LastAccessedTimeProperty = ConversationState.CreateProperty<DateTime>(nameof(LastAccessedTimeProperty));
}

Nakonec do metody robota OnTurnAsync přidejte kód, který vymaže stav dialogového okna, pokud je konverzace příliš stará.

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
    // Retrieve the property value, and compare it to the current time.
    var lastAccess = await LastAccessedTimeProperty.GetAsync(turnContext, () => DateTime.UtcNow, cancellationToken).ConfigureAwait(false);
    if ((DateTime.UtcNow - lastAccess) >= TimeSpan.FromSeconds(ExpireAfterSeconds))
    {
        // Notify the user that the conversation is being restarted.
        await turnContext.SendActivityAsync("Welcome back!  Let's start over from the beginning.").ConfigureAwait(false);

        // Clear state.
        await ConversationState.ClearStateAsync(turnContext, cancellationToken).ConfigureAwait(false);
    }

    await base.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false);

    // Set LastAccessedTime to the current time.
    await LastAccessedTimeProperty.SetAsync(turnContext, DateTime.UtcNow, cancellationToken).ConfigureAwait(false);

    // Save any state changes that might have occurred during the turn.
    await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false);
    await UserState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false);
}

Vypršení platnosti úložiště

Cosmos DB poskytuje funkci TTL (Time To Live), která umožňuje automaticky odstraňovat položky z kontejneru po určitém časovém období. To je možné nakonfigurovat z webu Azure Portal nebo během vytváření kontejneru (pomocí sad SDK služby Cosmos DB specifických pro jednotlivé jazyky).

Sada SDK služby Bot Framework nezpřístupňuje nastavení konfigurace TTL. Inicializace kontejnerů se ale dá přepsat a sadu SDK služby Cosmos DB je možné použít ke konfiguraci hodnoty TTL před inicializací úložiště služby Bot Framework.

Začněte novou kopií ukázky vícenásobných výzev a přidejte do Microsoft.Bot.Builder.Azure projektu balíček NuGet.

appsettings.json

Aktualizujte appsettings.json tak, aby zahrnovaly možnosti úložiště Cosmos DB:

{
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",

  "CosmosDbTimeToLive": 30,
  "CosmosDbEndpoint": "<endpoint-for-your-cosmosdb-instance>",
  "CosmosDbAuthKey": "<your-cosmosdb-auth-key>",
  "CosmosDbDatabaseId": "<your-database-id>",
  "CosmosDbUserStateContainerId": "<no-ttl-container-id>",
  "CosmosDbConversationStateContainerId": "<ttl-container-id>"
}

Všimněte si dvou ID kontejneru, jedné pro UserState a druhé pro ConversationState. Výchozí hodnota TTL je nastavena v kontejneru ConversationState , ale ne v UserState.

CosmosDbStorageInitializerHostedService.cs

Dále vytvořte CosmosDbStorageInitializerHostedService třídu, která vytvoří kontejner s nakonfigurovaným časem Time To Live.

// Add required using statements...

public class CosmosDbStorageInitializerHostedService : IHostedService
{
    readonly CosmosDbPartitionedStorageOptions _storageOptions;
    readonly int _cosmosDbTimeToLive;

    public CosmosDbStorageInitializerHostedService(IConfiguration config)
    {
        _storageOptions = new CosmosDbPartitionedStorageOptions()
        {
            CosmosDbEndpoint = config["CosmosDbEndpoint"],
            AuthKey = config["CosmosDbAuthKey"],
            DatabaseId = config["CosmosDbDatabaseId"],
            ContainerId = config["CosmosDbConversationStateContainerId"]
        };

        _cosmosDbTimeToLive = config.GetValue<int>("CosmosDbTimeToLive");
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        using (var client = new CosmosClient(
            _storageOptions.CosmosDbEndpoint,
            _storageOptions.AuthKey,
            _storageOptions.CosmosClientOptions ?? new CosmosClientOptions()))
        {
            // Create the contaier with the provided TTL
            var containerResponse = await client
                .GetDatabase(_storageOptions.DatabaseId)
                .DefineContainer(_storageOptions.ContainerId, "/id")
                .WithDefaultTimeToLive(_cosmosDbTimeToLive)
                .WithIndexingPolicy().WithAutomaticIndexing(false).Attach()
                .CreateIfNotExistsAsync(_storageOptions.ContainerThroughput)
                .ConfigureAwait(false);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

Startup.cs

Nakonec aktualizujte Startup.cs inicializátor úložiště a Cosmos DB pro stav:

// Existing code omitted...

// commented out MemoryStorage, since we are using CosmosDbPartitionedStorage instead
// services.AddSingleton<IStorage, MemoryStorage>();

// Add the Initializer as a HostedService (so it's called during the app service startup)
services.AddHostedService<CosmosDbStorageInitializerHostedService>();

// Create the storage options for User state
var userStorageOptions = new CosmosDbPartitionedStorageOptions()
{
    CosmosDbEndpoint = Configuration["CosmosDbEndpoint"],
    AuthKey = Configuration["CosmosDbAuthKey"],
    DatabaseId = Configuration["CosmosDbDatabaseId"],
    ContainerId = Configuration["CosmosDbUserStateContainerId"]
};

// Create the User state. (Used in this bot's Dialog implementation.)
services.AddSingleton(new UserState(new CosmosDbPartitionedStorage(userStorageOptions)));

// Create the storage options for Conversation state
var conversationStorageOptions = new CosmosDbPartitionedStorageOptions()
{
    CosmosDbEndpoint = Configuration["CosmosDbEndpoint"],
    AuthKey = Configuration["CosmosDbAuthKey"],
    DatabaseId = Configuration["CosmosDbDatabaseId"],
    ContainerId = Configuration["CosmosDbConversationStateContainerId"]
};

// Create the Conversation state. (Used by the Dialog system itself.)
services.AddSingleton(new ConversationState(new CosmosDbPartitionedStorage(conversationStorageOptions)));

// Existing code omitted...

Cosmos DB teď po 30 sekundách nečinnosti automaticky odstraní záznamy o stavu konverzací.

Další informace najdete v tématu Konfigurace času naživo ve službě Azure Cosmos DB.

Otestování robota

  1. Pokud jste to ještě neudělali, nainstalujte bot Framework Emulator.
  2. Spusťte ukázku místně na svém počítači.
  3. Spusťte emulátor, připojte se k robotovi a odešlete mu zprávu.
  4. Po jedné z výzev počkejte 30 sekund, než odpovíte.