We are having trouble calling a QnA Maker knowledge base from the Bot Framework. The code compiles fine. The bot emulator also opens and runs. The code is based on the following GitHub repo:
In the repo's code, there is a dispatch model with two LUIS instances and a QnA Maker instance. We were able to successfully change the dispatch code to instead point to our three QnA Maker instances. This worked in the bot emulator, and we were able to communicate with no errors or exceptions happening.
HOWEVER - we are using QnA Maker KB's with follow-up prompts. Although the code was running, none of the follow up prompts were being generated; only the answers were being returned. I found the following blog post, and attempted to modify ONE of our functions to be able to grab the follow-up prompts from the QnA Maker KB:
https://joji.me/en-us/blog/implement-follow-up-prompt-for-qna-bot/
This did not completely break our code. The code still compiles and runs. We can successfully ask questions that direct to the other QnA Maker KB's (which returns answers, but no follow up prompts) but we return the following errors when we give an input that directs the dispatch tool to our modified code (where we attempt to return follow-up prompts):
Console Error Messages: (note: this error only happens when we are testing the bot in the Bot Emulator and give input that directs the Dispatch to this KB)
fail: Microsoft.Bot.Builder.Integration.AspNet.Core.BotFrameworkHttpAdapter[0]
[OnTurnError] unhandled error : Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.BotBuilderSamples.DispatchBot.ProcessfindPartQnAAsync(ITurnContext`1 turnContext, CancellationToken cancellationToken) in C:\Users\c50941\source\repos\AIM bot\BotBuilder-Samples\samples\csharp_dotnetcore\14.nlp-with-dispatch\Bots\DispatchBot.cs:line 155
at Microsoft.BotBuilderSamples.DispatchBot.DispatchToTopIntentAsync(ITurnContext`1 turnContext, String intent, RecognizerResult recognizerResult, CancellationToken cancellationToken) in C:\Users\c50941\source\repos\AIM bot\BotBuilder-Samples\samples\csharp_dotnetcore\14.nlp-with-dispatch\Bots\DispatchBot.cs:line 109
at Microsoft.BotBuilderSamples.DispatchBot.OnMessageActivityAsync(ITurnContext`1 turnContext, CancellationToken cancellationToken) in C:\Users\c50941\source\repos\AIM bot\BotBuilder-Samples\samples\csharp_dotnetcore\14.nlp-with-dispatch\Bots\DispatchBot.cs:line 88
at Microsoft.Bot.Builder.ActivityHandler.OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.BotFrameworkAdapter.TenantIdWorkaroundForTeamsMiddleware.OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.MiddlewareSet.ReceiveActivityWithStatusAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.BotAdapter.RunPipelineAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)
QnAMaker Trace: (from the Bot Emulator)
Unable to find a QnA Maker service with Knowledge Base ID d1f*****-****-****-****-************. Please add a QnA Maker service to your bot.
Based on research of other's problems, we know that it is common to not have the right information in the appsettings.json. We have triple checked that this is correct on our end. That is also evidenced that we can call our other two Qna Maker KB's successfully, as their credentials are stored by the same method and in the same file. Here is our modified function to call the specific KB, with the intent of also getting the follow-up prompts. Based on the console errors, this is where it seems our error would be. Also, this is the only function we have modified since the entire program was working fully: (We have commented ERROR at the lines where the output logs have told us there is an issue)
private async Task ProcessfindPartQnAAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
_logger.LogInformation("ProcessfindPartQnAAsync");
var results = await _botServices.findPartQnA.GetAnswersAsync(turnContext);
// The actual call to the QnA Maker service.
var response = await _botServices.findPartQnA.GetAnswersAsync(turnContext);
if (response != null && response.Length > 0)
{
// create http client to perform qna query
var followUpCheckHttpClient = _httpClientFactory.CreateClient();
// add QnAAuthKey to Authorization header
followUpCheckHttpClient.DefaultRequestHeaders.Add("Authorization", _configuration["findPartQnAEndpointKey"]);
// construct the qna query url
var url = $"{_configuration["findPartQnAEndpointHostName"]}/knowledgebases/{_configuration["findPartQnAKnowledgebaseId"]}/generateAnswer";
// post query
// ***** ERROR *****
var checkFollowUpJsonResponse = await followUpCheckHttpClient.PostAsync(url, new StringContent("{\"question\":\"" + turnContext.Activity.Text + "\"}", Encoding.UTF8, "application/json")).Result.Content.ReadAsStringAsync();
// parse result
var followUpCheckResult = JsonConvert.DeserializeObject<FollowUpCheckResult>(checkFollowUpJsonResponse);
// initialize reply message containing the default answer
var reply = MessageFactory.Text(response[0].Answer);
// ***** ERROR *****
if (followUpCheckResult.Answers.Length > 0 && followUpCheckResult.Answers[0].Context.Prompts.Length > 0)
{
// if follow-up check contains valid answer and at least one prompt, add prompt text to SuggestedActions using CardAction one by one
reply.SuggestedActions = new SuggestedActions();
reply.SuggestedActions.Actions = new List<CardAction>();
for (int i = 0; i < followUpCheckResult.Answers[0].Context.Prompts.Length; i++)
{
var promptText = followUpCheckResult.Answers[0].Context.Prompts[i].DisplayText;
reply.SuggestedActions.Actions.Add(new CardAction() { Title = promptText, Type = ActionTypes.ImBack, Value = promptText });
}
}
await turnContext.SendActivityAsync(reply, cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken);
}
}
We believe that there must be something wrong with the way that we are constructing the URL... though we have triple-checked that all of the info is correct as it is named in our appsettings.json and that we have the right credentials from our Azure services stored there. Thank you for any help that can be provided.