仅当没有活动对话框时,对话框才会被Intents和QnaMaker Answer中断打断。

时间:2019-08-15 13:49:28

标签: c# botframework luis qnamaker

我不知道我能否很好地解释这一点,但请忍受。下面有代码和图片。

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

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

没有错误,但机器人行为不正常。

期望::当用户第一次与“入门”互动时,将调用主菜单,因为我在主菜单的意图中添加了“入门”。 实际结果:主菜单被调用两次。

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

期望::当没有活动对话框时(如果您取消对话框,则为例),用户可以问一个问题,然后漫游器会在QnaMaker中检查答案。 实际结果:机器人会回答问题,然后启动主菜单。即使有活动对话框,该机器人仍会回答问题。

以下是代码:

DialogBot:

    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;
        }

    }
}

MainDialog

    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时,它将正常启动 enter image description here

dialogA突然通过意图中断被调用。 enter image description here

即使有活动对话框,qna制造商也会提出问题 enter image description here

1 个答案:

答案 0 :(得分:1)

你好,我设法解决了我的问题。供我将来参考,这是我如何修复它。

通过在cancelandhelpdialog中添加此行,我通过意图中断解决了对话框的运行问题

  

返回新的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);
    }

希望这对某人有帮助。