diff --git a/src/Azure.AISearch.WebApp/Constants.cs b/src/Azure.AISearch.WebApp/Constants.cs index 39b62eb..cdc97ba 100644 --- a/src/Azure.AISearch.WebApp/Constants.cs +++ b/src/Azure.AISearch.WebApp/Constants.cs @@ -58,6 +58,8 @@ public static class Defaults public const double FrequencyPenalty = 0.0; public const double PresencePenalty = 0.0; public const string StopSequences = ""; + public const int Strictness = 3; + public const int DocumentCount = 5; public const int HnswParametersM = 4; public const int HnswParametersEfConstruction = 400; public const int HnswParametersEfSearch = 500; diff --git a/src/Azure.AISearch.WebApp/Models/SearchRequest.cs b/src/Azure.AISearch.WebApp/Models/SearchRequest.cs index 2dcc42c..cf0277c 100644 --- a/src/Azure.AISearch.WebApp/Models/SearchRequest.cs +++ b/src/Azure.AISearch.WebApp/Models/SearchRequest.cs @@ -21,6 +21,8 @@ public class SearchRequest public double? FrequencyPenalty { get; set; } = Constants.Defaults.FrequencyPenalty; public double? PresencePenalty { get; set; } = Constants.Defaults.PresencePenalty; public string? StopSequences { get; set; } = Constants.Defaults.StopSequences; + public int? Strictness { get; set; } = Constants.Defaults.Strictness; + public int? DocumentCount { get; set; } = Constants.Defaults.DocumentCount; public bool IsVectorSearch => QueryType == Models.QueryType.Vector || QueryType == Models.QueryType.HybridStandard || QueryType == Models.QueryType.HybridSemantic; public bool IsSemanticSearch => QueryType == Models.QueryType.TextSemantic || QueryType == Models.QueryType.HybridSemantic; diff --git a/src/Azure.AISearch.WebApp/Pages/Index.cshtml b/src/Azure.AISearch.WebApp/Pages/Index.cshtml index dca4355..f1ed1aa 100644 --- a/src/Azure.AISearch.WebApp/Pages/Index.cshtml +++ b/src/Azure.AISearch.WebApp/Pages/Index.cshtml @@ -274,8 +274,8 @@ -
- +
+ @@ -312,18 +312,18 @@
-
- +
+
-
- +
+
-
- +
+
@@ -346,6 +346,30 @@
+
+ +
+
+
+
+ Document count + +
+ +
+
+
+
+
+ Strictness + +
+ +
+
+
+
+
@@ -359,8 +383,8 @@
-
- +
+ @@ -369,7 +393,7 @@
-
+
diff --git a/src/Azure.AISearch.WebApp/Services/AzureCognitiveSearchService.cs b/src/Azure.AISearch.WebApp/Services/AzureCognitiveSearchService.cs index 9fbc81b..0a45cdf 100644 --- a/src/Azure.AISearch.WebApp/Services/AzureCognitiveSearchService.cs +++ b/src/Azure.AISearch.WebApp/Services/AzureCognitiveSearchService.cs @@ -33,6 +33,7 @@ public async Task SearchAsync(SearchRequest request) var searchOptions = new SearchOptions { QueryType = request.IsSemanticSearch ? SearchQueryType.Semantic : (request.QuerySyntax == QuerySyntax.Lucene ? SearchQueryType.Full : SearchQueryType.Simple), + Size = request.DocumentCount ?? Constants.Defaults.DocumentCount, HighlightPreTag = "", HighlightPostTag = "" }; diff --git a/src/Azure.AISearch.WebApp/Services/AzureOpenAISearchService.cs b/src/Azure.AISearch.WebApp/Services/AzureOpenAISearchService.cs index a75e42e..b78ae98 100644 --- a/src/Azure.AISearch.WebApp/Services/AzureOpenAISearchService.cs +++ b/src/Azure.AISearch.WebApp/Services/AzureOpenAISearchService.cs @@ -28,7 +28,23 @@ public async Task SearchAsync(SearchRequest request) ArgumentNullException.ThrowIfNull(request.Query); var searchResponse = new SearchResponse(); - var chatCompletionsOptions = new ChatCompletionsOptions { DeploymentName = this.settings.OpenAIGptDeployment }; + var chatCompletionsOptions = new ChatCompletionsOptions + { + DeploymentName = this.settings.OpenAIGptDeployment, + MaxTokens = request.MaxTokens ?? Constants.Defaults.MaxTokens, + Temperature = (float)(request.Temperature ?? Constants.Defaults.Temperature), + NucleusSamplingFactor = (float)(request.TopP ?? Constants.Defaults.TopP), + FrequencyPenalty = (float)(request.FrequencyPenalty ?? Constants.Defaults.FrequencyPenalty), + PresencePenalty = (float)(request.PresencePenalty ?? Constants.Defaults.PresencePenalty), + }; + var stopSequences = (request.StopSequences ?? Constants.Defaults.StopSequences).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (stopSequences.Any()) + { + foreach (var stopSequence in stopSequences) + { + chatCompletionsOptions.StopSequences.Add(stopSequence); + } + } chatCompletionsOptions.Messages.Add(new ChatRequestSystemMessage(request.SystemRoleInformation)); if (request.History != null && request.History.Any()) @@ -66,27 +82,30 @@ public async Task SearchAsync(SearchRequest request) throw new InvalidOperationException("Azure OpenAI didn't return a meaningful response."); } - // Process citations within the answer, which take the form "[doc1][doc2]..." and refer to the (1-based) index of - // the citations in the tool message. - foreach (var extensionMessage in answerMessage.AzureExtensionsContext.Messages.Where(m => m.Role == ChatRole.Tool)) + if (answerMessage.AzureExtensionsContext != null) { - Console.WriteLine(extensionMessage.Content); - var content = JsonSerializer.Deserialize(extensionMessage.Content!); - if (content?.Citations != null && content.Citations.Any()) + // Process citations within the answer, which take the form "[doc1][doc2]..." and refer to the (1-based) index of + // the citations in the tool message. + foreach (var extensionMessage in answerMessage.AzureExtensionsContext.Messages.Where(m => m.Role == ChatRole.Tool)) { - var citationIndex = 0; - foreach (var citation in content.Citations) + Console.WriteLine(extensionMessage.Content); + var content = JsonSerializer.Deserialize(extensionMessage.Content!); + if (content?.Citations != null && content.Citations.Any()) { - answerText = answerText.Replace($"[doc{++citationIndex}]", $"{citation.Title}", StringComparison.OrdinalIgnoreCase); - searchResponse.SearchResults.Add(new SearchResult + var citationIndex = 0; + foreach (var citation in content.Citations) { - DocumentId = citation.Id, - DocumentTitle = citation.Title, - Captions = string.IsNullOrWhiteSpace(citation.Content) ? Array.Empty() : new[] { citation.Content } - }); + answerText = answerText.Replace($"[doc{++citationIndex}]", $"{citation.Title}", StringComparison.OrdinalIgnoreCase); + searchResponse.SearchResults.Add(new SearchResult + { + DocumentId = citation.Id, + DocumentTitle = citation.Title, + Captions = string.IsNullOrWhiteSpace(citation.Content) ? Array.Empty() : new[] { citation.Content } + }); + } + // Stop looping through the tool messages once we find the first one holding the citations. + break; } - // Stop looping through the tool messages once we find the first one holding the citations. - break; } } @@ -117,6 +136,9 @@ private AzureCognitiveSearchChatExtensionConfiguration GetAzureCognitiveSearchDa }, ShouldRestrictResultScope = request.LimitToDataSource, // Limit responses to data from the data source only QueryType = GetQueryType(request), + RoleInformation = request.SystemRoleInformation ?? Constants.Defaults.SystemRoleInformation, + Strictness = request.Strictness ?? Constants.Defaults.Strictness, + DocumentCount = request.DocumentCount ?? Constants.Defaults.DocumentCount, SemanticConfiguration = request.IsSemanticSearch ? Constants.ConfigurationNames.SemanticConfigurationNameDefault : null, EmbeddingEndpoint = request.IsVectorSearch ? new Uri(new Uri(this.settings.OpenAIEndpoint), $"openai/deployments/{this.settings.OpenAIEmbeddingDeployment}/embeddings?api-version={this.settings.OpenAIApiVersion}") : null, EmbeddingKey = request.IsVectorSearch ? this.settings.OpenAIApiKey : null