Сохранение данных пользователя и диалогаSave user and conversation data

применимо к: Пакет SDK v4APPLIES TO: SDK v4

Бот по своей природе не учитывает состояния.A bot is inherently stateless. Развернутый бот не обязан выполнять следующий шаг в том же процессе или на том же компьютере, что и предыдущий.Once your bot is deployed, it may not run in the same process or on the same machine from one turn to the next. Но иногда боту нужно отслеживать контекст беседы, чтобы управлять ее ходом и запоминать ответы на предыдущие вопросы.However, your bot may need to track the context of a conversation so that it can manage its behavior and remember answers to previous questions. Функции состояния и хранения, предоставляемые пакетом SDK Bot Framework, позволяют реализовать в боте поддержку состояния.The state and storage features of the Bot Framework SDK allow you to add state to your bot. Для администрирования и хранения данных боты используют объекты хранилища и управления состоянием.Bots use state management and storage objects to manage and persist state. Диспетчер состояний предоставляет уровень абстракции, позволяющий пол учить доступ к свойствам состояния через методы доступа независимо от типа базового хранилища.The state manager provides an abstraction layer that lets you access state properties using property accessors, independent of the type of underlying storage.

Предварительные требованияPrerequisites

Об этом примереAbout this sample

Получив от пользователя входные данные, этот пример проверяет сохраненное состояние беседы и определяет, выводился ли этому пользователю запрос для ввода имени.Upon receiving user input, this sample checks the stored conversation state to see if this user has previously been prompted to provide their name. Если еще нет, он запрашивает имя пользователя и сохраняет ответ в состоянии пользователя.If not, the user's name is requested and that input is stored within user state. Если да, сохраненное в состоянии пользователя имя используется для взаимодействия с пользователем и обработки входных данных, а затем вместе со временем получения сообщения и идентификатором канала возвращается обратно пользователю.If so, the name stored within user state is used to converse with the user and their input data, along with the time received and input channel Id, is returned back to the user. Значения времени и идентификатора канала извлекаются из данных беседы с пользователем, а затем сохраняются в состоянии беседы.The time and channel Id values are retrieved from the user conversation data and then saved to conversation state. На следующей схеме показана связь между ботом, профилем пользователя и классами данных беседы.The following diagram shows the relationship between the bot, user profile, and conversation data classes.

Определение классовDefine classes

При настройке управления состоянием первым делом нужно определить классы, которые будут содержать все нужные сведения для управления состоянием пользователя и беседы.The first step in setting up state management is to define the classes containing the information to manage in the user and conversation state. В примере, используемом в этой статье, определяются следующие классы:The example used in this article, defines the following classes:

  • В UserProfile. CS вы определяете UserProfile класс для сведений о пользователе, которые будут собираются Bot.In UserProfile.cs, you define a UserProfile class for the user information that the bot will collect.
  • В конверсатиондата. CS вы определяете ConversationData класс для управления нашим состоянием диалога при сборе сведений о пользователе.In ConversationData.cs, you define a ConversationData class to control our conversation state while gathering user information.

В приведенных ниже примерах кода показаны определения классов UserProfile и ConversationData.The following code examples show the definitions for the UserProfile and ConversationData classes.

UserProfile.csUserProfile.cs

// Defines a state property used to track information about the user.
public class UserProfile
{
    public string Name { get; set; }
}

ConversationData.csConversationData.cs

// Defines a state property used to track conversation data.
public class ConversationData
{
    // The time-stamp of the most recent incoming message.
    public string Timestamp { get; set; }

    // The ID of the user's channel.
    public string ChannelId { get; set; }

    // Track whether we have already asked the user's name
    public bool PromptedUserForName { get; set; } = false;
}

Создание объектов состояния беседы и пользователяCreate conversation and user state objects

