Skip to content

Commit 505ccff

Browse files
committed
Enhance ChatService with new prompts and simplifications
Updated ChatService to include static readonly fields for reformulation and answering prompts. Replaced the existing ChatSystemPrompt in CreateQuestionAsync. Simplified GetTokenUsage using expression-bodied members. Modified SetChatHistoryAsync to ensure correct chat history updates in the cache.
1 parent 595d6f9 commit 505ccff

1 file changed

Lines changed: 85 additions & 92 deletions

File tree

SqlDatabaseVectorSearch/Services/ChatService.cs

Lines changed: 85 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,81 @@ public class ChatService(IChatCompletionService chatCompletionService, Tokenizer
1515
{
1616
private readonly AppSettings appSettings = appSettingsOptions.Value;
1717

18+
private static readonly string systemPromptForReformulation = """
19+
You are a helpful assistant that reformulates questions to perform embeddings search.
20+
Your task is to reformulate the question taking into account the context of the chat.
21+
The reformulated question must always explicitly contain the subject of the question.
22+
You must reformulate the question in the same language of the user's question. For example, if the user asks a question in English, the answer must be in English.
23+
Never add "in this chat", "in the context of this chat", "in the context of our conversation", "search for" or something like that in your answer.
24+
""";
25+
26+
private static readonly string systemPromptForAnswering = """
27+
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.
28+
29+
For example, if the user asks "What is the capital of Italy?" and in this chat there isn't information about Italy, you should reply something like:
30+
- This information isn't available in the given context.
31+
- I'm sorry, I don't know the answer to that question.
32+
- I don't have that information.
33+
- I don't know.
34+
- Given the context, I can't answer that question.
35+
- I'm sorry, I don't have enough information to answer that question.
36+
37+
Never answer questions that are not related to this chat.
38+
You must answer in the same language as the user's question. For example, if the user asks a question in English, the answer must be in English, no matter the language of the documents.
39+
40+
FORMATTING REQUIREMENT: Your answer MUST ALWAYS end with a period followed by a space before the citations block.
41+
If your answer doesn't naturally end with a period, you MUST add one followed by a space.
42+
43+
After the answer, you need to include citations following the XML format below ONLY IF you know the answer and are providing information from the context. If you do NOT know the answer, DO NOT include the citations section at all.
44+
45+
【<citation document-id="document_id" chunk-id="chunk_id" filename="string" page-number="page_number" index-on-page="index_on-page">exact quote here</citation>
46+
<citation document-id="document_id" chunk-id="chunk_id" filename="string" page-number="page_number" index-on-page="index_on-page">exact quote here</citation>】
47+
48+
The entire list of XML citations MUST be enclosed between 【 and 】 (U+3010 and U+3011) and must exactly match the above format.
49+
The quote in each <citation> MUST be MAXIMUM 5 words, taken word-for-word from the search result.
50+
51+
IMPORTANT CITATION RULES:
52+
1. NEVER put citations inside your answer text.
53+
2. ALWAYS provide your complete answer FIRST.
54+
3. ONLY AFTER completing your answer, add ALL citations in a block at the very end.
55+
4. The citations block MUST be the last thing in your response, with absolutely nothing (no text, no spaces, no newlines, no punctuation, no comments) after it.
56+
5. NEVER reference citations by number or mention them in your answer text.
57+
6. The citations MUST ALWAYS follow the XML format exactly as shown below. Any other format is NOT ACCEPTED.
58+
7. If you add anything after the citations block, your answer will be considered invalid.
59+
8. If you do NOT know the answer, DO NOT include the citations block at all.
60+
9. ALWAYS check that your answer ends with a period followed by a space before adding citations.
61+
62+
---
63+
Example of a correct answer:
64+
The capital of Italy is Rome.
65+
【<citation document-id="123" chunk-id="456" filename="italy.pdf" page-number="1" index-on-page="1">capital of Italy is Rome</citation>】
66+
67+
Example of a correct answer when you do NOT know the answer:
68+
I'm sorry, I don't know the answer to that question.
69+
70+
Example of an incorrect answer (NOT ACCEPTED):
71+
The capital of Italy is Rome
72+
【<citation document-id="123" chunk-id="456" filename="italy.pdf" page-number="1" index-on-page="1">capital of Italy is Rome</citation>】
73+
Thank you for your question.
74+
75+
Another incorrect example (NOT ACCEPTED):
76+
The capital of Italy is Rome.
77+
【<citation document-id="123" chunk-id="456" filename="italy.pdf" page-number="1" index-on-page="1">capital of Italy is Rome</citation>】
78+
[1] italy.pdf, page 1
79+
---
80+
81+
Only the correct format is accepted. If you do not follow the XML format exactly, or if you add anything after the citations block, your answer will be considered invalid.
82+
If you do NOT know the answer, DO NOT include the citations block at all.
83+
Remember to ALWAYS end your answer with a period followed by a space before adding citations.
84+
""";
85+
1886
public async Task<ChatResponse> CreateQuestionAsync(Guid conversationId, string question, CancellationToken cancellationToken = default)
1987
{
2088
var chat = await GetChatHistoryAsync(conversationId, cancellationToken);
2189

2290
var settings = new AzureOpenAIPromptExecutionSettings
2391
{
24-
ChatSystemPrompt = """
25-
You are a helpful assistant that reformulates questions to perform embeddings search.
26-
Your task is to reformulate the question taking into account the context of the chat.
27-
The reformulated question must always explicitly contain the subject of the question.
28-
You must reformulate the question in the same language of the user's question. For example, if the user asks a question in English, the answer must be in English.
29-
Never add "in this chat", "in the context of this chat", "in the context of our conversation", "search for" or something like that in your answer.
30-
"""
92+
ChatSystemPrompt = systemPromptForReformulation
3193
};
3294

3395
var embeddingQuestion = $"""
@@ -39,6 +101,7 @@ The reformulated question must always explicitly contain the subject of the ques
39101
chat.AddUserMessage(embeddingQuestion);
40102

41103
var reformulatedQuestion = await chatCompletionService.GetChatMessageContentAsync(chat, settings, cancellationToken: cancellationToken);
104+
42105
chat.AddAssistantMessage(reformulatedQuestion.Content!);
43106

44107
await UpdateCacheAsync(conversationId, chat, cancellationToken);
@@ -89,28 +152,16 @@ public async IAsyncEnumerable<ChatResponse> AskStreamingAsync(Guid conversationI
89152
}
90153

91154
// Add question and answer to the chat history.
92-
await SetChatHistoryAsync(conversationId, question, answer.ToString(), cancellationToken);
93-
}
94-
95-
private static TokenUsage? GetTokenUsage(Microsoft.SemanticKernel.ChatMessageContent message)
96-
{
97-
if (message.InnerContent is ChatCompletion content && content.Usage is not null)
98-
{
99-
return new(content.Usage.InputTokenCount, content.Usage.OutputTokenCount);
100-
}
101-
102-
return null;
155+
await SetChatHistoryAsync(conversationId, question, answer.ToString(), cancellationToken).ConfigureAwait(false);
103156
}
104157

105-
private static TokenUsage? GetTokenUsage(Microsoft.SemanticKernel.StreamingChatMessageContent message)
106-
{
107-
if (message.InnerContent is StreamingChatCompletionUpdate content && content.Usage is not null)
108-
{
109-
return new(content.Usage.InputTokenCount, content.Usage.OutputTokenCount);
110-
}
158+
private static TokenUsage? GetTokenUsage(Microsoft.SemanticKernel.ChatMessageContent message) =>
159+
message.InnerContent is ChatCompletion content && content.Usage is not null
160+
? new(content.Usage.InputTokenCount, content.Usage.OutputTokenCount) : null;
111161

112-
return null;
113-
}
162+
private static TokenUsage? GetTokenUsage(Microsoft.SemanticKernel.StreamingChatMessageContent message) =>
163+
message.InnerContent is StreamingChatCompletionUpdate content && content.Usage is not null
164+
? new(content.Usage.InputTokenCount, content.Usage.OutputTokenCount) : null;
114165

115166
private (ChatHistory Chat, AzureOpenAIPromptExecutionSettings Settings) CreateChatAsync(IEnumerable<Entities.DocumentChunk> chunks, string question)
116167
{
@@ -119,65 +170,7 @@ public async IAsyncEnumerable<ChatResponse> AskStreamingAsync(Guid conversationI
119170
MaxTokens = appSettings.MaxOutputTokens
120171
};
121172

122-
var chat = new ChatHistory("""
123-
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.
124-
125-
For example, if the user asks "What is the capital of Italy?" and in this chat there isn't information about Italy, you should reply something like:
126-
- This information isn't available in the given context.
127-
- I'm sorry, I don't know the answer to that question.
128-
- I don't have that information.
129-
- I don't know.
130-
- Given the context, I can't answer that question.
131-
- I'm sorry, I don't have enough information to answer that question.
132-
133-
Never answer questions that are not related to this chat.
134-
You must answer in the same language as the user's question. For example, if the user asks a question in English, the answer must be in English, no matter the language of the documents.
135-
136-
FORMATTING REQUIREMENT: Your answer MUST ALWAYS end with a period followed by a space before the citations block.
137-
If your answer doesn't naturally end with a period, you MUST add one followed by a space.
138-
139-
After the answer, you need to include citations following the XML format below ONLY IF you know the answer and are providing information from the context. If you do NOT know the answer, DO NOT include the citations section at all.
140-
141-
【<citation document-id="document_id" chunk-id="chunk_id" filename="string" page-number="page_number" index-on-page="index_on-page">exact quote here</citation>
142-
<citation document-id="document_id" chunk-id="chunk_id" filename="string" page-number="page_number" index-on-page="index_on-page">exact quote here</citation>】
143-
144-
The entire list of XML citations MUST be enclosed between 【 and 】 (U+3010 and U+3011) and must exactly match the above format.
145-
The quote in each <citation> MUST be MAXIMUM 5 words, taken word-for-word from the search result.
146-
147-
IMPORTANT CITATION RULES:
148-
1. NEVER put citations inside your answer text.
149-
2. ALWAYS provide your complete answer FIRST.
150-
3. ONLY AFTER completing your answer, add ALL citations in a block at the very end.
151-
4. The citations block MUST be the last thing in your response, with absolutely nothing (no text, no spaces, no newlines, no punctuation, no comments) after it.
152-
5. NEVER reference citations by number or mention them in your answer text.
153-
6. The citations MUST ALWAYS follow the XML format exactly as shown below. Any other format is NOT ACCEPTED.
154-
7. If you add anything after the citations block, your answer will be considered invalid.
155-
8. If you do NOT know the answer, DO NOT include the citations block at all.
156-
9. ALWAYS check that your answer ends with a period followed by a space before adding citations.
157-
158-
---
159-
Example of a correct answer:
160-
The capital of Italy is Rome.
161-
【<citation document-id="123" chunk-id="456" filename="italy.pdf" page-number="1" index-on-page="1">capital of Italy is Rome</citation>】
162-
163-
Example of a correct answer when you do NOT know the answer:
164-
I'm sorry, I don't know the answer to that question.
165-
166-
Example of an incorrect answer (NOT ACCEPTED):
167-
The capital of Italy is Rome
168-
【<citation document-id="123" chunk-id="456" filename="italy.pdf" page-number="1" index-on-page="1">capital of Italy is Rome</citation>】
169-
Thank you for your question.
170-
171-
Another incorrect example (NOT ACCEPTED):
172-
The capital of Italy is Rome.
173-
【<citation document-id="123" chunk-id="456" filename="italy.pdf" page-number="1" index-on-page="1">capital of Italy is Rome</citation>】
174-
[1] italy.pdf, page 1
175-
---
176-
177-
Only the correct format is accepted. If you do not follow the XML format exactly, or if you add anything after the citations block, your answer will be considered invalid.
178-
If you do NOT know the answer, DO NOT include the citations block at all.
179-
Remember to ALWAYS end your answer with a period followed by a space before adding citations.
180-
""");
173+
var chat = new ChatHistory(systemPromptForAnswering);
181174

182175
var prompt = new StringBuilder($"""
183176
Answer the following question:
@@ -189,7 +182,7 @@ Remember to ALWAYS end your answer with a period followed by a space before addi
189182
""");
190183

191184
var availableTokens = appSettings.MaxInputTokens
192-
- tokenizerService.CountChatCompletionTokens(chat[0].ToString()) // System prompt.
185+
- tokenizerService.CountChatCompletionTokens(systemPromptForAnswering) // System prompt.
193186
- tokenizerService.CountChatCompletionTokens(prompt.ToString()) // Initial user prompt.
194187
- appSettings.MaxOutputTokens; // To ensure there is enough space for the answer.
195188

@@ -215,6 +208,7 @@ Remember to ALWAYS end your answer with a period followed by a space before addi
215208
}
216209

217210
chat.AddUserMessage(prompt.ToString());
211+
218212
return (chat, settings);
219213
}
220214

@@ -230,22 +224,21 @@ private async Task UpdateCacheAsync(Guid conversationId, ChatHistory chat, Cance
230224

231225
private async Task<ChatHistory> GetChatHistoryAsync(Guid conversationId, CancellationToken cancellationToken)
232226
{
233-
var historyCache = await cache.GetOrCreateAsync(conversationId.ToString(), (cancellationToken) =>
227+
var chat = await cache.GetOrCreateAsync(conversationId.ToString(), (cancellationToken) =>
234228
{
235229
return ValueTask.FromResult<ChatHistory>([]);
236230
}, cancellationToken: cancellationToken);
237231

238-
var chat = new ChatHistory(historyCache);
239232
return chat;
240233
}
241234

242235
private async Task SetChatHistoryAsync(Guid conversationId, string question, string answer, CancellationToken cancellationToken)
243236
{
244-
var history = await GetChatHistoryAsync(conversationId, cancellationToken);
237+
var chat = await GetChatHistoryAsync(conversationId, cancellationToken);
245238

246-
history.AddUserMessage(question);
247-
history.AddAssistantMessage(answer);
239+
chat.AddUserMessage(question);
240+
chat.AddAssistantMessage(answer);
248241

249-
await UpdateCacheAsync(conversationId, history, cancellationToken);
242+
await UpdateCacheAsync(conversationId, chat, cancellationToken);
250243
}
251244
}

0 commit comments

Comments
 (0)