仅当没有活动对话时,对话通过 Intents 和 QnaMaker Answer 中断启动

Dialog starts via interruption by Intents and QnaMaker Answer only when there is no active dialog

我不知道我是否能解释清楚,但请耐心等待。下面有代码和图片。

所以我正在将我的代码从 Bot Framework V4 的第一个版本迁移到最新版本。

我正在尝试为可以调用其他对话并随时取消当前对话的机器人建立基础。并在没有活动对话框时使用 QnAMaker 回答问题。

没有错误,但机器人未按预期运行。

预期: 当用户第一次与 "Get Started" 交互时,将调用主菜单,因为我在主菜单的意图中添加了 "Get started"。 实际结果:主菜单被调用两次。

预期: 当我通过意图从中断中调用 DialogA 时。 dialogA 将被调用,如果有任何活动对话框,它将被取消。 实际结果:调用对话框 A 并结束当前活动对话框,但对话框 A 也突然结束。(即使您尚未回答其选择提示)。 注意: 当我通过主菜单中的选择提示调用 dialogA 时。对话框 A 正常开始而不是结束。

预期: 当没有活动对话框时(例如,如果您取消对话框)用户可以提出问题,机器人会在 QnaMaker 中检查答案。 实际结果: 机器人回答问题然后启动主菜单。甚至当有活动对话时,机器人仍然会回答问题。

代码如下:

对话机器人:

    namespace SabikoBotV2.Bots
{
    public class DialogBot<T> : ActivityHandler
        where T : Dialog
    {
        public readonly IStatePropertyAccessor<DialogState> _dialogAccessor;
        protected readonly Dialog Dialog;
        protected readonly BotState ConversationState;
        protected readonly BotState UserState;
        protected readonly ILogger Logger;
        private readonly IBotServices BotServices;

        private DialogSet Dialogs { get; set; }

        public DialogBot(IBotServices botServices, ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
        {
            ConversationState = conversationState;
            UserState = userState;
            Dialog = dialog;
            Logger = logger;
            BotServices = botServices;
            Dialogs = new DialogSet(conversationState.CreateProperty<DialogState>(nameof(DialogBot<T>)));
            RegisterDialogs(Dialogs);
        }

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

            await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
            await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
        }

        protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            Logger.LogInformation("Running dialog with Message Activity.");

            string text = string.IsNullOrEmpty(turnContext.Activity.Text) ? string.Empty : turnContext.Activity.Text.ToLower();

            string topIntent = string.Empty;
            RecognizerResult luisRecognizerResult = null;

            string topDispatch = string.Empty;
            RecognizerResult dispatchRecognizerResult = null;

            if (!string.IsNullOrEmpty(text))
            {
                dispatchRecognizerResult = await BotServices.DispatchService.RecognizeAsync(turnContext, cancellationToken);
                var topScoringDispatch = dispatchRecognizerResult?.GetTopScoringIntent();
                topDispatch = topScoringDispatch.Value.intent;

                luisRecognizerResult = await BotServices.LuisService.RecognizeAsync(turnContext, cancellationToken);
                var topScoringIntent = luisRecognizerResult?.GetTopScoringIntent();
                topIntent = topScoringIntent.Value.intent;

                turnContext.TurnState.Add("topDispatch", topDispatch);
                turnContext.TurnState.Add("dispatchRecognizerResult", dispatchRecognizerResult);
                turnContext.TurnState.Add("botServices", BotServices);
                turnContext.TurnState.Add("topIntent", topIntent);
            }

            var dc = await Dialogs.CreateContextAsync(turnContext, cancellationToken);
            var dialogResult = await dc.ContinueDialogAsync();

            if (!dc.Context.Responded)
            {
                switch (dialogResult.Status)
                {
                    case DialogTurnStatus.Empty:
                        await DispatchToTopIntentAsync(turnContext, topDispatch, dispatchRecognizerResult, cancellationToken);
                        break;

                    case DialogTurnStatus.Waiting:
                        break;

                    case DialogTurnStatus.Complete:
                        await dc.EndDialogAsync();
                        break;

                    default:
                        await dc.CancelAllDialogsAsync();
                        break;
                }
            }

            await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
        }