Далее вы регистрируетесь MemoryStorage , которая используется для создания UserState ConversationState объектов и.Next, you register MemoryStorage that is used to create UserState and ConversationState objects. В Startup создаются объекты состояния пользователя и беседы, а в конструктор бота добавляются зависимости.The user and conversation state objects are created at Startup and dependency injected into the bot constructor. Также для бота регистрируются дополнительные службы: поставщик учетных данных, адаптер и реализация бота.Other services for a bot that are registered are: a credential provider, an adapter, and the bot implementation.

Startup.cs.Startup.cs

// Create the storage we'll be using for User and Conversation state.
// (Memory is great for testing purposes - examples of implementing storage with
// Azure Blob Storage or Cosmos DB are below).
var storage = new MemoryStorage();
// Create the User state passing in the storage layer.
var userState = new UserState(storage);
services.AddSingleton(userState);

// Create the Conversation state passing in the storage layer.
var conversationState = new ConversationState(storage);
services.AddSingleton(conversationState);

Bots/StateManagementBot.csBots/StateManagementBot.cs

private BotState _conversationState;
private BotState _userState;

public StateManagementBot(ConversationState conversationState, UserState userState)
{
    _conversationState = conversationState;
    _userState = userState;
}

Добавление методов доступа к свойству состоянияAdd state property accessors

Теперь вы создаете методы доступа к свойствам с помощью CreateProperty метода, который предоставляет для BotState объекта маркер.Now you create property accessors using the CreateProperty method that provides a handle to the BotState object. Каждый метод доступа к свойству состояния позволяет получить или задать значение для соответствующего свойства состояния.Each state property accessor allows you to get or set the value of the associated state property. Прежде чем использовать свойства состояния, используйте каждый метод доступа, чтобы загрузить свойство из хранилища и получить его из кэша состояний.Before you use the state properties, use each accessor to load the property from storage and get it from the state cache. Чтобы получить ключ с правильной областью действия, связанный со свойством State, вызовите GetAsync метод.To get the properly scoped key associated with the state property, you call the GetAsync method.

Bots/StateManagementBot.csBots/StateManagementBot.cs

var conversationStateAccessors =  _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));

Доступ к состояниям из кода ботаAccess state from your bot

В предыдущем разделе мы рассматривали шаги, которые на этапе инициализации добавляют в бота методы доступа к свойствам состояния.The preceding section covers the initialization-time steps to add state property accessors to our bot. Теперь эти методы доступа можно использовать во время выполнения для чтения и записи сведений о состоянии.Now, you can use those accessors at run-time to read and write state information. Следующий пример кода использует представленный здесь поток логики:The sample code below uses the following logic flow:

  • Если userProfile.Name является пустым и Конверсатиондата. Промптедусерфорнаме имеет значение true, вы получаете указанное имя пользователя и сохраняете его в пользовательской среде.If userProfile.Name is empty and conversationData.PromptedUserForName is true, you retrieve the user name provided and store this within user state.
  • Если userProfile.Name является пустым и Конверсатиондата. Промптедусерфорнаме имеет значение false, запрашивается имя пользователя.If userProfile.Name is empty and conversationData.PromptedUserForName is false, you ask for the user's name.
  • Если userProfile.Name ранее хранился, вы получаете время сообщения и идентификатор канала из вводимых пользователем данных, выводите все данные обратно пользователю и сохраняете полученные данные в состоянии диалога.If userProfile.Name was previously stored, you retrieve message time and channel Id from the user input, echo all data back to the user, and store the retrieved data within conversation state.

