Skip to content

Commit a544c39

Browse files
committed
improving code documentation
1 parent 4c32dd6 commit a544c39

File tree

7 files changed

+79
-23
lines changed

7 files changed

+79
-23
lines changed

app/backend/src/main/java/com/microsoft/openai/samples/rag/ask/approaches/PlainJavaAskApproach.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import java.util.List;
2323

2424
/**
25-
* Simple retrieve-then-read java implementation, using the Cognitive Search and OpenAI APIs directly. It first retrieves
26-
* top documents from search, then constructs a prompt with them, and then uses OpenAI to generate a completion
27-
* (answer) with that prompt.
25+
Use Cognitive Search and Java OpenAI APIs.
26+
It first retrieves top documents from search and use them to build a prompt.
27+
Then, it uses OpenAI to generate an answer for the user question.
28+
Several cognitive search retrieval options are available: Text, Vector, Hybrid.
29+
When Hybrid and Vector are selected an additional call to OpenAI is required to generate embeddings vector for the question.
2830
*/
2931
@Component
3032
public class PlainJavaAskApproach implements RAGApproach<String, RAGResponse> {
@@ -49,6 +51,8 @@ public PlainJavaAskApproach(FactsRetrieverProvider factsRetrieverProvider, OpenA
4951
public RAGResponse run(String question, RAGOptions options) {
5052
//Get instance of retriever based on the retrieval mode: hybryd, text, vectors.
5153
Retriever factsRetriever = factsRetrieverProvider.getFactsRetriever(options);
54+
55+
//STEP 1: Retrieve relevant documents using user question as query
5256
List<ContentSource> sources = factsRetriever.retrieveFromQuestion(question, options);
5357
LOGGER.info("Total {} sources found in cognitive search for keyword search query[{}]", sources.size(),
5458
question);
@@ -62,12 +66,14 @@ public RAGResponse run(String question, RAGOptions options) {
6266
customPrompt = customPrompt.substring(1);
6367
}
6468

69+
//STEP 2: Build a prompt using RAG options to see if prompt should be replaced or extended.
6570
var answerQuestionChatTemplate = new AnswerQuestionChatTemplate(customPrompt, replacePrompt);
6671

72+
//STEP 3: Build the chat conversation with grounded messages using the retrieved facts
6773
var groundedChatMessages = answerQuestionChatTemplate.getMessages(question, sources);
6874
var chatCompletionsOptions = ChatGPTUtils.buildDefaultChatCompletionsOptions(groundedChatMessages);
6975

70-
// STEP 3: Generate a contextual and content specific answer using the retrieve facts
76+
// STEP 4: Generate a contextual and content specific answer
7177
ChatCompletions chatCompletions = openAIProxy.getChatCompletions(chatCompletionsOptions);
7278

7379
LOGGER.info("Chat completion generated with Prompt Tokens[{}], Completions Tokens[{}], Total Tokens[{}]",

app/backend/src/main/java/com/microsoft/openai/samples/rag/ask/approaches/semantickernel/JavaSemanticKernelChainsApproach.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.microsoft.openai.samples.rag.ask.approaches.semantickernel;
22

33
import com.azure.ai.openai.OpenAIAsyncClient;
4+
import com.azure.core.annotation.Get;
45
import com.microsoft.openai.samples.rag.approaches.ContentSource;
56
import com.microsoft.openai.samples.rag.approaches.RAGApproach;
67
import com.microsoft.openai.samples.rag.approaches.RAGOptions;
@@ -26,6 +27,7 @@
2627
/**
2728
* Use Java Semantic Kernel framework with semantic and native functions chaining. It uses an imperative style for AI orchestration through semantic kernel functions chaining.
2829
* InformationFinder.Search native function and RAG.AnswerQuestion semantic function are called sequentially.
30+
* Several cognitive search retrieval options are available: Text, Vector, Hybrid.
2931
*/
3032
@Component
3133
public class JavaSemanticKernelChainsApproach implements RAGApproach<String, RAGResponse> {
@@ -57,20 +59,30 @@ public JavaSemanticKernelChainsApproach(CognitiveSearchProxy cognitiveSearchProx
5759
@Override
5860
public RAGResponse run(String question, RAGOptions options) {
5961

62+
//Build semantic kernel context
6063
Kernel semanticKernel = buildSemanticKernel(options);
64+
65+
66+
//STEP 1: Retrieve relevant documents using user question. It reuses the CognitiveSearchRetriever appraoch through the CognitiveSearchPlugin native function.
6167
SKContext searchContext =
6268
semanticKernel.runAsync(
6369
question,
6470
semanticKernel.getSkill("InformationFinder").getFunction("Search", null)).block();
6571

6672
var sources = formSourcesList(searchContext.getResult());
6773

74+
//STEP 2: Build a SK context with the sources retrieved from the memory store and the user question.
6875
var answerVariables = SKBuilders.variables()
6976
.withVariable("sources", searchContext.getResult())
7077
.withVariable("input", question)
7178
.build();
7279

73-
SKContext answerExecutionContext =
80+
/**
81+
* STEP 3:
82+
* Get a reference of the semantic function [AnswerQuestion] of the [RAG] plugin (a.k.a. skill) from the SK skills registry and provide it with the pre-built context.
83+
* Triggering Open AI to get an answerVariables.
84+
*/
85+
SKContext answerExecutionContext =
7486
semanticKernel.runAsync(answerVariables,
7587
semanticKernel.getSkill("RAG").getFunction("AnswerQuestion", null)).block();
7688
return new RAGResponse.Builder()
@@ -108,6 +120,14 @@ private List<ContentSource> formSourcesList(String result) {
108120
.collect(Collectors.toList());
109121
}
110122

123+
/**
124+
* Build semantic kernel context with AnswerQuestion semantic function and InformationFinder.Search native function.
125+
* AnswerQuestion is imported from src/main/resources/semantickernel/Plugins.
126+
* InformationFinder.Search is implemented in a traditional Java class method: CognitiveSearchPlugin.search
127+
*
128+
* @param options
129+
* @return
130+
*/
111131
private Kernel buildSemanticKernel( RAGOptions options) {
112132
Kernel kernel = SKBuilders.kernel()
113133
.withDefaultAIService(SKBuilders.chatCompletion()

app/backend/src/main/java/com/microsoft/openai/samples/rag/ask/approaches/semantickernel/JavaSemanticKernelPlannerApproach.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@
2222
import java.util.Set;
2323

2424
/**
25-
* Use Java Semantic Kernel framework with built-in Planner for functions orchestration. It uses a declarative style for AI orchestration through the built-in SequentialPlanner.
26-
* SequentialPlanner call OpenAI to generate a plan for answering a question using available skills/plugins: InformationFinder and RAG
25+
* Use Java Semantic Kernel framework with built-in Planner for functions orchestration.
26+
* It uses a declarative style for AI orchestration through the built-in SequentialPlanner.
27+
* SequentialPlanner call OpenAI to generate a plan for answering a question using available plugins: InformationFinder and RAG
2728
*/
2829
@Component
2930
public class JavaSemanticKernelPlannerApproach implements RAGApproach<String, RAGResponse> {
3031

3132
private static final Logger LOGGER = LoggerFactory.getLogger(JavaSemanticKernelPlannerApproach.class);
32-
private static final String PLAN_PROMPT = """
33+
private static final String GOAL_PROMPT = """
3334
Take the input as a question and answer it finding any information needed
3435
""";
3536
private final CognitiveSearchProxy cognitiveSearchProxy;
@@ -55,6 +56,7 @@ public JavaSemanticKernelPlannerApproach(CognitiveSearchProxy cognitiveSearchPro
5556
@Override
5657
public RAGResponse run(String question, RAGOptions options) {
5758

59+
//Build semantic kernel context
5860
Kernel semanticKernel = buildSemanticKernel(options);
5961

6062
SequentialPlanner sequentialPlanner = new SequentialPlanner(semanticKernel, new SequentialPlannerRequestSettings(
@@ -66,10 +68,12 @@ public RAGResponse run(String question, RAGOptions options) {
6668
1024
6769
), null);
6870

69-
var plan = Objects.requireNonNull(sequentialPlanner.createPlanAsync(PLAN_PROMPT).block());
71+
//STEP 1: ask Open AI to generate an execution plan for the goal contained in GOAL_PROMPT.
72+
var plan = Objects.requireNonNull(sequentialPlanner.createPlanAsync(GOAL_PROMPT).block());
7073

7174
LOGGER.debug("Semantic kernel plan calculated is [{}]", plan.toPlanString());
7275

76+
//STEP 2: execute the plan calculated by the planner using Open AI
7377
SKContext planContext = Objects.requireNonNull(plan.invokeAsync(question).block());
7478

7579
return new RAGResponse.Builder()
@@ -87,6 +91,14 @@ public void runStreaming(String questionOrConversation, RAGOptions options, Outp
8791
throw new IllegalStateException("Streaming not supported for this approach");
8892
}
8993

94+
/**
95+
* Build semantic kernel context with AnswerQuestion semantic function and InformationFinder.Search native function.
96+
* AnswerQuestion is imported from src/main/resources/semantickernel/Plugins.
97+
* InformationFinder.Search is implemented in a traditional Java class method: CognitiveSearchPlugin.search
98+
*
99+
* @param options
100+
* @return
101+
*/
90102
private Kernel buildSemanticKernel( RAGOptions options) {
91103
Kernel kernel = SKBuilders.kernel()
92104
.withDefaultAIService(SKBuilders.chatCompletion()

app/backend/src/main/java/com/microsoft/openai/samples/rag/ask/approaches/semantickernel/JavaSemanticKernelWithMemoryApproach.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
import java.util.stream.Collectors;
2929

3030
/**
31-
* Accomplish the same task as in the PlainJavaAskApproach approach but using Semantic Kernel framework:
32-
* 1. Memory abstraction is used for vector search capability. It uses Azure Cognitive Search as memory store.
33-
* 2. Semantic function has been defined to ask question using sources from memory search results
31+
Use Java Semantic Kernel framework with built-in MemoryStore for embeddings similarity search.
32+
A semantic function is defined in RAG.AnswerQuestion (src/main/resources/semantickernel/Plugins) to build the prompt template which is grounded using results from the Memory Store.
33+
A customized version of SK built-in CognitiveSearchMemoryStore is used to map index fields populated by the documents ingestion process.
3434
*/
3535
@Component
3636
public class JavaSemanticKernelWithMemoryApproach implements RAGApproach<String, RAGResponse> {
@@ -66,10 +66,11 @@ public JavaSemanticKernelWithMemoryApproach(TokenCredential tokenCredential, Ope
6666
@Override
6767
public RAGResponse run(String question, RAGOptions options) {
6868

69-
//Build semantic kernel with Azure Cognitive Search as memory store. AnswerQuestion skill is imported from resources.
69+
//Build semantic kernel context with Azure Cognitive Search as memory store. AnswerQuestion skill is imported from src/main/resources/semantickernel/Plugins.
7070
Kernel semanticKernel = buildSemanticKernel(options);
7171

7272
/**
73+
* STEP 1: Retrieve relevant documents using user question
7374
* Use semantic kernel built-in memory.searchAsync. It uses OpenAI to generate embeddings for the provided question.
7475
* Question embeddings are provided to cognitive search via search options.
7576
*/
@@ -86,16 +87,18 @@ public RAGResponse run(String question, RAGOptions options) {
8687
String sources = buildSourcesText(memoryResult);
8788
List<ContentSource> sourcesList = buildSources(memoryResult);
8889

90+
//STEP 2: Build a SK context with the sources retrieved from the memory store and the user question.
8991
SKContext skcontext = SKBuilders.context().build()
9092
.setVariable("sources", sources)
9193
.setVariable("input", question);
9294

93-
95+
//STEP 3: Get a reference of the semantic function [AnswerQuestion] of the [RAG] plugin (a.k.a. skill) from the SK skills registry and provide it with the pre-built context.
9496
Mono<SKContext> result = semanticKernel.getFunction("RAG", "AnswerQuestion").invokeAsync(skcontext);
9597

9698
return new RAGResponse.Builder()
9799
//.prompt(plan.toPlanString())
98-
.prompt("placeholders for prompt")
100+
.prompt("Prompt is managed by SK and can't be displayed here. See App logs for prompt")
101+
//STEP 4: triggering Open AI to get an answer
99102
.answer(result.block().getResult())
100103
.sources(sourcesList)
101104
.sourcesAsText(sources)

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525

2626
/**
2727
* Simple chat-read-retrieve-read java implementation, using the Cognitive Search and OpenAI APIs directly.
28-
* It uses the ChatGPT API to turn the user question into a good search query.
29-
* It queries Azure Cognitive Search for search results for that query (optionally using the vector embeddings for that query).
30-
* It then combines the search results and original user question, and asks ChatGPT API to answer the question based on the sources. It includes the last 4K of message history as well (or however many tokens are allowed by the deployed model).
28+
* It first calls OpenAI to generate a search keyword for the chat history and then answer to the last chat question.
29+
* Several cognitive search retrieval options are available: Text, Vector, Hybrid.
30+
* When Hybrid and Vector are selected an additional call to OpenAI is required to generate embeddings vector for the chat extracted keywords.
3131
*/
3232
@Component
3333
public class PlainJavaChatApproach implements RAGApproach<ChatGPTConversation, RAGResponse> {
@@ -54,13 +54,15 @@ public PlainJavaChatApproach(
5454
*/
5555
@Override
5656
public RAGResponse run(ChatGPTConversation questionOrConversation, RAGOptions options) {
57-
57+
//Get instance of retriever based on the retrieval mode: hybryd, text, vectors.
5858
Retriever factsRetriever = factsRetrieverProvider.getFactsRetriever(options);
59+
60+
//STEP 1: Retrieve relevant documents using kewirds extracted from the chat history. An additional call to OpenAI is required to generate keywords.
5961
List<ContentSource> sources = factsRetriever.retrieveFromConversation(questionOrConversation, options);
6062
LOGGER.info("Total {} sources retrieved", sources.size());
6163

6264

63-
// Replace whole prompt is not supported yet
65+
//STEP 2: Build a grounded prompt using the retrieved documents. RAG options is used to configure additional prompt extension like 'suggesting follow up questions' option.
6466
var semanticSearchChat = new SemanticSearchChat(questionOrConversation, sources, options.getPromptTemplate(), false, options.isSuggestFollowupQuestions());
6567
var chatCompletionsOptions = ChatGPTUtils.buildDefaultChatCompletionsOptions(semanticSearchChat.getMessages());
6668

app/backend/src/main/java/com/microsoft/openai/samples/rag/retrieval/CognitiveSearchRetriever.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323

2424
/**
2525
* Cognitive Search retriever implementation that uses the Cognitive Search API to retrieve documents from the search
26-
* index. If retrieval mode is set to vectors or hybrid, it will use the OpenAI API to convert the user's query text to an embedding vector
26+
* index.
27+
* If retrieval mode is set to vectors or hybrid, it will use OpenAI embedding API to convert the user's query text to an embedding vector
2728
* The hybrid search is specific to cognitive search feature which fuses the best of text search and vector search.
2829
*/
2930
@Component
@@ -36,6 +37,13 @@ public CognitiveSearchRetriever(CognitiveSearchProxy cognitiveSearchProxy, OpenA
3637
this.cognitiveSearchProxy = cognitiveSearchProxy;
3738
this.openAIProxy = openAIProxy;
3839
}
40+
41+
/**
42+
*
43+
* @param question
44+
* @param ragOptions
45+
* @return the top documents retrieved from the search index based on the user's query text
46+
*/
3947
@Override
4048
public List<ContentSource> retrieveFromQuestion(String question, RAGOptions ragOptions) {
4149
// step 1. Convert the user's query text to an embedding
@@ -63,7 +71,12 @@ public List<ContentSource> retrieveFromQuestion(String question, RAGOptions ragO
6371

6472
}
6573

66-
74+
/**
75+
*
76+
* @param conversation
77+
* @param ragOptions
78+
* @return facts retrieved from the search index based on GPT optimized search keywords extracted from the chat history
79+
*/
6780
@Override
6881
public List<ContentSource> retrieveFromConversation(ChatGPTConversation conversation, RAGOptions ragOptions) {
6982

app/backend/src/main/java/com/microsoft/openai/samples/rag/retrieval/FactsRetrieverProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class FactsRetrieverProvider implements ApplicationContextAware {
1616
* @return retriever implementation
1717
*/
1818
public Retriever getFactsRetriever(RAGOptions options) {
19-
//default to Cognitive Semantic Search for MVP. More useful in the future to support multiple retrivial systems (RedisSearch.Pinecone, etc)
19+
//default to Cognitive Semantic Search for MVP. More useful in the future to support multiple retrieval systems (RedisSearch.Pinecone, etc)
2020
switch (options.getRetrievalMode()){
2121
case vectors,hybrid,text:
2222
return this.applicationContext.getBean(CognitiveSearchRetriever.class);

0 commit comments

Comments
 (0)