Microsoft Bot Framework начинает диалог с определенного шага водопада в диалоге

Microsoft Bot Framework V4, у меня есть диалог водопада, определенный в диалоговом окне, как показано ниже

var waterfallSteps = new WaterfallStep[]

        {
            CallConfirmAsync,
            SimilarProductAsync,
            CheckNewVersionAsync,
        };

        AddDialog(new WaterfallDialog("productenquiry", waterfallSteps));

После выполнения первых двух шагов водопада мой разговор останавливается из-за неотзывчивости со стороны пользователя. Поэтому я хочу вернуться с третьего метода, когда, например, CheckNewVersionAsync, когда пользователь снова возвращается к боту.

Может кто-нибудь, пожалуйста, помогите мне здесь.

2 ответа

Решение

Изменить: Дрю ответ правильный, но мой предоставляет другое потенциальное решение. Вы можете найти больше информации здесь: Управление государством. Особенно:

Состояние пользователя доступно в любой момент, когда бот общается с этим пользователем на этом канале, независимо от состояния разговора. Состояние диалога доступно в любой момент в определенном сеансе, независимо от пользователя (т. Е. Групповые разговоры). конкретный разговор и с этим конкретным пользователем

Совет

Как пользователь, так и состояние разговора ограничены каналом. Один и тот же человек, использующий разные каналы для доступа к вашему боту, выглядит как разные пользователи, по одному на каждый канал, и у каждого из них свое состояние пользователя.

Дополнительное решение

Это решение лучше всего подходит, если вы можете указать from Id, но не может гарантировать, что conversation Id остается тем же самым (см. ниже, под Gotchas).

Вы можете сохранить, на каком шаге находится пользователь в своем UserState,

BasicBot

BasicBot делает это со своим GreetingState учебный класс.

Из своего GreetingDialog:

На первом этапе он инициализирует GreetingState, который отслеживает, как далеко в диалоге находится пользователь, видя, какие пользовательские переменные уже установлены:

private async Task<DialogTurnResult> InitializeStateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
    if (greetingState == null)
    {
        var greetingStateOpt = stepContext.Options as GreetingState;
        if (greetingStateOpt != null)
        {
            await UserProfileAccessor.SetAsync(stepContext.Context, greetingStateOpt);
        }
        else
        {
            await UserProfileAccessor.SetAsync(stepContext.Context, new GreetingState());
        }
    }

    return await stepContext.NextAsync();
}

И затем на каждом этапе он загружает GreetingState:

var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context);

И проверяет, был ли шаг уже выполнен с чем-то вроде:

if (greetingState != null && !string.IsNullOrWhiteSpace(greetingState.Name) && !string.IsNullOrWhiteSpace(greetingState.City))

Если нет greetingState или же .Name или же .City существует, он запрашивает их, и если они уже заполнены, он переходит к:

return await stepContext.NextAsync();

На каждом этапе это сохраняет в GreetingState с чем-то вроде:

greetingState.Name = char.ToUpper(lowerCaseName[0]) + lowerCaseName.Substring(1);
await UserProfileAccessor.SetAsync(stepContext.Context, greetingState);

Упрощение для вашего случая использования

Для вас, если вам не нужно сохранять информацию пользователя, вы можете создать простой Step учебный класс:

{
    /// <summary>
    /// User state properties for Waterfall Step.
    /// </summary>
    public class Step
    {
        public string StepNumber { get; set; }
    }
}

Сделайте первый шаг вашего WaterfallDialog:

private async Task<DialogTurnResult> InitializeStateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var StepState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
    if (StepState == null)
    {
        var StepStateOpt = stepContext.Options as StepState;
        if (StepStateOpt != null)
        {
            await UserProfileAccessor.SetAsync(stepContext.Context, StepStateOpt );
        }
        else
        {
            await UserProfileAccessor.SetAsync(stepContext.Context, new StepState());
        }
    }

    return await stepContext.NextAsync();
}

На каждом шаге загружать текущий Step:

var stepState = await UserProfileAccessor.GetAsync(stepContext.Context);

Проверьте, прошли ли они уже текущий шаг:

if (stepState.StepNumber <= 2)
{
    // ...do stuff
    // Save that user has completed step
    stepState.StepNumber++;
    await UserProfileAccessor.SetAsync(stepContext.Context, stepState);
}
else
{
    return await stepContext.NextAsync();
}

Gotchas

Пара больших вещей, которые нужно остерегаться:

  • UserState сохраняется только для того же from ID а также channel ID, Убедитесь, что пользователь, который уходит посреди водопада, имеет from ID когда они повторно вводят это и что они повторно вводят это от того же самого канала. Это не по умолчанию для эмулятора - в эмуляторе, когда сеанс перезапускается, новый from ID создано. (Примечание: рассмотрим from ID быть синонимом User ID, Это просто приходит из Activity.From.Id)

  • ConversationState сохраняется только для того же conversation ID а также channel ID, Настойчивость conversation ID в пределах канала зависит от канала.

Подробнее о различных идентификаторах: поля идентификаторов в Bot Framework.

Итак, на уровне ботов это должно происходить автоматически, если вы настроили IStatePropertyAccessor<DialogState> с ConversationState, Независимо от того, сколько времени потребуется пользователю для ответа, ваш WaterfallDialog останется на вершине стека, и он будет точно помнить, на каком шаге он был. Предполагая, что ваш пользователь возвращается к тому же разговору, он будет продолжать с того места, где остановился.

Учитывая это, тот факт, что вы задаете этот вопрос, заставляет меня поверить, что, возможно, вы используете WebChat, который не поддерживает то же самое conversationId при загрузке страницы, если вы сами не настроите это. Если это так, то я бы посоветовал вам задать еще один вопрос о том, как это сделать, если вы не можете понять, как, поскольку это отдельная проблема из состояния диалога, сохраняющегося правильно.

Другие вопросы по тегам