0
votes

I am not able to get QnA Maker prompt buttons working properly. I am making standard QnA Maker REST calls like so:

        if (qnAcontext == null) {
            qnAcontext = {
                PreviousQnaId: 0,
                PreviousUserQuery: null
            }
        }
        
        console.log(qnAcontext);
        const qnaResult = await request({
            url: url,
            method: 'POST',
            headers: headers,
            json: {
                question: query,
                top: 3,
                context: qnAcontext
            }
        });

The bot is correctly getting qnAcontext and including it in the POST request. And I can see that the values are slightly different. E.g. if I type in a prompt without context, it comes back with 32.2% confidence, and if I add the context it is higher but only 38.64%. This is well below the required score to display the answer to a user.

If I do this in the QnA Maker test panel, the followup comes back at 100%. Based on the comments in this help article, it seems that this is accomplished by passing the qnaId of the prompt in the service. However, how can I know this ID if the user hasn't selected it yet? The prompts are generated into a Hero Card, and I do have qnaId at the time I generate the card, but I don't know how that could be of use. Here is how I generate the prompt buttons:

        var buttons = [];
        prompts.forEach(element => {
            buttons.push({
                value: element.displayText,
                type: ActionTypes.ImBack,
                title: element.displayText
            });
        });
                    
        var heroCard = CardFactory.heroCard(
            cardTitle,
            cardText,
            [],
            CardFactory.actions(buttons));

I suppose I could make a separate call with the previous question and find the qnaId for the submitted query but that seems like unnecessary overhead. Surely it should be possible to get the correct result from QnA Maker with just the text and the previous question?

EDIT: I did get this working with the following bit of code, but the latency is definitely noticeable. I have to imagine there is a better way to accomplish this...

        if (qnAcontext == null) {
            var qnaResult = await request({
                url: url,
                method: 'POST',
                headers: headers,
                json: {
                    question: query,
                    top: 3,
                    context: qnAcontext
                }
            });
        } else {
            var firstResult = await request({
                url: url,
                method: 'POST',
                headers: headers,
                json: {
                    question: qnAcontext.PreviousUserQuery
                }
            });
            var prompt = firstResult.answers[0].context.prompts.find(e => { return e.displayText == query });
            var qnaResult = await request({
                url: url,
                method: 'POST',
                headers: headers,
                json: {
                    qnaId: prompt.qnaId
                }
            });
        }
1

1 Answers

1
votes

The QnaContext isnt actually needed. You only need to pass the qnaId supplied with the follow up prompt. You are almost there with just a few small tweaks.

Assuming you are using Teams as your channel, then use the messageBack functionality, instead of imBack.

messageBack lets you send additional data by

  1. echoing one set of text to screen and
  2. sending separate data to the bot for processing (which is not visible on the front end), which can include your qnaid for follow up questions.

As per your code sample above, only a small change is needed assuming you already have a handle to the prompts:

        prompts.forEach(element => {
            buttons.push({
                type: ActionTypes.messageBack,
                title: element.displayText
                displayText: "<optional - echo text to conversation>",
                text: "User just clicked the MessageBack button",
                value: {
                    "qnaId": element.qnaid
                }
            });
        });

when the button is clicked an additional field called qnaId is returned in the activity.

the reply JSON will look like the following: (data is dummy data)

{
   "text":"User just clicked the MessageBack button",
   "value":{
      "qnaId":"6"
   },

   .... SNIP ....

   "channelData":{
      "channel":{
         "id":"19:malejcou081i20ojmlcau0@thread.skype"
      },
      "team":{
         "id":"19:12d021jdoijsaeoaue0u@thread.skype"
      },
      "tenant":{
         "id":"bec8e231-67ad-484e-87f4-3e5438390a77"
      }
   },
        "replyToId": "1575667808184",
}

so, assuming we assign the qnaId from activity.value.qnaid to a property called followUpQnaId

            var qnaResult = await request({
                url: url,
                method: 'POST',
                headers: headers,
                json: {
                    qnaId: followUpQnaId
                }
            });

see here: CardActions - messageBack

It means you only have one call to send card to user with embedded qnaid for each prompt and then when they answer, you ask for the exact match followup using the qnaid. it should reduce the lag you are seeing.