Bots/StateManagementBot.csBots/StateManagementBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    // Get the state properties from the turn context.

    var conversationStateAccessors =  _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
    var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());

    var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
    var userProfile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile());

    if (string.IsNullOrEmpty(userProfile.Name))
    {
        // First time around this is set to false, so we will prompt user for name.
        if (conversationData.PromptedUserForName)
        {
            // Set the name to what the user provided.
            userProfile.Name = turnContext.Activity.Text?.Trim();

            // Acknowledge that we got their name.
            await turnContext.SendActivityAsync($"Thanks {userProfile.Name}. To see conversation data, type anything.");

            // Reset the flag to allow the bot to go through the cycle again.
            conversationData.PromptedUserForName = false;
        }
        else
        {
            // Prompt the user for their name.
            await turnContext.SendActivityAsync($"What is your name?");

            // Set the flag to true, so we don't prompt in the next turn.
            conversationData.PromptedUserForName = true;
        }
    }
    else
    {
        // Add message details to the conversation data.
        // Convert saved Timestamp to local DateTimeOffset, then to string for display.
        var messageTimeOffset = (DateTimeOffset) turnContext.Activity.Timestamp;
        var localMessageTime = messageTimeOffset.ToLocalTime();
        conversationData.Timestamp = localMessageTime.ToString();
        conversationData.ChannelId = turnContext.Activity.ChannelId.ToString();

        // Display state data.
        await turnContext.SendActivityAsync($"{userProfile.Name} sent: {turnContext.Activity.Text}");
        await turnContext.SendActivityAsync($"Message received at: {conversationData.Timestamp}");
        await turnContext.SendActivityAsync($"Message received from: {conversationData.ChannelId}");
    }
}

Перед выходом из обработчика поворачивания используйте метод SaveChangesAsync () объектов управления состоянием для записи всех изменений состояния обратно в хранилище.Before you exit the turn handler, you use the state management objects' SaveChangesAsync() method to write all state changes back to storage.

Bots/StateManagementBot.csBots/StateManagementBot.cs

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
    await base.OnTurnAsync(turnContext, cancellationToken);

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

Тестирование ботаTest the bot

Скачайте и установите последнюю версию Bot Framework Emulator.Download and install the latest Bot Framework Emulator

  1. Выполните этот пример на локальном компьютере.Run the sample locally on your machine. Инструкции см. в разделе пример README файла для C# или JS.If you need instructions, refer to the README file for C# Sample or JS Sample.
  2. Примените эмулятор для тестирования бота, как показано ниже.Use the Emulator to test the bot as shown below.

Пример тестирования бота с управлением состояния

Дополнительные ресурсыAdditional resources

Конфиденциальность. Если вы собираетесь хранить персональные данные пользователя, обеспечьте соблюдение Общего регламента по защите данных.Privacy: If you intend to store user's personal data, you should ensure compliance with General Data Protection Regulation.

Управление состоянием. Все вызовы методов управления состоянием обрабатываются асинхронно, и по умолчанию применяется только последнее действие, выполняющее запись данных.State management: All of the state management calls are asynchronous, and last-writer-wins by default. На практике следует размещать методы get, set и save state как можно ближе друг к другу в коде бота.In practice, you should get, set, and save state as close together in your bot as possible.

Критически важные бизнес-данные. Используйте состояние бота для хранения настроек, имени пользователя или сведений о последнем заказе, но не используйте его для хранения критически важных бизнес-данных.Critical business data: Use bot state to store preferences, user name, or the last thing they ordered, but do not use it to store critical business data. Для критически важных данных создайте собственные компоненты хранилища или записывайте их непосредственно в хранилище.For critical data, create your own storage components or write directly to storage.

Recognizer-Text. В этом примере используются библиотеки Microsoft/Recognizer-Text для синтаксического анализа и проверки пользовательского ввода.Recognizer-Text: The sample uses the Microsoft/Recognizers-Text libraries to parse and validate user input. Дополнительные сведения см. на странице Использование Azure DNS для частных доменов.For more information, see the overview page.

Дальнейшие действияNext steps

Теперь вы умеете настраивать состояние, которое упрощает чтение и запись данных бота в хранилище. Давайте перейдем к изучению методов, позволяющих задать пользователю ряд вопросов, проверить его ответы и сохранить полученные данные.Now that you know how to configure state to help you read and write bot data to storage, let's learn how ask the user a series of questions, validate their answers, and save their input.