【问题标题】:Dialog starts via interruption by Intents and QnaMaker Answer only when there is no active dialog仅当没有活动对话时,对话才会通过 Intents 和 QnaMaker Answer 的中断启动
【发布时间】:2019-12-21 22:54:10
【问题描述】:

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

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

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

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

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

期待:当我通过意图从中断中调用 DialogA 时。 dialogA 将被调用,如果有任何活动对话框,它将被取消。 实际结果:对话框 A 被调用,当前活动对话框结束,但 dialogA 也突然结束。(即使您还没有回答它的选择提示)。 注意: 当我通过主菜单中的选择提示调用 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 回答问题,即使有一个活跃的对话

【问题讨论】:

    标签: c# botframework azure-language-understanding qnamaker


    【解决方案1】:

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

    我通过在 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);
        }
    

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

    【讨论】:

      猜你喜欢
      • 2013-10-13
      • 2014-12-10
      • 1970-01-01
      • 2019-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多