Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions libraries/Microsoft.Bot.Builder.AI.QnA/Models/QueryResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,14 @@ public class QueryResults
/// </value>
[JsonProperty("answers")]
public QueryResult[] Answers { get; set; }

/// <summary>
/// Gets or sets a value indicating whether gets or set for the active learning enable flag.
/// </summary>
/// <value>
/// The active learning enable flag.
/// </value>
[JsonProperty("activeLearningEnabled")]
public bool ActiveLearningEnabled { get; set; }
}
}
23 changes: 21 additions & 2 deletions libraries/Microsoft.Bot.Builder.AI.QnA/QnAMaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,25 @@ public async Task<QueryResult[]> GetAnswersAsync(
QnAMakerOptions options,
Dictionary<string, string> telemetryProperties,
Dictionary<string, double> telemetryMetrics = null)
{
var result = await GetAnswersRawAsync(turnContext, options, telemetryProperties, telemetryMetrics).ConfigureAwait(false);

return result.Answers;
}

/// <summary>
/// Generates an answer from the knowledge base.
/// </summary>
/// <param name="turnContext">The Turn Context that contains the user question to be queried against your knowledge base.</param>
/// <param name="options">The options for the QnA Maker knowledge base. If null, constructor option is used for this instance.</param>
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the QnaMessage event.</param>
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the QnaMessage event.</param>
/// <returns>A list of answers for the user query, sorted in decreasing order of ranking score.</returns>
public async Task<QueryResults> GetAnswersRawAsync(
ITurnContext turnContext,
QnAMakerOptions options,
Dictionary<string, string> telemetryProperties = null,
Dictionary<string, double> telemetryMetrics = null)
{
if (turnContext == null)
{
Expand All @@ -175,9 +194,9 @@ public async Task<QueryResult[]> GetAnswersAsync(
throw new ArgumentException("Null or empty text");
}

var result = await this.generateAnswerHelper.GetAnswersAsync(turnContext, messageActivity, options).ConfigureAwait(false);
var result = await this.generateAnswerHelper.GetAnswersRawAsync(turnContext, messageActivity, options).ConfigureAwait(false);

await OnQnaResultsAsync(result, turnContext, telemetryProperties, telemetryMetrics, CancellationToken.None).ConfigureAwait(false);
await OnQnaResultsAsync(result.Answers, turnContext, telemetryProperties, telemetryMetrics, CancellationToken.None).ConfigureAwait(false);

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,22 @@ public GenerateAnswerUtils(IBotTelemetryClient telemetryClient, QnAMakerEndpoint
/// <param name="messageActivity">Message activity of the turn context.</param>
/// <param name="options">The options for the QnA Maker knowledge base. If null, constructor option is used for this instance.</param>
/// <returns>A list of answers for the user query, sorted in decreasing order of ranking score.</returns>
[Obsolete]
public async Task<QueryResult[]> GetAnswersAsync(ITurnContext turnContext, IMessageActivity messageActivity, QnAMakerOptions options)
{
var result = await this.GetAnswersRawAsync(turnContext, messageActivity, options).ConfigureAwait(false);

return result.Answers;
}

/// <summary>
/// Generates an answer from the knowledge base.
/// </summary>
/// <param name="turnContext">The Turn Context that contains the user question to be queried against your knowledge base.</param>
/// <param name="messageActivity">Message activity of the turn context.</param>
/// <param name="options">The options for the QnA Maker knowledge base. If null, constructor option is used for this instance.</param>
/// <returns>A list of answers for the user query, sorted in decreasing order of ranking score.</returns>
public async Task<QueryResults> GetAnswersRawAsync(ITurnContext turnContext, IMessageActivity messageActivity, QnAMakerOptions options)
{
if (turnContext == null)
{
Expand All @@ -73,12 +88,12 @@ public async Task<QueryResult[]> GetAnswersAsync(ITurnContext turnContext, IMess

var result = await QueryQnaServiceAsync((Activity)messageActivity, hydratedOptions).ConfigureAwait(false);

await EmitTraceInfoAsync(turnContext, (Activity)messageActivity, result, hydratedOptions).ConfigureAwait(false);
await EmitTraceInfoAsync(turnContext, (Activity)messageActivity, result.Answers, hydratedOptions).ConfigureAwait(false);

return result;
}

private static async Task<QueryResult[]> FormatQnaResultAsync(HttpResponseMessage response, QnAMakerOptions options)
private static async Task<QueryResults> FormatQnaResultAsync(HttpResponseMessage response, QnAMakerOptions options)
{
var jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

Expand All @@ -89,9 +104,9 @@ private static async Task<QueryResult[]> FormatQnaResultAsync(HttpResponseMessag
answer.Score = answer.Score / 100;
}

var result = results.Answers.Where(answer => answer.Score > options.ScoreThreshold).ToArray();
results.Answers = results.Answers.Where(answer => answer.Score > options.ScoreThreshold).ToArray();

return result;
return results;
}

private static void ValidateOptions(QnAMakerOptions options)
Expand Down Expand Up @@ -170,7 +185,7 @@ private QnAMakerOptions HydrateOptions(QnAMakerOptions queryOptions)
return hydratedOptions;
}

private async Task<QueryResult[]> QueryQnaServiceAsync(Activity messageActivity, QnAMakerOptions options)
private async Task<QueryResults> QueryQnaServiceAsync(Activity messageActivity, QnAMakerOptions options)
{
var requestUrl = $"{_endpoint.Host}/knowledgebases/{_endpoint.KnowledgeBaseId}/generateanswer";
var jsonRequest = JsonConvert.SerializeObject(
Expand Down
31 changes: 31 additions & 0 deletions tests/Microsoft.Bot.Builder.AI.QnA.Tests/QnAMakerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,37 @@ public async Task QnaMaker_ReturnsAnswer()
StringAssert.StartsWith(results[0].Answer, "BaseCamp: You can use a damp rag to clean around the Power Pack");
}

[TestMethod]
[TestCategory("AI")]
[TestCategory("QnAMaker")]
public async Task QnaMaker_ReturnsAnswerRaw()
{
var mockHttp = new MockHttpMessageHandler();
mockHttp.When(HttpMethod.Post, GetRequestUrl())
.Respond("application/json", GetResponse("QnaMaker_ReturnsAnswer.json"));

var options = new QnAMakerOptions
{
Top = 1,
};

var qna = GetQnAMaker(
mockHttp,
new QnAMakerEndpoint
{
KnowledgeBaseId = _knowlegeBaseId,
EndpointKey = _endpointKey,
Host = _hostname,
},
options);

var results = await qna.GetAnswersRawAsync(GetContext("how do I clean the stove?"), options);
Assert.IsNotNull(results.Answers);
Assert.IsTrue(results.ActiveLearningEnabled);
Assert.AreEqual(results.Answers.Length, 1, "should get one result");
StringAssert.StartsWith(results.Answers[0].Answer, "BaseCamp: You can use a damp rag to clean around the Power Pack");
}

[TestMethod]
[TestCategory("AI")]
[TestCategory("QnAMaker")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
{
"answers": [
{
"questions": [
"how do I clean the stove?"
],
"answer": "BaseCamp: You can use a damp rag to clean around the Power Pack",
"score": 100,
"id": 5,
"source": "Editorial",
"metadata": [],
"context": {
"isContextOnly": true,
"prompts": [
{
"displayOrder": 0,
"qnaId": 55,
"qna": null,
"displayText": "Where can I buy?"
}
]
"activeLearningEnabled": true,
"answers": [
{
"questions": [
"how do I clean the stove?"
],
"answer": "BaseCamp: You can use a damp rag to clean around the Power Pack",
"score": 100,
"id": 5,
"source": "Editorial",
"metadata": [],
"context": {
"isContextOnly": true,
"prompts": [
{
"displayOrder": 0,
"qnaId": 55,
"qna": null,
"displayText": "Where can I buy?"
}
}
]
]
}
}
]
}