Azure Bot Framework Bot с диалогами LUIS и WaterFall работает ненормально.Неожиданный диалог - PullRequest
6 голосов
/ 19 июня 2019

Я работаю над тем, чтобы взломать пример кода GitHub 'CoreBot' для Bot Framework V4 (с LUIS) для моих собственных целей, и столкнулся с проблемой при выполнении шагов отклика и диалогового окна водопада.

У меня есть диалог верхнего уровня. Я ожидаю, что этот диалог делает первоначальный вызов LUIS на основе ввода и маршрутизирует в различные диалоги на основе этого ввода. На данный момент есть только возможность поприветствовать бота и сообщить об опасности. Мои настройки диалога такие же (игнорируйте BookingDialog, это часть образца).

public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger)
    : base(nameof(MainDialog))
{
    Configuration = configuration;
    Logger = logger;

    AddDialog(new TextPrompt(nameof(TextPrompt)));
    AddDialog(new BookingDialog());
    AddDialog(new HazardDialog());
    AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
    {
        MainStepAsync,
        EndOfMainDialogAsync
    }));

    // The initial child Dialog to run.
    InitialDialogId = nameof(WaterfallDialog);
}

Я ожидаю, что выполняется MainStepAsync, который выполняет следующее:

   private async Task<DialogTurnResult> MainStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {

        CoreBot.StorageLogging.LogToTableAsync($"Main step entered. I am contacting LUIS to determine the intent");

        string what_i_got = await LuisHelper.ExecuteMyLuisQuery(Configuration, Logger, stepContext.Context, cancellationToken);

        CoreBot.StorageLogging.LogToTableAsync($"LUIS intent matched: {what_i_got}");
        //await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text($"Hi! My name is Sorella. Your intent was {what_i_got}") }, cancellationToken);

        switch (what_i_got)
        {
            case "Hazard":
                StorageLogging.LogToTableAsync($"We have been asked to report a hazard");
                StorageLogging.LogToTableAsync(stepContext);
                var hazardDetails = new ResponseSet.Hazard();
                return await stepContext.BeginDialogAsync(nameof(HazardDialog), hazardDetails, cancellationToken);
            case "Greeting":
                StorageLogging.LogToTableAsync($"We have been asked to provide a greeting. After this greeting the waterfall will end");
                StorageLogging.LogToTableAsync(stepContext);
                await stepContext.Context.SendActivityAsync(MessageFactory.Text("Hi there! :). What can I help you with today? "), cancellationToken);
                return await stepContext.EndDialogAsync(null, cancellationToken);
            default:
                StorageLogging.LogToTableAsync($"We got an intent we haven't catered for. After this the waterfall will end");
                StorageLogging.LogToTableAsync(stepContext);
                return await stepContext.NextAsync(null, cancellationToken);
        }
    }

Если целью является Harzard, тогда начните HazardDialog. В противном случае, если они приветствуют бота, просто поздоровайтесь и завершите диалог верхнего уровня с водопадом. Если пользователь перенаправлен на HazardDialog, запустите следующий водопад, настроив его следующим образом:

   public class HazardDialog : CancelAndHelpDialog 
    {
        public HazardDialog()
        : base(nameof(HazardDialog))
        {
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
            {
                GetInitialHazardInfoAsync,
                GetHazardUrgencyAsync,
                FinalStepAsync
            }));
            // The initial child Dialog to run.
            InitialDialogId = nameof(WaterfallDialog);
        }

Сначала их просят описать опасность:

    private async Task<DialogTurnResult> GetInitialHazardInfoAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        CoreBot.StorageLogging.LogToTableAsync("Entered hazard step 1. Asking for hazard type");

        var hazardDetails = (ResponseSet.Hazard)stepContext.Options;

        hazardDetails.HazardType = (string)stepContext.Result;

        if (hazardDetails.HazardType == null)
        {
            CoreBot.StorageLogging.LogToTableAsync($"No hazard type provided. Asking");
            return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text("What kind of hazard would you like to report? Provide me a brief description") }, cancellationToken);
        }
        else
        {
            CoreBot.StorageLogging.LogToTableAsync($"Hazard provided. Moving on");
            return await stepContext.NextAsync(hazardDetails.HazardType, cancellationToken);
        }
    }

