Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Вложенные диалоги: сообщение отправляется дважды

Автор вопроса: Gabriel PiffarettiОтветов: 1

У меня есть бот, в котором я делаю что-то вроде этого:

1) Приходит новое действие (сообщение).

2) Я отправляю сообщение в RootDialog.

3) В зависимости от некоторой логики RootDialog может:

 a) Call a LuisDialog (handling natural language)

 b) Call a CustomDialog (handles some business logic).

Но когда пользовательское состояние сбрасывается и поток приводит к намерению внутри LuisDialog, он дважды вызывает метод намерения. Просто первое время состояние пустое, потом работает нормально.

Позвольте мне показать вам код:

Контроллер сообщений:

   public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
            try
            {
                await Conversation.SendAsync(activity, () => new RootDialog());
            }
            catch (HttpRequestException e)
            {
             ...
            }
     }

Корневой диалог:

 public class RootDialog : IDialog<object>
 {
    public async Task StartAsync(IDialogContext context)
    {
        await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
    }

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> awaitable)
    {
        bool value = DoSomeCustomLogic();
        if (value)
        {
            string message = DoSomething();
            await context.PostAsync(message);
        } else {
            bool value2 = DoSomeCustomLogic2();
            if (value2)
            {
                var answerValidationDialog = new ValidateAnswerWithUserDialog();
                context.Call(answerValidationDialog, ValidateAnswerWithUserDialogCompletedCallback);
            } else {
                var luisDialog = new LuisDialog();
                await context.Forward(luisDialog,LuisDialogCompletedCallback, context.Activity, CancellationToken.None);
            }
        }
    }

Обратные вызовы выполняют только context.Done(true);

И у LuisDialog есть намерение, которое звучит так:

  [LuisIntent(LuisUtils.INTENT_MENU_SALUTE)]
    public async Task SaluteOrMenu(IDialogContext context, LuisResult result)
    {
        if (LuisUtils.IntentScoreIsHighEnough(result))
        {
            string userName = context.Activity.From.Name;
            ContextHelper helper = new ContextHelper(MessageReceived);
            await helper.AskUserToDoSomeInitialAction(context, saluteWord, userName);
            context.Done(true);
        }
        else
        {
            await None(context, result);
        }
    }

И, наконец, класс ContextHelper:

public class ContextHelper
{
    private Func<IDialogContext, IAwaitable<IMessageActivity>, Task> MessageReceived;

    public ContextHelper(Func<IDialogContext, IAwaitable<IMessageActivity>, Task> messageReceived)
    {
        MessageReceived = messageReceived;
    }

    public async Task AskUserToDoSomeInitialAction(IDialogContext context, string saluteWord, string userName)
    {
        var reply = context.MakeMessage();
        List<CardAction> buttons = BuildInitialOptionActions();
        List<CardImage> images = BuildInitialOptionImages();
        string initialText = $"Hi stranger!"
        var card = new HeroCard
        {
            Title = "Hello!"
            Text = initialText,
            Buttons = buttons,
            Images = images
        };
        reply.Attachments = new List<Attachment> { card.ToAttachment() };
        await context.PostAsync(reply);
        context.Wait(AfterUserChoseOptionInSalute);
    }

    private async Task AfterUserChoseOptionInSalute(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        await ReDispatchMessageReceivedToDialog(context);
    }

    private async Task ReDispatchMessageReceivedToDialog(IDialogContext context)
    {
        await MessageReceived(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
    }

}

Намерение SaluteOrMenu вызывается дважды (только при первом взаимодействии с ботом или при удалении состояния. После отладки я увидел, что после выполнения context.Wait(AfterUserChoseOptionInSalute); бот вызывает эту функцию (вместо ожидания события для ее вызова)

Любые идеи?

Заранее спасибо.

  • Можете ли вы создать небольшой образец, где это можно было бы воспроизвести? Ezequiel Jadib 08.04.2017
  • 0) Как я сказал в посте, это происходит, когда состояние бота пусто (первый раз, когда пользователь использует его или когда я делаю /deleteprofile). 1) пользователь набирает привет. 2) Логика, о которой я сказал, оценивается (в моем случае DoSomeLogic() вызывает службу QnA и в зависимости от оценки отвечает на вопрос или нет). Поскольку "привет" не является допустимым вопросом, он перенаправляет сообщение в LuisDialog. 3) Салют Интент вызывается и показывает маленькую карту героя. он заканчивается контекстом. Подождите (AfterUserChoseOptionInSalute); 4) Сам по себе AfterUserChoseOption. Gabriel Piffaretti 08.04.2017
  • Да, я читал сообщение, но если вы не хотите, чтобы кто-то закодировал точно такой же сценарий, чтобы попытаться воспроизвести проблему, это будет сложно... диалоги переадресации/вызова работают, как и ожидалось; так что проблема в вашем коде. Ezequiel Jadib 08.04.2017
  • Другое дело context.Done(true); в методе намерения выполняется, но также выполняется ожидание. Итак, либо вы ждете, либо завершаете диалог; вы не можете делать и то, и другое. помощник контекста ожидает, поэтому вы должны удалить контекст. Готово‹true› Ezequiel Jadib 08.04.2017

botframeworkazure-language-understanding

Ответы

Ответ 1 · рейтинг 3 · Gabriel Piffaretti

Я нашел строку, которая была неправильной. Это было в первом диалоге (RootDialog):

public async Task StartAsync(IDialogContext context)
{
   await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}

Это строка, которая повторно отправляет сообщение с входящей активностью. У меня это было где-то в каком-то фрагменте кода, и (не знаю почему) я подумал, что было бы неплохо использовать его в StartAsync. Итак, из-за этого произошло два звонка.

Тупой меня.

Я просто изменил его на это и работал:

public async Task StartAsync(IDialogContext context)
{
    context.Wait(MessageReceivedAsync);
}

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы