1

for my current project i try to use context-only follow-up prompts.

My problem is now, that some of the questions from the follow-up prompts are the same. qna example

In the qna ui the separations of the context works fine, but when the user answers the question from the chat i implementen in my application, qna returns the wrong answer.

Code: Botbuilder version: 4.5.3 NodeJS: 12.10.0

onMessage(){.....

let results = qna.getAnswer(context);
if(results[0].context.prompts.length){ //  Answer with Follow-up
      return context.sendActivity(CardHelper.GetHeroCard(results[0].answer, results[0].context.prompts))
   }else{ // normal answer
      return context.sendActivity(results[0].answer)
   }
}
Sample Questions:

Expected Answer:
I want to learn programming => java => here is our guide
Real Answer:
I want to learn programming => java => that is the java test

Expected Answer:
I want to do a test => java => that is the java test
Real Answer:
I want to do a test => java => that is the java test

How is it possible to implement these follow-up prompts in the code and don't lose the follow-up context?

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
Oli Kuroh
  • 123
  • 2
  • 8
  • For your first issue, are you saying "Java" is a follow-up question to multiple root questions, and you want to give a different answer to that question depending on the context? And when the user says "Java" after question 1, the API returns the answer for question 2's Java and vice versa? – Kyle Delaney Sep 26 '19 at 20:45
  • 1
    i added some examples to my question, i hope that will help :) tltr: the follow-up "java" returns always the same answer – Oli Kuroh Sep 27 '19 at 08:00
  • Okay that clarifies the first issue, but what do you mean by "In addition i also would like to answer some of the follow-up questions already based on data from the code"? Are you saying you want to generate some answers dynamically in the bot code? Do you want to use Dispatch to distinguish QnA knowledge base questions from code-data questions, or do you want the bot to know that it needs to generate an answer from code-data based on the answer it received from QnA Maker? Can you give examples? Do you want both issues solved in this post or do you want to make another post for the second one? – Kyle Delaney Sep 27 '19 at 18:03
  • 1
    The idea was that i save QuestionContext like "Java" in the Userprofile and use these information to return the answer to qna without asking the user again. But this is not relevant in first place so it prob remove this part form my question. – Oli Kuroh Sep 28 '19 at 06:41
  • To be clear, you're using the `QnAMaker.getAnswers` method from the botbuilder-ai package, right? – Kyle Delaney Sep 30 '19 at 23:58
  • Is my answer acceptable? – Kyle Delaney Oct 10 '19 at 18:29
  • Sorry i wasn't working on that project for a bit but your answer/example was exactly what i was looking for. – Oli Kuroh Oct 11 '19 at 23:39

1 Answers1

2

The first thing to understand is that multi-turn conversations and follow-up prompts are in preview. This generally means one should expect bugs and missing functionality. In this case it means the feature is not only missing from the SDK, it's even missing from the API reference. You can see in the Generate Answer reference that a call to the generateAnswer endpoint includes a context property in its body, but the type of that object is undocumented. It links to the Context object type that gets returned in the response rather than what you're supposed to put in the request.

Since your question mentions CardHelper.GetHeroCard, I presume you're already familiar with the QnA Maker prompting sample. If you are by some chance not familiar with that sample, it is the ultimate source of truth when it comes to multi-turn conversations in QnA Maker. That sample contains the entire answer to your question so I'm unsure why you're not using it. However, you should have also seen what you need to do in the documentation you should be following:

A JSON request to return a non-initial answer and follow-up prompts

Fill the context object to include the previous context.

In the following JSON request, the current question is Use Windows Hello to sign in and the previous question was accounts and signing in.

{
  "question": "Use Windows Hello to sign in",
  "top": 10,
  "userId": "Default",
  "isTest": false,
  "qnaId": 17,
  "context": {
    "previousQnAId": 15,
    "previousUserQuery": "accounts and signing in"
  }
}

QnA Maker doesn't save any state on its own, so it depends on your bot to give it the context from the previous turn. Your bot isn't doing that, and that's why it's not working. Here's a simplified version of the code from the sample to help you understand what you need to do:

async testQnAMaker(turnContext) {
    var qna = new QnAMaker({
        knowledgeBaseId: '<GUID>',
        endpointKey: '<GUID>',
        host: 'https://<APPNAME>.azurewebsites.net/qnamaker'
    });

    var context = await this.qnaState.get(turnContext) || {
        PreviousQnaId: 0,
        PreviousUserQuery: null
    };

    // We're passing a context property into the QnAMakerOptions
    // even though it's not part of the interface yet
    var results = await qna.getAnswers(turnContext, { context });
    var firstResult = results[0];

    if (firstResult) {
        var answer = firstResult.answer;
        var resultContext = firstResult.context;
        var prompts = resultContext && resultContext.prompts;

        if (prompts && prompts.length) {
            await this.qnaState.set(turnContext, {
                PreviousQnaId: firstResult.id,
                PreviousUserQuery: turnContext.activity.text
            });

            answer = ChoiceFactory.forChannel(
                turnContext,
                prompts.map(prompt => prompt.displayText),
                answer);
        }

        await turnContext.sendActivity(answer);
    } else {
        await turnContext.sendActivity("I can't answer that");
    }
}

Because you're using a feature that's currently in preview, you will need to use your own ingenuity when it comes to figuring out how to use it. I'm just using a state property accessor to save the context of the previous question on each turn, but it's likely that you'll want to build this into a dialog and save each user query in the dialog state. The point is that you'll have to save the QnA Maker state if you want the follow-up prompts to work.

EDIT: It turns out there is a way to use follow-up prompts without bot state if you put the context in the actions themselves, but that will only work if the user clicks the buttons instead of typing something: Display Text for QnAMaker follow-on prompts

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66