/// <summary> /// Every conversation turn for our QnA bot will call this method. /// There are no dialogs used, the sample only uses "single turn" processing, /// meaning a single request and response, with no stateful conversation. /// </summary> /// <param name="turnContext">A <see cref="ITurnContext"/> containing all the data needed /// for processing this conversation turn. </param> /// <param name="cancellationToken">(Optional) A <see cref="CancellationToken"/> that can be used by other objects /// or threads to receive notice of cancellation.</param> /// <returns>A <see cref="Task"/> that represents the work queued to execute.</returns> /// <seealso cref="BotStateSet"/> /// <seealso cref="ConversationState"/> /// <seealso cref="IMiddleware"/> public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { // Handle Message activity type, which is the main activity type for shown within a conversational interface // Message activities may contain text, speech, interactive cards, and binary or unknown attachments. // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types if (turnContext.Activity.Type == ActivityTypes.Message) { var dialogContext = await dialogs.CreateContextAsync(turnContext, cancellationToken); var qnastatus = await _accessors.QnAResultState.GetAsync(turnContext, () => new ShowQnAResultState()); if (qnastatus.IsFeedback) { await HandleFeedbackFlow(qnastatus, turnContext, cancellationToken); } string requery = null; if (qnastatus.ConsiderState) { // Call Active Learning Train API if (qnastatus.ActiveLearningAnswer) { _services.QnAServices.TryGetValue(QnAMakerKey, out var qnaservice); var activeLearningData = new ActiveLearningDTO() { hostName = qnaservice.Endpoint.Host, endpointKey = qnaservice.Endpoint.EndpointKey, kbid = qnaservice.Endpoint.KnowledgeBaseId, userId = turnContext.Activity.From.Id, userQuestion = qnastatus.ActiveLearningUserQuestion, }; requery = Utils.GetRequery(qnastatus, turnContext.Activity.Text, activeLearningData); if (requery != null) { TelemetryUtils.LogTrainApiResponse(this._services.TelemetryClient, TelemetryConstants.TrainApiEvent, turnContext.Activity, activeLearningData, requery); } } else { requery = Utils.GetRequery(qnastatus, turnContext.Activity.Text); } if (requery != null) { turnContext.Activity.Text = requery; } } QueryResult responseGeneral = null, responseLuis = null; // Get QnA Answer for multiturn var responseMultiturn = await GetMultiturnResponseFromKB(turnContext, qnastatus.QnaAnswer); if (responseMultiturn == null) { // Get general response from KB responseGeneral = await GetGeneralResponseFromKB(turnContext, qnastatus, true); if (responseGeneral == null) { // Get LUIS intent var luisIntent = await GetLuisIntent(turnContext, Constants.EnableLuis); if (!luisIntent.Equals("None")) { // Get Luis response responseLuis = await GetResponseWithLuisIntent(turnContext, luisIntent); } } } // choose response var qnaresponse = Utils.SelectResponse(responseMultiturn, responseGeneral, responseLuis, qnastatus.QnaAnswer); if (qnaresponse != null) { if (qnaresponse.Options != null && qnaresponse.Options.Count != 0) { await ShowQnAResponseWithOptions(turnContext, qnaresponse, qnastatus); } else { await ShowQnAResponseWithText(turnContext, qnaresponse, qnastatus); } if (qnastatus.QnaAnswer.Name == Constants.MetadataValue.Redirection) { await ShowRedirection(Constants.RedirectionType.GuidedFlow, turnContext, cancellationToken); } if (qnastatus.NeuroconCount >= Constants.ConsecutiveNeuroconAnswersAllowed) { await ShowRedirection(Constants.RedirectionType.Neurocon, turnContext, cancellationToken); } } else { // get answer from Nurocon var personalitychatanswer = await SendCognitiveServicesResponse(turnContext, cancellationToken, Constants.EnablePersonalityChat); if (personalitychatanswer == string.Empty || personalitychatanswer.Equals(@"Sorry, I don't have a response for that.", StringComparison.OrdinalIgnoreCase)) { var msg = @"I don't have an answer for that. Try typing MENU to go back to the main menu"; TelemetryUtils.LogNoAnswerEvent(this._services.TelemetryClient, turnContext.Activity); qnastatus.NeuroconCount++; await _accessors.QnAResultState.SetAsync(turnContext, qnastatus); await _accessors.ConversationState.SaveChangesAsync(turnContext); await turnContext.SendActivityAsync(msg, cancellationToken : cancellationToken); if (qnastatus.NeuroconCount >= Constants.ConsecutiveNeuroconAnswersAllowed) { await ShowRedirection(Constants.RedirectionType.Neurocon, turnContext, cancellationToken); } } else { personalitychatanswer = personalitychatanswer + " [🛈](https://labs.cognitive.microsoft.com/en-us/project-personality-chat \"Auto generated answer using Personality chat. Click to know more\")"; qnastatus.ConsiderState = false; qnastatus.QnaAnswer = null; qnastatus.NeuroconCount++; TelemetryUtils.LogNeuroconResponse(this._services.TelemetryClient, TelemetryConstants.NeuroconEvent, turnContext.Activity, qnastatus, personalitychatanswer); await _accessors.QnAResultState.SetAsync(turnContext, qnastatus); await _accessors.ConversationState.SaveChangesAsync(turnContext); await turnContext.SendActivityAsync(personalitychatanswer, cancellationToken : cancellationToken); if (qnastatus.NeuroconCount >= Constants.ConsecutiveNeuroconAnswersAllowed) { await ShowRedirection(Constants.RedirectionType.Neurocon, turnContext, cancellationToken); } } } } else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate) { return; } else if (turnContext.Activity.Type == ActivityTypes.Event) { // Send a welcome message to the user. var eventActivity = turnContext.Activity.AsEventActivity(); if (eventActivity.Name == Constants.EventWelcomeMessage) { TelemetryUtils.LogWelcomeResponse(this._services.TelemetryClient, "Welcome Event Detected", turnContext.Activity); var dialogContext = await dialogs.CreateContextAsync(turnContext, cancellationToken); var qnastatus = await _accessors.QnAResultState.GetAsync(turnContext, () => new ShowQnAResultState()); qnastatus.ConsiderState = false; qnastatus.QnaAnswer.Text = Constants.WelcomeQuestion; qnastatus.QnaAnswer.Name = Constants.MetadataValue.Welcome; await _accessors.QnAResultState.SetAsync(turnContext, qnastatus); await _accessors.ConversationState.SaveChangesAsync(turnContext); await SendWelcomeMessageAsync(turnContext, cancellationToken); } } else { await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected", cancellationToken : cancellationToken); } }
/// <summary> /// get requery text. /// </summary> /// <param name="qnastatus">qnastatus.</param> /// <param name="activityText">activity text.</param> /// <param name="activeLearningdata">activeleraning data.</param> /// <returns></returns> public static string GetRequery(ShowQnAResultState qnastatus, string activityText, ActiveLearningDTO activeLearningdata = null) { string requery = null; if (qnastatus.QnaAnswer.Options != null && qnastatus.QnaAnswer.Options.Count != 0) { foreach (var promptoption in qnastatus.QnaAnswer.Options) { if (promptoption.Option.Equals(activityText, StringComparison.CurrentCultureIgnoreCase)) { requery = string.IsNullOrEmpty(promptoption.Requery) ? promptoption.Option : promptoption.Requery; // call train API if active learning if (qnastatus.ActiveLearningAnswer == true && activeLearningdata != null) { qnastatus.ActiveLearningAnswer = false; activeLearningdata.qnaId = promptoption.QnAId; ActiveLearning.CallTrainApi(activeLearningdata); qnastatus.ActiveLearningUserQuestion = null; } break; } } } else { requery = string.IsNullOrEmpty(qnastatus.QnaAnswer.Requery) ? null : qnastatus.QnaAnswer.Requery; } return(requery); }
/// <summary> /// Log train api call. /// </summary> /// <param name="telemetryClient">telemetry client.</param> /// <param name="eventName">event name.</param> /// <param name="activity">sctivity.</param> /// <param name="activeLearningData">active learning data.</param> /// <param name="requery">requery.</param> public static void LogTrainApiResponse(Microsoft.ApplicationInsights.TelemetryClient telemetryClient, string eventName, Activity activity, ActiveLearningDTO activeLearningData, string requery) { var properties = new Dictionary <string, string>() { { TelemetryConstants.EndpointKey, activeLearningData.endpointKey }, { TelemetryConstants.HostName, activeLearningData.hostName }, { TelemetryConstants.Kbid, activeLearningData.kbid }, { TelemetryConstants.UserQuestion, activeLearningData.userQuestion }, { TelemetryConstants.UserSelectedPrompt, requery }, }; AddBasicProperties(properties, activity); telemetryClient.TrackEvent(eventName, properties); }