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
при загрузке страницы, если вы сами не настроите это. Если это так, то я бы посоветовал вам задать еще один вопрос о том, как это сделать, если вы не можете понять, как, поскольку это отдельная проблема из состояния диалога, сохраняющегося правильно.