瀑布对话框中的C#Bot V4文本提示

时间:2019-07-12 14:37:13

标签: c# botframework

我需要添加一个问题“这个帮助了吗?”从QnA获得响应后,并从用户那里获取反馈。如果对此没有响应,并且下一个输入是一个全新的查询,则该流程应从bot.cs重新开始

我尝试使用textprompt,但是在模拟器中进行测试时,bot不会在提示后等待用户输入。

Bot.cs

public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
    var activity = turnContext.Activity;
    var dc = await _dialogs.CreateContextAsync(turnContext);

    if (turnContext == null)
    {
        throw new ArgumentNullException(nameof(turnContext));
    }

    if (turnContext.Activity.Type == ActivityTypes.Message)
    {
        if (turnContext.Activity.Text != null)
        {                   
            var luisResults = await _services.LuisServices[LuisConfiguration].RecognizeAsync(dc.Context, cancellationToken);                    
            var luisProperties = LuisEntities.FromLuisResults(luisResults);                  
            await _luisEntitiesAccessor.SetAsync(turnContext, luisProperties);                   
            var topScoringIntent = luisResults?.GetTopScoringIntent();
            var topIntent = topScoringIntent.Value.intent;

            switch (topIntent)
            {
                case NoneIntent:
                    await dc.BeginDialogAsync(QnADialog.Name);
                    break;
                case GreetingsIntent:
                    await dc.BeginDialogAsync(QnAGreetingsDialog.Name);                                       
                    break;
                case CredentialsIntent:
                    await dc.BeginDialogAsync(CredentialsDialog.Name);
                    break;
                case ContactusIntent:                                       
                    await dc.BeginDialogAsync(FeedbackDialog.Name);
                    break;
                case FeedbackIntent:
                    await dc.BeginDialogAsync(FeedbackDialog.Name);                                       
                    break;
                default:                                        
                    await dc.Context.SendActivityAsync("I didn't understand what you just said to me.");                                       
                    break;
            }
        }
        else if (string.IsNullOrEmpty(turnContext.Activity.Text))
        {
            await HandleSubmitActionAsync(turnContext, userProfile);
        }
    }
    else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate)
    {
        if (turnContext.Activity.MembersAdded != null)
        {
            await SendWelcomeMessageAsync(turnContext);
        }
    }
    else if (turnContext.Activity.Type == ActivityTypes.Event)
    {
        await SendWelcomeMessageAsync(turnContext);
    }
    else
    {
        await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected");
    }

    // Save the dialog state into the conversation state.
    await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}

QnADialog.cs-我要在其中运行提示的对话框

public class QnADialog : ComponentDialog
{        
    public const int QnaNumResults = 1;
    public const double QnaConfidenceThreshold = 0.5;

    public const string QnaConfiguration = "QnAFaqSubscriptionKey";
    private const string QnAFeedbackDialog = "qnaDialog";
    public const string Name = "QnA";
    public const string TextPrompt = "textPrompt";

    private readonly BotServices _services;
    private readonly IStatePropertyAccessor<UserProfile> _userProfileAccessor;

    Action<string, string, bool, int, int> updateQna;
    private int InvalidMessageCount = 0;
    string Query = string.Empty;
    List<int> qnaIdStorage;
    UserProfile userProfile = new UserProfile();

    public QnADialog(Action<string, string, bool, int, int> updateQna, bool isCollection, List<int> rotationTemStorage, BotServices services, UserProfile _userProfile, IStatePropertyAccessor<UserProfile> userProfileAccessor, int invalidMessageCount = 0, string dialogId = null)
        : base(Name)
    {
        _services = services ?? throw new ArgumentNullException(nameof(services));
        _userProfileAccessor = userProfileAccessor ?? throw new ArgumentNullException(nameof(userProfileAccessor));
        userProfile = _userProfile;

        this.updateQna = updateQna;
        this.InvalidMessageCount = invalidMessageCount;
        qnaIdStorage = rotationTemStorage;

        var waterfallSteps = new WaterfallStep[]
        {
            BeginStepAsync,
            FetchFAQResultStepAsync,
            FeedbackStepAsync,                                                                 
            FeedbackResponseStepAsync,                       

        };           

        AddDialog(new WaterfallDialog(QnAFeedbackDialog, waterfallSteps));            
        AddDialog(new TextPrompt("userFeed"));      
    }

