botbuilder-js
botbuilder-js copied to clipboard
Adding activeLearning capabilities to qnaMakerRecognizer
Use this query to search for the most popular feature requests.
Is your feature request related to a problem? Please describe. I am migrating a bot using botframework 4.3 to use AdaptiveDialogs, the issue comes when I try to maintain the same functionality in QnA dialogs, I reviewed the source and I see that you are not checking if the activeLearning feature is enabled so it can't be used with qnaMakerRecognizer
Describe the solution you'd like Please add the corresponding logic for checking if activeLearning is enabled using getAnswersRaw instead of getAnswers in the recognize method, as well as the callTrainAsync method for sending the suggestions to QnAMaker
Describe alternatives you've considered Keep everything using the old code, or copy the files from your package into a local folder, so I can modify them and use that instead
Additional context `/** * Queries the knowledgebase and either passes result to the next step or constructs and displays an active learning card * if active learning is enabled and multiple score close answers are returned. **/ async callGenerateAnswer(step) { const dialogOptions = step.activeDialog.state[this.options]; dialogOptions.qnaMakerOptions.qnaId = 0; dialogOptions.qnaMakerOptions.context = { previousQnAId: 0, previousUserQuery: '' };
step.values[this.currentQuery] = step.context.activity.text;
const previousContextData = step.activeDialog.state[this.qnAContextData] || {};
var previousQnAId = step.activeDialog.state[this.previousQnAId] || 0;
if (previousQnAId > 0) {
dialogOptions.qnaMakerOptions.context = { previousQnAId: previousQnAId, previousUserQuery: '' };
if (previousContextData[step.context.activity.text]) {
dialogOptions.qnaMakerOptions.qnaId = previousContextData[step.context.activity.text];
}
}
const qna = this.getQnAClient();
const response = await qna.getAnswersRaw(step.context, dialogOptions.qnaMakerOptions);
const qnaResponse = {
activeLearningEnabled: response.activeLearningEnabled,
answers: response.answers
};
previousQnAId = -1;
step.activeDialog.state[this.previousQnAId] = previousQnAId;
const isActiveLearningEnabled = qnaResponse.activeLearningEnabled;
step.values[this.qnAData] = response.answers;
if (isActiveLearningEnabled && qnaResponse.answers.length > 0 && qnaResponse.answers[0].score <= this.maximumScoreForLowScoreVariation) {
qnaResponse.answers = qna.getLowScoreVariation(qnaResponse.answers);
if (qnaResponse.answers && qnaResponse.answers.length > 1) {
var suggestedQuestions = [];
qnaResponse.answers.forEach(answer => {
suggestedQuestions.push(answer.questions[0]);
});
var message = QnACardBuilder.getSuggestionsCard(suggestedQuestions, dialogOptions.qnaDialogResponseOptions.activeLearningCardTitle, dialogOptions.qnaDialogResponseOptions.cardNoMatchText);
await step.context.sendActivity(message);
step.activeDialog.state[this.options] = dialogOptions;
return Dialog.EndOfTurn;
}
}
const result = [];
if (response.answers && response.answers.length > 0) {
result.push(response.answers[0]);
}
step.values[this.qnAData] = result;
step.activeDialog.state[this.options] = dialogOptions;
return await step.next(result);
}
/**
* If active learning options were displayed in the previous step and the user has selected an option other
* than 'no match' then the training API is called, passing the user's chosen question back to the knowledgebase.
* If no active learning options were displayed in the previous step, the incoming result is immediately passed to the next step.
**/
async callTrain(step) {
const dialogOptions = step.activeDialog.state[this.options];
const trainResponses = step.values[this.qnAData];
const currentQuery = step.values[this.currentQuery];
const reply = step.context.activity.text;
if (trainResponses && trainResponses.length > 1) {
const qnaResult = trainResponses.filter(r => r.questions[0] == reply);
if (qnaResult && qnaResult.length > 0) {
var results = [];
results.push(qnaResult[0]);
step.values[this.qnAData] = results;
var records = [];
records.push({
userId: step.context.activity.id,
userQuestion: currentQuery,
qnaId: qnaResult[0].id.toString()
});
var feedbackRecords = { feedbackRecords: records };
await this.getQnAClient().callTrainAsync(feedbackRecords);
return await step.next(qnaResult);
} else if (reply == dialogOptions.qnaDialogResponseOptions.cardNoMatchText) {
const activity = dialogOptions.qnaDialogResponseOptions.cardNoMatchResponse;
await step.context.sendActivity(activity || this.defaultCardNoMatchResponse);
return step.endDialog();
}
else {
return await super.runStep.call(this, step, 0, DialogReason.beginCalled);
}
}
return await step.next(step.result);
}`
@sahithimurty, please review this issue and let us know if changes are needed.