        private void RegisterDialogs(DialogSet dialogs)
        {
            dialogs.Add(new MainDialog());
            dialogs.Add(new DialogA());
            dialogs.Add(new DialogB());
        }

        private async Task DispatchToTopIntentAsync(ITurnContext turnContext, string intent, RecognizerResult recognizerResult, CancellationToken cancellationToken)
        {
            switch (intent)
            {
                case QnAModel:
                    await DispatchToQnAMakerAsync(turnContext, cancellationToken);
                    break;
            }
        }

        private async Task DispatchToQnAMakerAsync(ITurnContext turnContext, CancellationToken cancellationToken)
        {
            if (!string.IsNullOrEmpty(turnContext.Activity.Text))
            {
                var results = await BotServices.QnaService.GetAnswersAsync(turnContext);
                if (results.Any())
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text(results.First().Answer), cancellationToken);
                }
                else
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, could not find an answer in the Q and A system."), cancellationToken);
                }
            }
        }

    }
}

启动

    namespace SabikoBotV2
{
    public class Startup
    {
        public Startup()
        {

        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

            services.AddSingleton<IStorage, MemoryStorage>();

            services.AddSingleton<UserState>();

            services.AddSingleton<ConversationState>();

            services.AddSingleton<IBotServices, BotServices>();

            services.AddTransient<MainDialog>();
            services.AddTransient<DialogA>();
            services.AddTransient<DialogB>();

            services.AddTransient<IBot, DialogBot<MainDialog>>();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseMvc();
        }
    }
}

CancelAndHelpDialog

    namespace SabikoBotV2.DialogsV2
{
    public class CancelAndHelpDialog : ComponentDialog
    {
        public CancelAndHelpDialog(string id)
            : base(id)
        {
        }

        protected override async Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default(CancellationToken))
        {
            var result = await IsTurnInterruptedAsyncHelpAndCancel(innerDc, cancellationToken);
            if (result != null)
            {
                return result;
            }

            return await base.OnBeginDialogAsync(innerDc, options, cancellationToken);
        }

        protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
        {
            var result = await IsTurnInterruptedAsyncHelpAndCancel(innerDc, cancellationToken);
            if (result != null)
            {
                return result;
            }

            return await base.OnContinueDialogAsync(innerDc, cancellationToken);
        }

        protected virtual async Task<DialogTurnResult> IsTurnInterruptedAsyncHelpAndCancel(DialogContext innerDc, CancellationToken cancellationToken)
        {
            var topIntent = innerDc.Context.TurnState.Get<string>("topIntent");
            var text = innerDc.Context.TurnState.Get<string>("text");

            if (topIntent.Equals("Cancel"))
            {
                if (innerDc.ActiveDialog != null)
                {
                    await innerDc.CancelAllDialogsAsync();
                    await innerDc.Context.SendActivityAsync(" Ok. I've cancelled our last activity.");
                }
                else
                {
                    await innerDc.Context.SendActivityAsync("I don't have anything to cancel.");
                }

            }

            if (topIntent.Equals("Help"))
            {
                await innerDc.Context.SendActivityAsync("Let me help you");

                if (innerDc.ActiveDialog != null)
                {
                    await innerDc.RepromptDialogAsync();
                }
            }

            if (topIntent.Equals("MainDialog"))
            {
                if (innerDc.ActiveDialog != null)
                {
                    await innerDc.CancelAllDialogsAsync();
                    await innerDc.BeginDialogAsync(nameof(MainDialog));
                }
                else
                {
                    await innerDc.BeginDialogAsync(nameof(MainDialog));
                }
            }

            if (topIntent.Equals("DialogA"))
            {
                if (innerDc.ActiveDialog != null)
                {
                    await innerDc.CancelAllDialogsAsync();
                    await innerDc.BeginDialogAsync(nameof(DialogA));
                }
                else
                {
                    await innerDc.BeginDialogAsync(nameof(DialogA));
                }
            }

            if (topIntent.Equals("DialogB"))
            {
                if (innerDc.ActiveDialog != null)
                {
                    await innerDc.CancelAllDialogsAsync();
                    await innerDc.BeginDialogAsync(nameof(DialogB));
                }
                else
                {
                    await innerDc.BeginDialogAsync(nameof(DialogB));
                }
            }

            return null;
        }

    }
}