    public async Task<DialogTurnResult> BeginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        var messageToForward = stepContext.Context.Activity;
        UserProfile.previousQuestion = messageToForward.Text;

        string[] supportList = { "HELP", "FEEDBACK", "SUPPORT", "ESCALATE", "AGENT" };

        if (messageToForward.Text == null || messageToForward.Text.ToLower() == "no")
        {
            await stepContext.Context.SendActivityAsync("Sorry, I was not able to help you.");
            return await stepContext.EndDialogAsync();
        }
        else if (messageToForward.Text == null || supportList.Any(x => x == messageToForward.Text.ToUpper()))
        {
            await stepContext.Context.SendActivityAsync("Please reach out to... ");
            return await stepContext.EndDialogAsync();
        }
        else
        {
            return await stepContext.NextAsync();
        }
    }

    private async Task<DialogTurnResult> FetchFAQResultStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        var message = stepContext.Context.Activity;

        var qnaResult = await FaqQnaMakerService.GetQnaResult(_services, stepContext, this.Query);
        var qnaIdColl = GetQnaIdColl(this.Query, qnaResult);
        int qnaPreviousId = 0;
        int qnaNewId = 0;

        if (qnaIdColl != null && qnaIdColl.Count > 1)
        {
            qnaIdColl = qnaIdColl.Distinct().OrderBy(x => x).ToList();
            //Compare the previous Qnaid collection and existing collection , if it is matching produce the result.
            var matchItem = qnaIdColl.Intersect(qnaIdStorage);

            if (matchItem.Count() == 0)
            {
                //If there is no previous collection Qna id then take the first item from the existing Qna collection
                qnaNewId = qnaIdColl.FirstOrDefault();
            }
            else
            {
                //If there any previous Qnaid that contain in the existing collection then pick the next value and generate a new qna result.
                qnaPreviousId = matchItem.FirstOrDefault();
                qnaNewId = GetNextRotationKey(qnaIdColl, qnaPreviousId);
            }

            //Create a new response based on selected new qna id.                
            qnaResult = new[] { qnaResult.Where(x => x.Id == qnaNewId).Single() };                
        }

        if (qnaResult.First().Answer.Length > 0)
        {
            if (qnaResult.First().Score > 0)
            {
                updateQna(this.Query, qnaResult.First().Answer, false, qnaPreviousId, qnaNewId);
                InvalidMessageCount = 0;
                var QuestionCollection = TextFormatter.FormattedQuestionColl(qnaResult.First().Answer);

                if (QuestionCollection != null)
                {
                    userProfile.IsAswerCollection = true;
                    updateQna(this.Query, qnaResult.First().Answer, true, qnaPreviousId, qnaNewId);
                    var replyMessage = stepContext.Context.Activity.CreateReply();
                    replyMessage.Attachments = new List<Attachment>() { AllAdaptiveCard.QnaAttachment(new Tuple<string, string[]>(QuestionCollection.Item2, QuestionCollection.Item3)) };

                    if (!string.IsNullOrEmpty(QuestionCollection.Item1))
                    {
                        await stepContext.Context.SendActivityAsync(QuestionCollection.Item1);
                    }

                    await stepContext.Context.SendActivityAsync(replyMessage);
                    return await stepContext.EndDialogAsync();                      
                }
                else
                {
                    await stepContext.Context.SendActivityAsync(qnaResult.First().Answer);
                }                   
            }             
            else
            {
                InvalidMessageCount++;
                return await stepContext.ContinueDialogAsync();                   
            }
        }          

        return await stepContext.NextAsync();
    }

    private async Task<DialogTurnResult> FeedbackStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        return await stepContext.PromptAsync("userFeed", new PromptOptions
        {
            Prompt = stepContext.Context.Activity.CreateReply("Did this help?")                
        });
    }

    private async Task<DialogTurnResult> FeedbackResponseStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        var message = stepContext.Context.Activity;
        var mesgActivity = message as Activity;

        string var = userProfile.qnaData;
        var qnaResultModel = new { InvalidMessageCount = 0, originalQueryText = string.Empty };
        NeedMoreInformation needmoreInfo = NeedMoreInformation.NotSelected;

        if (message != null && message.Text == null && message.Value != null)
        {
            dynamic value = mesgActivity.Value.ToString();
            UserReply response = JsonConvert.DeserializeObject<UserReply>(value);
            if (!string.IsNullOrEmpty(response.Reply))
            {
                mesgActivity.Text = response.Reply;
            }

        }

        //This if condition work only the user reply back to the question "Did this help?"
        if (userProfile.needMoreInformation == true && message?.Text?.ToLower() != "yes" && message?.Text?.ToLower() != "no")
        {
            //The response message pass to LUIS service to understand the intention of the conversation is “yes” or “no” 
            bool? moreInformationYes = await LUISService.GetResultAESChatBotYesNo(message?.Text);

            if (moreInformationYes != null && moreInformationYes == true)
            {
                //Once the LUIS understand the conversation change the original message to yes.
                message.Text = "yes";
                //needmoreInfo = NeedMoreInformation.Yes;
            }
            else if (moreInformationYes != null && moreInformationYes == false)
            {
                ////Once the LUIS understand the conversation change the original message to no.
                message.Text = "no";
                needmoreInfo = NeedMoreInformation.No;
            }
            else
            {
                needmoreInfo = NeedMoreInformation.None;
            }
        }

        if (userProfile.needMoreInformation == true && message?.Text?.ToLower() == "yes")
        {                   
            userProfile.qnaInvalidMessageCount = 0;
            userProfile.needMoreInformation = false;
            dynamic value = stepContext.Context.Activity.Value;
            var output = JsonConvert.DeserializeObject<UserReply>(stepContext.Context.Activity.Value.ToString());
            if (userProfile.feedbackCard == false)
            {
                var replyMessage = stepContext.Context.Activity.CreateReply();
                replyMessage.Attachments = new List<Attachment>() { AllAdaptiveCard.FeedbackAdapativecard() };
                await stepContext.Context.SendActivityAsync(replyMessage);
            }
            if (output.Reply != "yes")
            {
                await AdaptiveCardReplyAsync(_services, stepContext, userProfile);
            }                   
        }
        else if (userProfile.needMoreInformation == true && message?.Text?.ToLower() == "no")
        {
            userProfile.qnaInvalidMessageCount = 0;
            userProfile.needMoreInformation = false;
            dynamic value = stepContext.Context.Activity.Value;

            if (value.Type == "GetMoreContent")
            {
                await AdaptiveCardGetMoreContent(_services, stepContext, userProfile);
            }
            else if (value.Type == "GetHelpSubmit")
            {
                await AdaptiveCardReplyAsync(_services, stepContext, userProfile);
            }
            else if (userProfile.getMoreContentCard == false)
            {
                var replyMessage = stepContext.Context.Activity.CreateReply();
                replyMessage.Attachments = new List<Attachment>() { AllAdaptiveCard.GetMoreContent() };
                await stepContext.Context.SendActivityAsync(replyMessage);
            }

            // context.Wait(AdaptiveCardGetMoreContent);
        } 
        else
        { 
            await stepContext.BeginDialogAsync(nameof(Bot.cs));                    
        }

        return await stepContext.EndDialogAsync();
    }
}

