From 09c8512f96cac63c0ca82e719a36067eebd1aa2d Mon Sep 17 00:00:00 2001 From: Victor Das Date: Tue, 29 Nov 2022 21:30:52 +0530 Subject: [PATCH 1/3] Custom Question Answering Recognizer --- .gitignore | 3 +- Bot.Builder.Community.sln | 11 + README.md | 1 + ...s.CustomQuestionAnsweringRecognizer.csproj | 27 ++ .../BotComponent.cs | 16 + .../CustomQuestionAnsweringRecognizer.cs | 359 ++++++++++++++++++ ...s.CustomQuestionAnsweringRecognizer.schema | 166 ++++++++ 7 files changed, 582 insertions(+), 1 deletion(-) create mode 100644 libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj create mode 100644 libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/BotComponent.cs create mode 100644 libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/QuestionAnswering/CustomQuestionAnsweringRecognizer.cs create mode 100644 libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Schemas/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.schema diff --git a/.gitignore b/.gitignore index 5d4a1b21..86edf4d3 100644 --- a/.gitignore +++ b/.gitignore @@ -274,4 +274,5 @@ Documentation/docs/ PublishProfiles/ appsettings.local.json -appsettings.Development.json \ No newline at end of file +appsettings.Development.json +/outputpackages diff --git a/Bot.Builder.Community.sln b/Bot.Builder.Community.sln index 0e3583bc..fe625f72 100644 --- a/Bot.Builder.Community.sln +++ b/Bot.Builder.Community.sln @@ -183,6 +183,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot.Builder.Community.Adapt EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot.Builder.Community.Adapters.Facebook.Tests", "tests\Bot.Builder.Community.Adapters.Facebook.Tests\Bot.Builder.Community.Adapters.Facebook.Tests.csproj", "{8201DC48-763A-4534-9E51-466E15DF01D8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer", "libraries\Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer\Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj", "{2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug - NuGet Packages|Any CPU = Debug - NuGet Packages|Any CPU @@ -805,6 +807,14 @@ Global {8201DC48-763A-4534-9E51-466E15DF01D8}.Documentation|Any CPU.Build.0 = Debug|Any CPU {8201DC48-763A-4534-9E51-466E15DF01D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {8201DC48-763A-4534-9E51-466E15DF01D8}.Release|Any CPU.Build.0 = Release|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Documentation|Any CPU.ActiveCfg = Debug|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Documentation|Any CPU.Build.0 = Debug|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -893,6 +903,7 @@ Global {428AD1B4-DF58-4D21-9C19-AB4AB6001A90} = {840D4038-9AB8-4750-9FFE-365386CE47E2} {3348B9A5-E3CE-4AF8-B059-8B4D7971C25A} = {840D4038-9AB8-4750-9FFE-365386CE47E2} {8201DC48-763A-4534-9E51-466E15DF01D8} = {840D4038-9AB8-4750-9FFE-365386CE47E2} + {2FF7084F-98F8-461B-BF3F-F0D8F0AA3E66} = {2975F285-9749-49EA-A6E6-E5D78DDE20B6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9FE3B75E-BA2B-45BC-BBF0-DDA8BA10C4F0} diff --git a/README.md b/README.md index d16849e4..30c34cb4 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ The following recognizers are currently available; | Name | Description | NuGet | | ------ | ------ | ------ | | [Fuzzy Matching Recognizer](libraries/Bot.Builder.Community.Recognizers.FuzzyRecognizer) | A recognizer that allows you to use fuzzy matching to compare strings. Useful in situations such as when a user make a spelling mistake etc. When the recognizer is used a list of matches, along with confidence scores, are returned. | [![NuGet version](https://img.shields.io/badge/NuGet-blue.svg)](https://www.nuget.org/packages/Bot.Builder.Community.Recognizers.FuzzyRecognizer/) | +| [Custom Question Answering Recognizer](libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer) | A recognizer that allows you to do inference calls to Azure Custom Question Answering projects. | [![NuGet version](https://img.shields.io/badge/NuGet-blue.svg)](https://www.nuget.org/packages/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/) | ### Storage diff --git a/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj new file mode 100644 index 00000000..5a3cb4ab --- /dev/null +++ b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp3.1 + enable + msbot-component;msbot-recognizer;composer;botframework;botbuilder + 1.0.0-preview + + Bot Builder Community 2022 + https://github.com/BotBuilderCommunity/botbuilder-community-dotnet/blob/develop/libraries/Bot.Builder.Community.Recognizers.QuestionAnsweringRecognizer/ + True + snupkg + Question Answering Recognizer + BotBuilderCommunity + MIT + Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer + Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer + + + + + + + + + + diff --git a/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/BotComponent.cs b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/BotComponent.cs new file mode 100644 index 00000000..0e7721ba --- /dev/null +++ b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/BotComponent.cs @@ -0,0 +1,16 @@ +using Bot.Builder.Community.Recognizers; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Dialogs.Declarative; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Bot.Builder.Community.Recognizers +{ + public class CustomQuestionAnsweringRecognizerBotComponent : BotComponent + { + public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton((sp) => new DeclarativeType(CustomQuestionAnsweringRecognizer.Kind)); + } + } +} diff --git a/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/QuestionAnswering/CustomQuestionAnsweringRecognizer.cs b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/QuestionAnswering/CustomQuestionAnsweringRecognizer.cs new file mode 100644 index 00000000..f12c1295 --- /dev/null +++ b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/QuestionAnswering/CustomQuestionAnsweringRecognizer.cs @@ -0,0 +1,359 @@ +using AdaptiveExpressions.Properties; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.AI.QnA; +using Microsoft.Bot.Builder.Dialogs; +using Microsoft.Bot.Schema; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Bot.Builder.Community.Recognizers +{ + /// + /// This recognizer is a modified copy of the QnAMakerRecognizer from Bot Framework SDK + /// + public class CustomQuestionAnsweringRecognizer : Recognizer + { + /// + /// The declarative type for this recognizer. + /// + [JsonProperty("$kind")] + public const string Kind = "Bot.Builder.Community.Recognizers.QuestionAnsweringRecognizer"; + + /// + /// Key used when adding the intent to the intents collection. + /// + public const string QnAMatchIntent = "QnAMatch"; + + private const string IntentPrefix = "intent="; + + private readonly JsonSerializerSettings _settings = new JsonSerializerSettings { MaxDepth = null }; + + /// + /// Initializes a new instance of the class. + /// + public CustomQuestionAnsweringRecognizer() + { + } + + /// + /// Gets or sets the KnowledgeBase Id of your QnA Maker KnowledgeBase. + /// + /// + /// The knowledgebase Id. + /// + [JsonProperty("projectName")] + public StringExpression ProjectName { get; set; } + + /// + /// Gets or sets the Hostname for your QnA Maker service. + /// + /// + /// The host name of the QnA Maker knowledgebase. + /// + [JsonProperty("hostname")] + public StringExpression HostName { get; set; } + + /// + /// Gets or sets the Endpoint key for the QnA Maker KB. + /// + /// + /// The endpoint key for the QnA service. + /// + [JsonProperty("endpointKey")] + public StringExpression EndpointKey { get; set; } + + /// + /// Gets or sets the number of results you want. + /// + /// + /// The number of results you want. + /// + [DefaultValue(3)] + [JsonProperty("top")] + public IntExpression Top { get; set; } = 3; + + /// + /// Gets or sets the threshold score to filter results. + /// + /// + /// The threshold for the results. + /// + [DefaultValue(0.3F)] + [JsonProperty("threshold")] + public NumberExpression Threshold { get; set; } = 0.3F; + + /// + /// Gets or sets a value indicating whether gets or sets environment of knowledgebase to be called. + /// + /// + /// A value indicating whether to call test or prod environment of knowledgebase. + /// + [JsonProperty("isTest")] + public bool IsTest { get; set; } + + /// + /// Gets or sets ranker Type. + /// + /// + /// The desired RankerType. + /// + [JsonProperty("rankerType")] + public StringExpression RankerType { get; set; } = RankerTypes.DefaultRankerType; + + /// + /// Gets or sets join operator. + /// + /// + /// A value used for Join operation of Metadata . + /// + [JsonProperty("strictFiltersJoinOperator")] + public JoinOperator StrictFiltersJoinOperator { get; set; } + + /// + /// Gets or sets the whether to include the dialog name metadata for QnA context. + /// + /// + /// A bool or boolean expression. + /// + [DefaultValue(true)] + [JsonProperty("includeDialogNameInMetadata")] + public BoolExpression IncludeDialogNameInMetadata { get; set; } = true; + + /// + /// Gets or sets an expression to evaluate to set additional metadata name value pairs. + /// + /// An expression to evaluate for pairs of metadata. + [JsonProperty("metadata")] + public ArrayExpression Metadata { get; set; } + + /// + /// Gets or sets an expression to evaluate to set the context. + /// + /// An expression to evaluate to QnARequestContext to pass as context. + [JsonProperty("context")] + public ObjectExpression Context { get; set; } + + /// + /// Gets or sets an expression or numberto use for the QnAId paratmer. + /// + /// The expression or number. + [JsonProperty("qnaId")] + public IntExpression QnAId { get; set; } = 0; + + /// + /// Gets or sets the to be used when calling the QnA Maker API. + /// + /// + /// A instance of . + /// + [JsonIgnore] + public HttpClient HttpClient { get; set; } + + /// + /// Gets or sets the flag to determine if personal information should be logged in telemetry. + /// + /// + /// The flag to indicate in personal information should be logged in telemetry. + /// + [JsonProperty("logPersonalInformation")] + public BoolExpression LogPersonalInformation { get; set; } = "=settings.runtimeSettings.telemetry.logPersonalInformation"; + + /// + /// Return results of the call to QnA Maker. + /// + /// Context object containing information for a single turn of conversation with a user. + /// The incoming activity received from the user. The Text property value is used as the query text for QnA Maker. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Additional properties to be logged to telemetry with the LuisResult event. + /// Additional metrics to be logged to telemetry with the LuisResult event. + /// A containing the QnA Maker result. + public override async Task RecognizeAsync(DialogContext dialogContext, Activity activity, CancellationToken cancellationToken, Dictionary telemetryProperties = null, Dictionary telemetryMetrics = null) + { + // Identify matched intents + var recognizerResult = new RecognizerResult + { + Text = activity.Text, + Intents = new Dictionary(), + }; + + if (string.IsNullOrEmpty(activity.Text)) + { + recognizerResult.Intents.Add("None", new IntentScore()); + return recognizerResult; + } + + var filters = new List(); + //if (IncludeDialogNameInMetadata.GetValue(dialogContext.State)) + //{ + // filters.Add(new Metadata + // { + // Name = "dialogName", + // Value = dialogContext.ActiveDialog.Id + // }); + //} + + // if there is $qna.metadata set add to filters + var externalMetadata = Metadata?.GetValue(dialogContext.State); + if (externalMetadata != null) + { + filters.AddRange(externalMetadata); + } + + // Calling QnAMaker to get response. + var qnaClient = await GetQnAMakerClientAsync(dialogContext).ConfigureAwait(false); + var answers = await qnaClient.GetAnswersAsync( + dialogContext.Context, + new QnAMakerOptions + { + Context = Context?.GetValue(dialogContext.State), + ScoreThreshold = (float)Threshold.GetValue(dialogContext.State), + StrictFilters = filters.ToArray(), + Top = Top.GetValue(dialogContext.State), + QnAId = QnAId.GetValue(dialogContext.State), + RankerType = RankerType.GetValue(dialogContext.State), + IsTest = IsTest, + StrictFiltersJoinOperator = StrictFiltersJoinOperator + }, + null).ConfigureAwait(false); + + if (answers.Any()) + { + QueryResult topAnswer = null; + foreach (var answer in answers) + { + if (topAnswer == null || answer.Score > topAnswer.Score) + { + topAnswer = answer; + } + } + + if (topAnswer.Answer.Trim().ToUpperInvariant().StartsWith(IntentPrefix.ToUpperInvariant(), StringComparison.Ordinal)) + { + recognizerResult.Intents.Add(topAnswer.Answer.Trim().Substring(IntentPrefix.Length).Trim(), new IntentScore { Score = topAnswer.Score }); + } + else + { + recognizerResult.Intents.Add(QnAMatchIntent, new IntentScore { Score = topAnswer.Score }); + } + + var answerArray = new JArray(); + answerArray.Add(topAnswer.Answer); + ObjectPath.SetPathValue(recognizerResult, "entities.answer", answerArray); + + var instance = new JArray(); + var data = JObject.FromObject(topAnswer); + data["startIndex"] = 0; + data["endIndex"] = activity.Text.Length; + instance.Add(data); + ObjectPath.SetPathValue(recognizerResult, "entities.$instance.answer", instance); + + recognizerResult.Properties["answers"] = answers; + } + else + { + recognizerResult.Intents.Add("None", new IntentScore { Score = 1.0f }); + } + + TrackRecognizerResult(dialogContext, "QnAMakerRecognizerResult", FillRecognizerResultTelemetryProperties(recognizerResult, telemetryProperties, dialogContext), telemetryMetrics); + + return recognizerResult; + } + + /// + /// Gets an instance of . + /// + /// The used to access state. + /// An instance of . + protected virtual Task GetQnAMakerClientAsync(DialogContext dc) + { + var qnaClient = dc.Context.TurnState.Get(); + if (qnaClient != null) + { + // return mock client + return Task.FromResult(qnaClient); + } + + var httpClient = dc.Context.TurnState.Get(); + if (httpClient == null) + { + httpClient = HttpClient; + } + + var (epKey, error) = EndpointKey.TryGetValue(dc.State); + var (hn, error2) = HostName.TryGetValue(dc.State); + var (kbId, error3) = ProjectName.TryGetValue(dc.State); + var (logPersonalInfo, error4) = LogPersonalInformation.TryGetValue(dc.State); + + var endpoint = new QnAMakerEndpoint + { + EndpointKey = epKey ?? throw new InvalidOperationException($"Unable to get a value for {nameof(EndpointKey)} from state. {error}"), + Host = hn ?? throw new InvalidOperationException($"Unable to a get value for {nameof(HostName)} from state. {error2}"), + KnowledgeBaseId = kbId ?? throw new InvalidOperationException($"Unable to get a value for {nameof(ProjectName)} from state. {error3}"), + QnAServiceType = Microsoft.Bot.Builder.AI.QnA.Models.ServiceType.Language + }; + + var options = new QnAMakerOptions + { + ScoreThreshold = (float)Threshold.GetValue(dc.State), + StrictFilters = Metadata?.GetValue(dc.State)?.ToArray(), + Top = Top.GetValue(dc.State), + Context = new QnARequestContext(), + QnAId = 0, + RankerType = RankerType?.GetValue(dc.State), + IsTest = IsTest, + }; + //return Task.FromResult(new QnAMaker(endpoint, new QnAMakerOptions(), httpClient, TelemetryClient, logPersonalInfo)); + return Task.FromResult(new CustomQuestionAnswering(endpoint, options, httpClient, TelemetryClient, LogPersonalInformation.GetValue(dc.State))); + } + + /// + /// Uses the RecognizerResult to create a list of properties to be included when tracking the result in telemetry. + /// + /// Recognizer Result. + /// A list of properties to append or override the properties created using the RecognizerResult. + /// Dialog Context. + /// A dictionary that can be included when calling the TrackEvent method on the TelemetryClient. + protected override Dictionary FillRecognizerResultTelemetryProperties(RecognizerResult recognizerResult, Dictionary telemetryProperties, DialogContext dialogContext = null) + { + if (dialogContext == null) + { + throw new ArgumentNullException(nameof(dialogContext), "DialogContext needed for state in AdaptiveRecognizer.FillRecognizerResultTelemetryProperties method."); + } + + var properties = new Dictionary + { + { "TopIntent", recognizerResult.Intents.Any() ? recognizerResult.Intents.First().Key : null }, + { "TopIntentScore", recognizerResult.Intents.Any() ? recognizerResult.Intents.First().Value?.Score?.ToString("N1", CultureInfo.InvariantCulture) : null }, + { "Intents", recognizerResult.Intents.Any() ? JsonConvert.SerializeObject(recognizerResult.Intents, _settings) : null }, + { "Entities", recognizerResult.Entities?.ToString() }, + { "AdditionalProperties", recognizerResult.Properties.Any() ? JsonConvert.SerializeObject(recognizerResult.Properties, _settings) : null }, + }; + + var (logPersonalInfo, error) = LogPersonalInformation.TryGetValue(dialogContext.State); + + if (logPersonalInfo && !string.IsNullOrEmpty(recognizerResult.Text)) + { + properties.Add("Text", recognizerResult.Text); + properties.Add("AlteredText", recognizerResult.AlteredText); + } + + // Additional Properties can override "stock" properties. + if (telemetryProperties != null) + { + return telemetryProperties.Concat(properties) + .GroupBy(kv => kv.Key) + .ToDictionary(g => g.Key, g => g.First().Value); + } + + return properties; + } + } +} diff --git a/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Schemas/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.schema b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Schemas/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.schema new file mode 100644 index 00000000..778f6aeb --- /dev/null +++ b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Schemas/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.schema @@ -0,0 +1,166 @@ +{ + "$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema", + "$role": "implements(Microsoft.IRecognizer)", + "title": "Custom Question Answering Recognizer", + "description": "Recognizer for generating QnAMatch intents from a KB.", + "type": "object", + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Optional unique id using with RecognizerSet." + }, + "projectName": { + "$ref": "schema:#/definitions/stringExpression", + "title": "projectName", + "description": "project name of question answering knowledge base.", + "default": "=settings.qna.knowledgebaseid" + }, + "endpointKey": { + "$ref": "schema:#/definitions/stringExpression", + "title": "Endpoint key", + "description": "Endpoint key for the QnA Maker KB.", + "default": "=settings.qna.endpointkey" + }, + "hostname": { + "$ref": "schema:#/definitions/stringExpression", + "title": "Hostname", + "description": "Hostname for your QnA Maker service.", + "default": "=settings.qna.hostname", + "examples": [ + "https://yourserver.azurewebsites.net/qnamaker" + ] + }, + "threshold": { + "$ref": "schema:#/definitions/numberExpression", + "title": "Threshold", + "description": "Threshold score to filter results.", + "default": 0.3 + }, + "strictFilters": { + "$ref": "schema:#/definitions/arrayExpression", + "title": "Strict filters", + "description": "Metadata filters to use when calling the QnA Maker KB.", + "items": { + "type": "object", + "title": "Metadata filters", + "description": "Metadata filters to use when querying QnA Maker KB.", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name to filter on.", + "maximum": 100 + }, + "value": { + "type": "string", + "title": "Value", + "description": "Value to restrict filter.", + "maximum": 100 + } + } + } + }, + "top": { + "$ref": "schema:#/definitions/numberExpression", + "title": "Top", + "description": "The number of answers you want to retrieve.", + "default": 3 + }, + "isTest": { + "$ref": "schema:#/definitions/booleanExpression", + "title": "Use test environment", + "description": "True, if pointing to Test environment, else false.", + "examples": [ + true, + "=f(x)" + ] + }, + "rankerType": { + "title": "Ranker type", + "description": "Type of Ranker.", + "oneOf": [ + { + "type": "string", + "title": "Ranker type", + "description": "Type of Ranker.", + "enum": [ + "default", + "questionOnly", + "autoSuggestQuestion" + ], + "default": "default" + }, + { + "$ref": "schema:#/definitions/equalsExpression" + } + ] + }, + "strictFiltersJoinOperator": { + "$ref": "schema:#/definitions/stringExpression", + "title": "StrictFiltersJoinOperator", + "description": "Join operator for Strict Filters.", + "oneOf": [ + { + "title": "Join operator", + "description": "Value of Join Operator to be used as onjuction with Strict Filter values.", + "enum": [ + "AND", + "OR" + ], + "default": "AND" + }, + { + "$ref": "schema:#/definitions/equalsExpression" + } + ] + }, + "includeDialogNameInMetadata": { + "$ref": "schema:#/definitions/booleanExpression", + "title": "Include dialog name", + "description": "When set to false, the dialog name will not be passed to QnAMaker. (default) is true", + "default": true, + "examples": [ + true, + "=f(x)" + ] + }, + "metadata": { + "$ref": "schema:#/definitions/arrayExpression", + "title": "Metadata filters", + "description": "Metadata filters to use when calling the QnA Maker KB.", + "items": { + "type": "object", + "title": "Metadata filter", + "description": "Metadata filter to use when calling the QnA Maker KB.", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name of value to test." + }, + "value": { + "type": "string", + "title": "Value", + "description": "Value to filter against." + } + } + } + }, + "context": { + "$ref": "schema:#/definitions/objectExpression", + "title": "QnA request context", + "description": "Context to use for ranking." + }, + "qnaId": { + "$ref": "schema:#/definitions/integerExpression", + "title": "QnA Id", + "description": "A number or expression which is the QnAId to paass to QnAMaker API." + } + }, + "required": [ + "knowledgeBaseId", + "endpointKey", + "hostname" + ] +} \ No newline at end of file From 2bdbcd016db37cde2429794e2ffb9b02eec9ebe8 Mon Sep 17 00:00:00 2001 From: Ben Williams Date: Tue, 29 Nov 2022 21:28:06 -0600 Subject: [PATCH 2/3] fix project for pack --- ...ity.Recognizers.CustomQuestionAnsweringRecognizer.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj index 5a3cb4ab..42966448 100644 --- a/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj +++ b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer.csproj @@ -4,15 +4,15 @@ netcoreapp3.1 enable msbot-component;msbot-recognizer;composer;botframework;botbuilder - 1.0.0-preview - + 1.0.0 + https://github.com/BotBuilderCommunity/botbuilder-community-dotnet/blob/master/LICENSE + https://github.com/BotBuilderCommunity/botbuilder-community-dotnet/tree/master/libraries/Bot.Builder.Community.Recognizers.QuestionAnsweringRecognizer/ Bot Builder Community 2022 https://github.com/BotBuilderCommunity/botbuilder-community-dotnet/blob/develop/libraries/Bot.Builder.Community.Recognizers.QuestionAnsweringRecognizer/ True snupkg Question Answering Recognizer BotBuilderCommunity - MIT Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer From 2940fa539acedf04aeae8476eef3020029af87ec Mon Sep 17 00:00:00 2001 From: Victor Das Date: Thu, 1 Dec 2022 08:52:56 +0530 Subject: [PATCH 3/3] Readme for CQA recognizer --- .../README.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/README.md diff --git a/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/README.md b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/README.md new file mode 100644 index 00000000..16eb03b5 --- /dev/null +++ b/libraries/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/README.md @@ -0,0 +1,28 @@ +# Custom Question Answering Recognizer + +## Summary +This recognizer helps you add a custom recognizer to a Bot Composer empty bot in order to use [Azure Question Answering](https://azure.microsoft.com/en-us/products/cognitive-services/question-answering/) (instead of using QnA Maker). + +## Quickstart +1. Install the [package](https://www.nuget.org/packages/Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer/) to your Composer project. +2. Change the root dialog's recognizer type to custom. +4. Update the settings in the custom recognizer with the hostname, project name and your keys. +3. Use custom intent triggers or the built in QnAIntent trigger as usual in the root dialog. + +## Settings +If using the Question Answering recognizer, use the following definition + +``` +{ + "$kind": "Bot.Builder.Community.Recognizers.CustomQuestionAnsweringRecognizer", + "hostname": "", + "projectName": "", + "endpointKey": "" +} +``` + +Because the Question Answering recognizer is a modified version of the existing QnAMaker recognizer, the workflow elements (such as multi turn) work the same. In addition the same QnAMaker events and telemetry are written out to the logs. + +## Limitations + +* Composer does not integrate natively with Question Answering, so managing the question/answer pairs must be done in the language studio portal instead of Composer. \ No newline at end of file