Delete QnA Pairs in QnA Maker Knowledge Base using the Bot in C#

This is part 2 of the series “Auto Updater QnA Maker KB Bot“. Update your QnA Maker Knowledge using the ChatBot. Add new question-answer pairs to your KB with the help of a bot interactive environment. You can also update and delete existing KB with this bot. In this part, we will be deleting the QnA pairs in the KB using the Bot.

QnA Maker is a cloud-based Natural Language Processing (NLP) service that easily creates a natural conversational layer over your data. It can be used to find the most appropriate answer for any given natural language input, from your custom knowledge base (KB) of information.

A client application for QnA Maker is any conversational application that communicates with a user in natural language to answer a question. Examples of client applications include social media apps, chatbots, and speech-enabled desktop applications.

For more information, refer Microsoft Documentation.

Prerequisites

  1. Visual Studio
  2. Bot Emulator
  3. Bot Framework SDK Templates
  4. QnA Maker KB

You can get the links to the first 3 requirements on the Downloads page. If you have not created a Knowledge Base (KB) before, you can refer to my post on “Create QnA Maker Bot without writing Code using Azure Bot Service“.


Implementing the Bot with Delete Operation

In the first part, we have already started with this project where we implemented the Add QnA Pairs functionality. This part is the continuation of the last. Here we will be coding the Delete dialog.

Previous part, we have created an empty dialog class files, replace the code with below in DeleteDialog.cs.

using Microsoft.Azure.CognitiveServices.Knowledge.QnAMaker;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.AI.QnA;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Extensions.Configuration;
using QnAUpdatorBot.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace QnAUpdatorBot.Dialogs.Operations
{
    public class DeleteDialog : ComponentDialog
    {
        private readonly IConfiguration Configuration;
        private readonly IHttpClientFactory _httpClientFactory;

        public DeleteDialog(IConfiguration configuration, IHttpClientFactory httpClientFactory) : base(nameof(DeleteDialog))
        {
            Configuration = configuration;
            _httpClientFactory = httpClientFactory;
            var waterfallSteps = new WaterfallStep[]
            {
                QuestionStepAsync,
                VerifyQuestionStepAsync,
                ConfirmDeleteStepAsync,
                ConfirmStepAsync,
                SummaryStepAsync,
            };

            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>)));
            AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
            AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
            AddDialog(new QuestionPhraseDialog(Configuration, _httpClientFactory));

            InitialDialogId = nameof(WaterfallDialog);
        }


        private async Task<DialogTurnResult> QuestionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Please enter the question.")
            }, cancellationToken);
        }

        private async Task<DialogTurnResult> VerifyQuestionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            stepContext.Values["Question"] = (string)stepContext.Result;
            //string question = (string)stepContext.Values["Question"];

            var httpClient = _httpClientFactory.CreateClient();

            var qnaMaker = new QnAMaker(new QnAMakerEndpoint
            {
                KnowledgeBaseId = Configuration["KnowledgeBaseId"],
                EndpointKey = Configuration["EndpointKey"],
                Host = Configuration["Host"]
            },
            null,
            httpClient);

            var options = new QnAMakerOptions { Top = 1 };

            // The actual call to the QnA Maker service.
            var response = await qnaMaker.GetAnswersAsync(stepContext.Context, options);

            if (response != null && response.Length == 1)
            {
                await stepContext.Context.SendActivityAsync("Here is the response from the Knowledge Base.",
                    cancellationToken: cancellationToken);

                for (int i = 0; i < response[0].Questions.Length; i++)
                {
                    await stepContext.Context.SendActivityAsync("Question: " + response[0].Questions[i], cancellationToken: cancellationToken);
                }

                await stepContext.Context.SendActivityAsync("Answer: " + response[0].Answer, cancellationToken: cancellationToken);

                await stepContext.Context.SendActivityAsync("Confidence Score: " + response[0].Score * 100, cancellationToken: cancellationToken);

                stepContext.Values["QnA ID"] = response[0].Id;

                return await stepContext.NextAsync(true, cancellationToken);
            }
            else
            {
                await stepContext.Context.SendActivityAsync("The question you entered does not exists in the Knowledge Base.", cancellationToken: cancellationToken);

                return await stepContext.NextAsync(false, cancellationToken);
            }          
        }

        private async Task<DialogTurnResult> ConfirmDeleteStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            stepContext.Values["Result"] = (bool)stepContext.Result;
            bool result = (bool)stepContext.Values["Result"];

            if (result)
            {
                return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions
                {
                    Prompt = MessageFactory.Text("Would you like to Delete this pair with QnA ID: " + stepContext.Values["QnA ID"] + "?")
                }, cancellationToken);
            }
            else
            {
                return await stepContext.NextAsync(false, cancellationToken);
            }
            
        }

        private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            if ((bool)stepContext.Result)
            {
                QnAData.QnAID.Add((int)stepContext.Values["QnA ID"]);
            }
            return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Would you like to Delete more QnA Pairs?")
            }, cancellationToken);
        }

        private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            if ((bool)stepContext.Result)
            {
                var promptMessage = "What else can I do for you?";
                return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
            }
            else
            {
                if (QnAData.QnAID.Count != 0)
                {
                    await stepContext.Context.SendActivityAsync(MessageFactory.Text("Ok. Deleting QnA pair with ids " + string.Join(", ", QnAData.QnAID)));

                    await stepContext.Context.SendActivityAsync(MessageFactory.Text("Please wait while I update your Knowledge Base."), cancellationToken);

                    var authoringURL = $"https://{Configuration["ResourceName"]}.cognitiveservices.azure.com";

                    // <AuthorizationAuthor>
                    var client = new QnAMakerClient(new ApiKeyServiceClientCredentials(Configuration["Key"]))
                    { Endpoint = authoringURL };
                    // </AuthorizationAuthor>

                    QnAClientModel.UpdateKBForDelete(client, Configuration["KnowledgeBaseId"], QnAData.QnAID).Wait();
                    QnAClientModel.PublishKb(client, Configuration["KnowledgeBaseId"]).Wait();

                    await stepContext.Context.SendActivityAsync(MessageFactory.Text("I have deleted the qna pair in the Knowledge Base. Thank you for using QnA Updator Bot Service."));

                    return await stepContext.EndDialogAsync(null, cancellationToken);
                }
                
                return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
            }
        }

        
    }
}

The above piece of code asks the user the question he/she would like to delete. When the question is provided, the bot runs a check if the question is present in the KB. This returns the match with the highest confidence score. Then we ask the user if he/she would like to delete the returned question-answer pair.

Again, the bot asks if the user wants to delete more pairs. If yes, then the delete dialog starts from the beginning. If no, the bot asks the user to confirm the delete. Based on the user response, the bot deletes the pair from the QnA Maker KB.

Thank you All!!! Hope you find this useful.


One thought on “Delete QnA Pairs in QnA Maker Knowledge Base using the Bot in C#

Add yours

Leave a Reply

Up ↑

%d