在出现此提示后,应转到瀑布步骤中添加的下一步,但没有。任何可能的建议/帮助将不胜感激。提前致谢!

1 个答案:

答案 0 :(得分:0)

在瀑布中看不到其他步骤的代码,例如BeginStepAsyncFetchFAQResultStepAsync,很难为您的情况提供准确的答案。

我建议您如何完成此操作,是通过使用一条消息以及该消息下方的建议操作来实现的,一旦单击任一操作,两个选项都将消失,从而消除了同一用户针对同一用户进行多次提交的可能性回答回复。

您在这里有几个选择:

1)使用此日期为sample的版本,该版本使用Microsoft.Bot.Builder NuGet软件包的v3.9.0,其类别位于QnADialogFeedbackDialog类中。

重要的部分是QnADialog实现QnAMakerDialog

2)在将答案和答案发送给用户之后(我假设在FetchFAQResultsStepAsync内部),您可以添加以下代码:

var feedback = ((Activity)context.Activity).CreateReply("Did you find what you need?");

feedback.SuggestedActions = new SuggestedActions()
{
    Actions = new List<CardAction>()
    {
        new CardAction(){ Title = "Yes", Type=ActionTypes.PostBack, Value=$"yes-positive-feedback" },
        new CardAction(){ Title = "No", Type=ActionTypes.PostBack, Value=$"no-negative-feedback" }
    }
};