Тогда для срочности:

   private async Task<DialogTurnResult> GetHazardUrgencyAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        CoreBot.StorageLogging.LogToTableAsync($"Entered hazard step 2. Asking for urgency");
        var hazardDetails = (ResponseSet.Hazard)stepContext.Options;
        hazardDetails.HazardType = (string)stepContext.Result;
        var hazardAsJson = JsonConvert.SerializeObject(hazardDetails);
        StorageLogging.LogToTableAsync(hazardAsJson);

        if (hazardDetails.HarzardUrgency == null)
        {
            CoreBot.StorageLogging.LogToTableAsync($"No urgency provided. Asking");
            return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text($"Thanks. So your hazard is {hazardDetails.HazardType}? How urgent is it?") }, cancellationToken);
        }
        else
        {
            CoreBot.StorageLogging.LogToTableAsync($"Urgency given. We're all done");
            var guid = Guid.NewGuid();
            var ticketId = "HAZ" + Convert.ToString(guid).ToUpper().Substring(1,4);
            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Thanks! I've got all the informatio I need. I'll raise this with the API team on your behalf. Your Ticket ID is: {ticketId} "), cancellationToken);
            return await stepContext.NextAsync(cancellationToken, cancellationToken);
        }
    }

Если у нас есть и срочность, и тип, мы «поднимаем заявку» и переходим к последнему шагу, который просто заканчивает стек.

  private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        CoreBot.StorageLogging.LogToTableAsync($"Entered hazard step 3. Final step");
        var hazardDetails = (ResponseSet.Hazard)stepContext.Options;
        hazardDetails.HarzardUrgency = (string)stepContext.Result;
        var hazardAsJson = JsonConvert.SerializeObject(hazardDetails);
        StorageLogging.LogToTableAsync(hazardAsJson);
        return await stepContext.EndDialogAsync(hazardDetails, cancellationToken);
    }

Я ожидаю, что окончание HarzardDialog должно затем вернуться к следующему шагу в диалоговом окне «Родительский водопад», то есть EndOfMainDialogAsync, который просто говорит, что мы все сделали, и могу ли я чем-нибудь помочь?

  private async Task<DialogTurnResult> EndOfMainDialogAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        StorageLogging.LogToTableAsync($"Ending the main dialog");
        StorageLogging.LogToTableAsync(stepContext);
        await stepContext.Context.SendActivityAsync(MessageFactory.Text("Ok, I think we're all done with that. Can I do anything else to help?"), cancellationToken);
        return await stepContext.EndDialogAsync(null, cancellationToken);
    }

Тем не менее, в моем реальном разговоре поток заканчивается неправильным поведением, и фактически (если вы посмотрите на разговор ниже) запускает GetHazardUrgencyAsync из дочернего водопада, затем MainStepAsync из родительского водопада, а затем GetHazardUrgencyAsync из дочернего водопада. второй раз?

enter image description here

ОБНОВЛЕНИЕ: Согласно предложениям, я обновил свои WaterFallDialogs, чтобы иметь уникальные имена, и повторно протестировал. Я все еще веду себя неправильно. Смотрите скриншот ниже:

enter image description here

Я ожидаю, что после описания опасности меня спросят, насколько это срочно. Вместо этого я получаю следующие диалоговые ответы в виде «блока».

  • На вопрос, насколько это срочно (правильно)
  • Приветствую меня снова (Неверно - это от родительского водопада)
  • На вопрос, насколько это срочно (неверно)

Я немного растерялся здесь. Я бы предположил / подумал из своего кода, что диалоговое окно дочернего водопада должно быть полностью выполнено / завершено перед возвратом к родительскому диалоговому окну.

Ответы [ 2 ]

2 голосов
/ 05 июля 2019

Все выглядит хорошо в вашем примере.Я на самом деле попробовал его и не обнаружил никаких проблем с ним, поэтому это должна быть какая-то внешняя зависимость, которую вы пропустили в вопросе.

Пожалуйста, обратитесь к этому образцу, который точно использует ваш код и работает так, как ожидалось.Единственное отличие состоит в том, что он не связывается с LUIS, но это не должно иметь никакого значения.

Репозиторий: https://github.com/jvanderbiest/Core-bot-troubleshooting

Выход:

enter image description here

2 голосов
/ 19 июня 2019

Имена диалогов глобально уникальны внутри бота.Оба ваших диалоговых окна водопада называются «WaterfallDialog».Таким образом, вы в основном меняете водопад на лету.

Измените их на уникальные имена.

    AddDialog(new WaterfallDialog("MainDialogWaterfall", new WaterfallStep[]
    {
        MainStepAsync,
        EndOfMainDialogAsync
    }));
    AddDialog(new WaterfallDialog("HazardInfoWaterfallDialog", new WaterfallStep[]
    {
        GetInitialHazardInfoAsync,
        GetHazardUrgencyAsync,
        FinalStepAsync
    }));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...