Skip to content

Commit d8d6b14

Browse files
author
Milder Hernandez Cagua
committed
Convert to file plugins
1 parent 2667487 commit d8d6b14

File tree

5 files changed

+113
-77
lines changed

5 files changed

+113
-77
lines changed

app/backend/src/main/java/com/microsoft/openai/samples/rag/chat/approaches/semantickernel/JavaSemanticKernelWithMemoryChatApproach.java

Lines changed: 20 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -58,25 +58,6 @@ public class JavaSemanticKernelWithMemoryChatApproach implements RAGApproach<Cha
5858

5959
@Value("${openai.embedding.deployment}")
6060
private String embeddingDeploymentModelId;
61-
62-
private static final String FOLLOW_UP_QUESTIONS_TEMPLATE = """
63-
After answering question, also generate three very brief follow-up questions that the user would likely ask next.
64-
Use double angle brackets to reference the questions, e.g. <<Are there exclusions for prescriptions?>>.
65-
Try not to repeat questions that have already been asked.
66-
Only generate questions and do not generate any text before or after the questions, such as 'Next Questions'
67-
""";
68-
private static final String SYSTEM_CHAT_MESSAGE_TEMPLATE = """
69-
Assistant helps the company employees with their healthcare plan questions, and questions about the employee handbook. Be brief in your answers.
70-
Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. If asking a clarifying question to the user would help, ask the question.
71-
For tabular information return it as an html table. Do not return markdown format.
72-
Each source has a name followed by colon and the actual information, always include the source name for each fact you use in the response. Use square brackets to reference the source, e.g. [info1.txt]. Don't combine sources, list each source separately, e.g. [info1.txt][info2.pdf].
73-
%s
74-
75-
%s
76-
Sources:
77-
%s
78-
""" ;
79-
8061
public JavaSemanticKernelWithMemoryChatApproach(TokenCredential tokenCredential, OpenAIAsyncClient openAIAsyncClient, SearchAsyncClient searchAsyncClient) {
8162
this.tokenCredential = tokenCredential;
8263
this.openAIAsyncClient = openAIAsyncClient;
@@ -87,26 +68,30 @@ public JavaSemanticKernelWithMemoryChatApproach(TokenCredential tokenCredential,
8768
public RAGResponse run(ChatGPTConversation questionOrConversation, RAGOptions options) {
8869
String question = ChatGPTUtils.getLastUserQuestion(questionOrConversation.getMessages());
8970

90-
//Build semantic kernel with Azure Cognitive Search as memory store. AnswerQuestion skill is imported from resources.
71+
// STEP 1: Build semantic kernel with Azure Cognitive Search as memory store. AnswerQuestion skill is imported from resources.
9172
Kernel semanticKernel = buildSemanticKernel(options);
9273

93-
List<MemoryQueryResult> sourcesResult = getSourcesFromConversation(questionOrConversation, semanticKernel, options);
74+
// STEP 2: Retrieve relevant documents using keywords extracted from the chat history
75+
String conversation = ChatGPTUtils.formatAsChatML(questionOrConversation.toOpenAIChatMessages());
76+
List<MemoryQueryResult> sourcesResult = getSourcesFromConversation(conversation, semanticKernel, options);
9477

9578
LOGGER.info("Total {} sources found in cognitive vector store for search query[{}]", sourcesResult.size(), question);
9679

9780
String sources = buildSourcesText(sourcesResult);
9881
List<ContentSource> sourcesList = buildSources(sourcesResult);
9982

100-
// Use ChatCompletion Service to generate a reply
101-
OpenAIChatCompletion chat = (OpenAIChatCompletion) semanticKernel.getService(null, ChatCompletion.class);
102-
OpenAIChatHistory history = buildChatHistory(questionOrConversation, options, chat, sources);
83+
// STEP 3: Generate a contextual and content specific answer using the search results and chat history
84+
SKFunction answerConversation = semanticKernel.getFunction("RAG", "AnswerConversation");
85+
SKContext skcontext = SKBuilders.context().build()
86+
.setVariable("sources", sources)
87+
.setVariable("conversation", conversation)
88+
.setVariable("input", question);
10389

104-
Mono<String> reply = chat.generateMessageAsync(history, null);
90+
Mono<SKContext> reply = answerConversation.invokeAsync(skcontext);
10591

10692
return new RAGResponse.Builder()
107-
//.prompt(plan.toPlanString())
10893
.prompt("placeholders for prompt")
109-
.answer(reply.block())
94+
.answer(reply.block().getResult())
11095
.sources(sourcesList)
11196
.sourcesAsText(sources)
11297
.question(question)
@@ -118,44 +103,20 @@ public void runStreaming(ChatGPTConversation questionOrConversation, RAGOptions
118103
throw new IllegalStateException("Streaming not supported for this approach");
119104
}
120105

121-
private List<MemoryQueryResult> getSourcesFromConversation (ChatGPTConversation conversation, Kernel kernel, RAGOptions options) {
122-
String searchQueryPrompt = """
123-
Generate a search query for the below conversation.
124-
Do not include cited source filenames and document names e.g info.txt or doc.pdf in the search query terms.
125-
Do not include any text inside [] or <<>> in the search query terms.
126-
Do not enclose the search query in quotes or double quotes.
127-
conversation:
128-
{{$conversation}}
129-
""" ;
130-
131-
SKContext skcontext = SKBuilders.context().build()
132-
.setVariable("conversation", ChatGPTUtils.formatAsChatML(conversation.toOpenAIChatMessages()));
133-
134-
SKFunction searchQuery = kernel
135-
.getSemanticFunctionBuilder()
136-
.withPromptTemplate(searchQueryPrompt)
137-
.withFunctionName("searchQuery")
138-
.withCompletionConfig(
139-
new PromptTemplateConfig.CompletionConfig(
140-
0.2,
141-
1,
142-
0.0,
143-
0.0,
144-
1024
145-
)
146-
)
147-
.build();
106+
private List<MemoryQueryResult> getSourcesFromConversation (String conversation, Kernel kernel, RAGOptions options) {
107+
SKFunction extractKeywords = kernel.getFunction("RAG", "ExtractKeywords");
108+
SKContext skcontext = SKBuilders.context().build().setVariable("conversation", conversation);
148109

149-
Mono<SKContext> result = searchQuery.invokeAsync(skcontext);
150-
String query = result.block().getResult();
110+
Mono<SKContext> result = extractKeywords.invokeAsync(skcontext);
111+
String searchQuery = result.block().getResult();
151112

152113
/**
153114
* Use semantic kernel built-in memory.searchAsync. It uses OpenAI to generate embeddings for the provided question.
154115
* Question embeddings are provided to cognitive search via search options.
155116
*/
156117
List<MemoryQueryResult> memoryResult = kernel.getMemory().searchAsync(
157118
indexName,
158-
query,
119+
searchQuery,
159120
options.getTop(),
160121
0.5f,
161122
false)
@@ -164,25 +125,6 @@ private List<MemoryQueryResult> getSourcesFromConversation (ChatGPTConversation
164125
return memoryResult;
165126
}
166127

167-
private OpenAIChatHistory buildChatHistory(ChatGPTConversation conversation, RAGOptions options, OpenAIChatCompletion chat,
168-
String sources) {
169-
String systemMessage = SYSTEM_CHAT_MESSAGE_TEMPLATE.formatted(
170-
options.isSuggestFollowupQuestions() ? FOLLOW_UP_QUESTIONS_TEMPLATE : "",
171-
options.getPromptTemplate() != null ? options.getPromptTemplate() : "",
172-
sources);
173-
174-
OpenAIChatHistory chatHistory = chat.createNewChat(systemMessage);
175-
conversation.getMessages().forEach(message -> {
176-
if(message.role() == ChatGPTMessage.ChatRole.USER){
177-
chatHistory.addUserMessage(message.content());
178-
} else if(message.role() == ChatGPTMessage.ChatRole.ASSISTANT) {
179-
chatHistory.addAssistantMessage(message.content());
180-
}
181-
});
182-
183-
return chatHistory;
184-
}
185-
186128
private List<ContentSource> buildSources(List<MemoryQueryResult> memoryResult) {
187129
return memoryResult
188130
.stream()
@@ -224,7 +166,8 @@ private Kernel buildSemanticKernel(RAGOptions options) {
224166
.build())
225167
.build();
226168

227-
kernelWithACS.importSkillFromResources("semantickernel/Plugins", "RAG", "AnswerQuestion", null);
169+
kernelWithACS.importSkillFromResources("semantickernel/Plugins", "RAG", "AnswerConversation", null);
170+
kernelWithACS.importSkillFromResources("semantickernel/Plugins", "RAG", "ExtractKeywords", null);
228171
return kernelWithACS;
229172
}
230173

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"schema": 1,
3+
"description": "Answer a chat conversation question based on the provided sources",
4+
"type": "completion",
5+
"completion": {
6+
"max_tokens": 1024,
7+
"temperature": 0.2,
8+
"top_p": 1,
9+
"presence_penalty": 0.0,
10+
"frequency_penalty": 0.0
11+
},
12+
"input": {
13+
"parameters": [
14+
{
15+
"name": "input",
16+
"description": "Question to answer"
17+
},
18+
{
19+
"name": "sources",
20+
"description": "Information used to answer the question"
21+
},
22+
{
23+
"name": "conversation",
24+
"description": "Chat history"
25+
}
26+
]
27+
}
28+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
You are an intelligent assistant helping Contoso Inc employees with their healthcare plan questions and employee handbook questions.
2+
Use 'you' to refer to the individual asking the questions even if they ask with 'I'.
3+
Answer the following question using only the information below.
4+
For tabular information return it as an html table. Do not return markdown format.
5+
Each source has a name followed by colon and the actual information, always include the source name for each fact you use in the response.
6+
If you cannot answer using the sources below, say you don't know.
7+
8+
[EXAMPLES]
9+
[EXAMPLE 1]
10+
[INFORMATION]
11+
info1.txt: deductibles depend on whether you are in-network or out-of-network. In-network deductibles are $500 for employee and $1000 for family. Out-of-network deductibles are $1000 for employee and $2000 for family.
12+
info2.pdf: Overlake is in-network for the employee plan.
13+
info3.pdf: Overlake is the name of the area that includes a park and ride near Bellevue.
14+
info4.pdf: In-network institutions include Overlake, Swedish and others in the region
15+
[END INFORMATION]
16+
Question: What is the deductible for the employee plan for a visit to Overlake in Bellevue?
17+
Answer: In-network deductibles are $500 for employee and $1000 for family [info1.txt] and Overlake is in-network for the employee plan [info2.pdf][info4.pdf].
18+
[END EXAMPLE 1]
19+
20+
21+
[EXAMPLE 2]
22+
[INFORMATION]
23+
info1.txt: deductibles depend on whether you are in-network or out-of-network. In-network deductibles are $500 for employee and $1000 for family. Out-of-network deductibles are $1000 for employee and $2000 for family.
24+
info2.pdf: Overlake is in-network for the employee plan.
25+
info3.pdf: Overlake is the name of the area that includes a park and ride near Bellevue.
26+
info4.pdf: In-network institutions include Overlake, Swedish and others in the region
27+
[END INFORMATION]
28+
Question: what are the responsibilities of the product manager?
29+
Answer: I do not have enough information to answer that.
30+
[END EXAMPLE 2]
31+
[END EXAMPLES]
32+
33+
[INFORMATION]
34+
{{$sources}}
35+
{{$conversation}}
36+
[END INFORMATION]
37+
38+
Question: {{$input}}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"schema": 1,
3+
"description": "Extract keywords from a conversation to form a search query",
4+
"type": "completion",
5+
"completion": {
6+
"max_tokens": 1024,
7+
"temperature": 0.2,
8+
"top_p": 1,
9+
"presence_penalty": 0.0,
10+
"frequency_penalty": 0.0
11+
},
12+
"input": {
13+
"parameters": [
14+
{
15+
"name": "conversation",
16+
"description": "Chat conversation"
17+
}
18+
]
19+
}
20+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Generate a search query for the below conversation.
2+
Do not include cited source filenames and document names e.g info.txt or doc.pdf in the search query terms.
3+
Do not include any text inside [] or <<>> in the search query terms.
4+
Do not enclose the search query in quotes or double quotes.
5+
6+
conversation:
7+
{{$conversation}}

0 commit comments

Comments
 (0)