await context.PostAsync(feedback);

编辑

感谢您为QnADialog类提供完整的代码,很遗憾,我无法在本地运行它,因为其中的GetQnaIdCollGetNextRotationKeyTextFormatter.FormattedQuestionColl等方法的实现您调用但尚未提供的其他方法和类。您的提示用户响应的代码看起来正确,但听起来您甚至没有显示反馈提示,或者您似乎正在显示反馈提示,但您被困在那里,您可以确认它是哪个吗? ?您是否尝试单步执行代码以查看采用哪条路径?

另一个建议是将QnA步骤和“反馈”步骤分离到单独的对话框中,我已经在下面证明了示例反馈对话框。

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using System.Threading;
using System.Threading.Tasks;

namespace ChatBot.VirtualAssistant.Dialogs
{
    public class RateAnswerDialog : ComponentDialog
    {
        public RateAnswerDialog()
            : base(nameof(RateAnswerDialog))
        {
            InitialDialogId = nameof(RateAnswerDialog);

            var askToRate = new WaterfallStep[]
            {
                AskRating,
                FinishDialog
            };

            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new WaterfallDialog(InitialDialogId, askToRate));
        }

        private async Task<DialogTurnResult> AskRating(WaterfallStepContext sc, CancellationToken cancellationToken)
        {
            PromptOptions promptOptions = new PromptOptions
            {
                Prompt = MessageFactory.Text("Was this helpful?")
            };

            return await sc.PromptAsync(nameof(TextPrompt), promptOptions);
        }

        private async Task<DialogTurnResult> FinishDialog(WaterfallStepContext sc, CancellationToken cancellationToken)
        {
            return await sc.EndDialogAsync(sc);
        }

        protected override async Task<DialogTurnResult> EndComponentAsync(DialogContext outerDc, object context, CancellationToken cancellationToken)
        {
            var waterfallContext = (WaterfallStepContext)context;

            var userResponse = ((string)waterfallContext.Result).ToLowerInvariant();

            if (userResponse == "yes")
            {
                await waterfallContext.Context.SendActivityAsync("Thank you for your feedback");
            }
            else if (userResponse == "no")
            {
                await waterfallContext.Context.SendActivityAsync("Sorry I couldn't help you");
            }
            else
            {
                await waterfallContext.Context.SendActivityAsync("The valid answers are 'yes' or 'no'");

                // TODO reprompt if required
            }

            return await outerDc.EndDialogAsync();
        }
    }
}