主对话框

    namespace SabikoBotV2.Dialogs
{
    public class MainDialog : CancelAndHelpDialog
    {
        private const string InitialId = nameof(MainDialog);
        public MainDialog()
            : base(nameof(MainDialog))
        {
            InitialDialogId = InitialId;
            WaterfallStep[] waterfallSteps = new WaterfallStep[]
             {
                 FirstStepAsync,
                 SecondStepAsync,
                 ThirdStepAsync,

             };
            AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
            AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new DialogA());
            AddDialog(new DialogB());
        }


        private static async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            await stepContext.Context.SendActivityAsync("Start of Main");
            return await stepContext.PromptAsync(
             nameof(ChoicePrompt),
             new PromptOptions
             {
                 Prompt = MessageFactory.Text($"What do you want to do next?"),
                 Choices = new List<Choice>
                 {
                        new Choice
                        {
                            Value = "choice1",
                        },
                        new Choice
                        {
                            Value = "choice2",
                        },
                 },
                 RetryPrompt = MessageFactory.Text($"Please choose one of the options."),
             });
        }

        private static async Task<DialogTurnResult> SecondStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            switch ((stepContext.Result as FoundChoice).Value.ToString().ToLower())
            {
                case "choice1":
                    return await stepContext.BeginDialogAsync(nameof(DialogA));

                case "choice2":
                    return await stepContext.BeginDialogAsync(nameof(DialogB));

                default:
                    return await stepContext.ReplaceDialogAsync(nameof(MainDialog));
            }
        }

        private static async Task<DialogTurnResult> ThirdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            await stepContext.Context.SendActivityAsync("End of Main");
            return await stepContext.EndDialogAsync();
        }
    }
}

DialogA 和 DialogB 与 maindialog 相同,但它们继承了 ComponentDialog 而不是 CancelAndHelpDialog。

部分截图供参考:

当通过选择主菜单调用 dialogA 时,它会正常启动

dialogA 是通过意图中断调用的,它突然结束。

qna maker 回答问题,即使有一个活跃的对话

您好,我设法解决了我的问题。为了将来其他人的参考,我是如何修复它的。

我通过在 cancelandhelpdialog

中添加这一行,通过意图中断修复了对话框 运行

return new DialogTurnResult(DialogTurnStatus.Waiting);

            if (topIntent.Equals("MainDialog"))
        {
            if (innerDc.ActiveDialog != null)
            {
                await innerDc.CancelAllDialogsAsync();
                await innerDc.BeginDialogAsync(nameof(MainDialog));
            }
            else
            {
                await innerDc.BeginDialogAsync(nameof(MainDialog));
            }

            return new DialogTurnResult(DialogTurnStatus.Waiting);
        }

并且 QnaMaker 仅在没有活动对话时通过在 OnTurnAsync 中执行此操作来回答:

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

        var topDispatch = turnContext.TurnState.Get<string>("topDispatch");
        var dispatchRecognizerResult = turnContext.TurnState.Get<RecognizerResult>("dispatchRecognizerResult");
        var dc = await Dialogs.CreateContextAsync(turnContext, cancellationToken);
        var dialogResult = await dc.ContinueDialogAsync();

        if (!dc.Context.Responded)
        {
            switch (dialogResult.Status)
            {
                case DialogTurnStatus.Empty:
                    await DispatchToTopIntentAsync(turnContext, topDispatch, dispatchRecognizerResult, cancellationToken);
                    break;

                case DialogTurnStatus.Waiting:
                    break;

                case DialogTurnStatus.Complete:
                    await dc.EndDialogAsync();
                    break;

                default:
                    await dc.CancelAllDialogsAsync();
                    break;
            }
        }

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

希望这会对某人有所帮助。