mirror of
https://github.com/marcominerva/SqlDatabaseVectorSearch.git
synced 2026-06-20 12:23:10 +00:00
Refactor ChatService and VectorSearchService parameters
Updated parameter types in ChatService and VectorSearchService from IEnumerable<string> to IEnumerable<Entities.DocumentChunk> for better structure. Enhanced citation formatting rules in ChatService. Increased MaxRelevantChunks and MaxInputTokens in appsettings.json to improve processing capabilities.
This commit is contained in:
@@ -7,6 +7,7 @@ using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
|
|||||||
using OpenAI.Chat;
|
using OpenAI.Chat;
|
||||||
using SqlDatabaseVectorSearch.Models;
|
using SqlDatabaseVectorSearch.Models;
|
||||||
using SqlDatabaseVectorSearch.Settings;
|
using SqlDatabaseVectorSearch.Settings;
|
||||||
|
using Entities = SqlDatabaseVectorSearch.Data.Entities;
|
||||||
|
|
||||||
namespace SqlDatabaseVectorSearch.Services;
|
namespace SqlDatabaseVectorSearch.Services;
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ public class ChatService(IChatCompletionService chatCompletionService, Tokenizer
|
|||||||
return new(reformulatedQuestion.Content!, tokenUsage);
|
return new(reformulatedQuestion.Content!, tokenUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ChatResponse> AskQuestionAsync(Guid conversationId, IEnumerable<string> chunks, string question, CancellationToken cancellationToken = default)
|
public async Task<ChatResponse> AskQuestionAsync(Guid conversationId, IEnumerable<Entities.DocumentChunk> chunks, string question, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var chat = CreateChatAsync(chunks, question);
|
var chat = CreateChatAsync(chunks, question);
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ public class ChatService(IChatCompletionService chatCompletionService, Tokenizer
|
|||||||
return new(answer.Content!, tokenUsage);
|
return new(answer.Content!, tokenUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async IAsyncEnumerable<ChatResponse> AskStreamingAsync(Guid conversationId, IEnumerable<string> chunks, string question, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
public async IAsyncEnumerable<ChatResponse> AskStreamingAsync(Guid conversationId, IEnumerable<Entities.DocumentChunk> chunks, string question, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var chat = CreateChatAsync(chunks, question);
|
var chat = CreateChatAsync(chunks, question);
|
||||||
|
|
||||||
@@ -110,19 +111,57 @@ public class ChatService(IChatCompletionService chatCompletionService, Tokenizer
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChatHistory CreateChatAsync(IEnumerable<string> chunks, string question)
|
private ChatHistory CreateChatAsync(IEnumerable<Entities.DocumentChunk> chunks, string question)
|
||||||
{
|
{
|
||||||
var chat = new ChatHistory("""
|
var chat = new ChatHistory("""
|
||||||
You can use only the information provided in this chat to answer questions. If you don't know the answer, reply suggesting to refine the question.
|
You can use only the information provided in this chat to answer questions. If you don't know the answer, reply suggesting to refine the question.
|
||||||
|
|
||||||
For example, if the user asks "What is the capital of France?" and in this chat there isn't information about France, you should reply something like:
|
For example, if the user asks "What is the capital of France?" and in this chat there isn't information about France, you should reply something like:
|
||||||
- This information isn't available in the given context
|
- This information isn't available in the given context
|
||||||
- I'm sorry, I don't know the answer to that question
|
- I'm sorry, I don't know the answer to that question
|
||||||
- I don't have that information
|
- I don't have that information
|
||||||
- I don't know
|
- I don't know
|
||||||
- Given the context, I can't answer that question
|
- Given the context, I can't answer that question
|
||||||
- I'my sorry, I don't have enough information to answer that question
|
- I'm sorry, I don't have enough information to answer that question
|
||||||
Never answer to questions that are not related to this chat.
|
|
||||||
You must answer in the same language of the user's question. For example, it the user asks a question in English, the answer must be in English.
|
Never answer questions that are not related to this chat.
|
||||||
|
You must answer in the same language as the user's question.
|
||||||
|
|
||||||
|
IMPORTANT - CITATION PLACEMENT AND LENGTH:
|
||||||
|
The quote in each <citation> MUST be MAXIMUM 5 words, taken word-for-word from the search result. If the quote is longer than 5 words, your answer is INVALID.
|
||||||
|
When you find an answer, you MUST place ALL citations ONLY at the very end of your response, never inside or between sentences.
|
||||||
|
First provide your complete answer, then add a blank line, then list all citations.
|
||||||
|
|
||||||
|
Use this XML format for citations:
|
||||||
|
<citation filename='string' page_number='1'>exact quote here</citation>
|
||||||
|
|
||||||
|
STRICT RULES for citations:
|
||||||
|
- Citations MUST NEVER appear inside, before, or between sentences of your answer. They MUST be grouped together ONLY at the end, after a blank line.
|
||||||
|
- If you include citations anywhere except at the end, your answer is WRONG and INVALID.
|
||||||
|
- Always include the citation(s) if there are results. If you don't know the answer, do NOT include citations.
|
||||||
|
- The quote must be max 5 words, taken word-for-word from the search result, and is the basis for why the citation is relevant. If the quote is longer than 5 words, your answer is INVALID.
|
||||||
|
- Do NOT refer to the presence of citations; just emit these tags right at the end, with no surrounding text.
|
||||||
|
- The citations must always be in a list at the end of the response, one after the other. Never add the citations between the actual response text or inside sentences.
|
||||||
|
- Do NOT add any text after the citations.
|
||||||
|
- ALWAYS leave a blank line between your answer and the first citation.
|
||||||
|
|
||||||
|
Examples (CORRECT):
|
||||||
|
Here is my complete answer to your question. I'm providing all the information based on the context.
|
||||||
|
|
||||||
|
<citation filename='doc1.pdf' page_number='1'>Paris is the capital</citation>
|
||||||
|
<citation filename='doc2.pdf' page_number='2'>largest city in France</citation>
|
||||||
|
|
||||||
|
Examples (WRONG):
|
||||||
|
Here is my answer <citation filename='doc1.pdf' page_number='1'>Paris is the capital of France and is known for the Eiffel Tower</citation> with more text.
|
||||||
|
<citation filename='doc1.pdf' page_number='1'>Paris is the capital of France and is known for the Eiffel Tower</citation> Here is my answer.
|
||||||
|
Here is my answer. (without any citations when information is available)
|
||||||
|
Here is my answer.
|
||||||
|
<citation filename='doc1.pdf' page_number='1'>Paris is the capital of France and is known for the Eiffel Tower</citation> More answer text.
|
||||||
|
|
||||||
|
YOU MUST SEPARATE YOUR ANSWER FROM CITATIONS WITH A BLANK LINE.
|
||||||
|
NEVER INSERT CITATIONS WITHIN YOUR ANSWER TEXT.
|
||||||
|
CITATIONS MUST ONLY APPEAR AT THE END, AFTER A BLANK LINE.
|
||||||
|
IF YOU DO NOT FOLLOW THESE RULES, YOUR RESPONSE IS INVALID.
|
||||||
""");
|
""");
|
||||||
|
|
||||||
var prompt = new StringBuilder($"""
|
var prompt = new StringBuilder($"""
|
||||||
@@ -141,7 +180,7 @@ public class ChatService(IChatCompletionService chatCompletionService, Tokenizer
|
|||||||
|
|
||||||
foreach (var chunk in chunks)
|
foreach (var chunk in chunks)
|
||||||
{
|
{
|
||||||
var text = $"---{Environment.NewLine}{chunk}";
|
var text = $"--- {chunk.Document.Name} (Document ID: {chunk.Document.Id} | Chunk ID: {chunk.Id}) {Environment.NewLine}{chunk.Content}{Environment.NewLine}";
|
||||||
|
|
||||||
var tokenCount = tokenizerService.CountChatCompletionTokens(text);
|
var tokenCount = tokenizerService.CountChatCompletionTokens(text);
|
||||||
if (tokenCount > availableTokens)
|
if (tokenCount > availableTokens)
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class VectorSearchService(IServiceProvider serviceProvider, ApplicationDb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(ChatResponse ReformulatedQuestion, int EmbeddingTokenCount, IEnumerable<string> Chunks)> CreateContextAsync(Question question, bool reformulate, CancellationToken cancellationToken)
|
private async Task<(ChatResponse ReformulatedQuestion, int EmbeddingTokenCount, IEnumerable<Entities.DocumentChunk> Chunks)> CreateContextAsync(Question question, bool reformulate, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Reformulate the question taking into account the context of the chat to perform keyword search and embeddings.
|
// Reformulate the question taking into account the context of the chat to perform keyword search and embeddings.
|
||||||
var reformulatedQuestion = reformulate ? await chatService.CreateQuestionAsync(question.ConversationId, question.Text, cancellationToken) : new(question.Text);
|
var reformulatedQuestion = reformulate ? await chatService.CreateQuestionAsync(question.ConversationId, question.Text, cancellationToken) : new(question.Text);
|
||||||
@@ -107,9 +107,8 @@ public class VectorSearchService(IServiceProvider serviceProvider, ApplicationDb
|
|||||||
// Perform Vector Search on SQL Database.
|
// Perform Vector Search on SQL Database.
|
||||||
var questionEmbedding = await embeddingGenerator.GenerateVectorAsync(reformulatedQuestion.Text!, cancellationToken: cancellationToken);
|
var questionEmbedding = await embeddingGenerator.GenerateVectorAsync(reformulatedQuestion.Text!, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
var chunks = await dbContext.DocumentChunks
|
var chunks = await dbContext.DocumentChunks.Include(c => c.Document)
|
||||||
.OrderBy(c => EF.Functions.VectorDistance("cosine", c.Embedding, questionEmbedding.ToArray()))
|
.OrderBy(c => EF.Functions.VectorDistance("cosine", c.Embedding, questionEmbedding.ToArray()))
|
||||||
.Select(c => c.Content)
|
|
||||||
.Take(appSettings.MaxRelevantChunks)
|
.Take(appSettings.MaxRelevantChunks)
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
"MaxTokensPerLine": 300,
|
"MaxTokensPerLine": 300,
|
||||||
"MaxTokensPerParagraph": 1000,
|
"MaxTokensPerParagraph": 1000,
|
||||||
"OverlapTokens": 100,
|
"OverlapTokens": 100,
|
||||||
"MaxRelevantChunks": 10,
|
"MaxRelevantChunks": 50,
|
||||||
"MaxInputTokens": 16384,
|
"MaxInputTokens": 32768,
|
||||||
"MaxOutputTokens": 800,
|
"MaxOutputTokens": 800,
|
||||||
"MessageExpiration": "00:05:00",
|
"MessageExpiration": "00:05:00",
|
||||||
"MessageLimit": 20
|
"MessageLimit": 20
|
||||||
|
|||||||
Reference in New Issue
Block a user