Skip to content

Commit 1d0975d

Browse files
committed
added support for cognitive search vector database. App insights monitoring added as well. README updated
1 parent 6f7b426 commit 1d0975d

File tree

24 files changed

+3149
-3076
lines changed

24 files changed

+3149
-3076
lines changed

app/backend/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<properties>
1717
<java.version>17</java.version>
1818
<spring-cloud-azure.version>4.9.0</spring-cloud-azure.version>
19-
<azure-search.version>11.6.0-beta.6</azure-search.version>
19+
<azure-search.version>11.6.0-beta.8</azure-search.version>
2020
<azure-openai.version>1.0.0-beta.2</azure-openai.version>
2121
<semantic-kernel.version>0.2.8-alpha</semantic-kernel.version>
2222
<mockito-inline.version>4.5.1</mockito-inline.version>

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
package com.microsoft.openai.samples.rag.approaches;
22

3-
import com.microsoft.openai.samples.rag.ask.approaches.RetrieveThenReadApproach;
4-
import com.microsoft.openai.samples.rag.ask.approaches.semantickernel.ReadRetrieveReadApproach;
5-
import com.microsoft.openai.samples.rag.chat.approaches.ChatReadRetrieveReadApproach;
3+
import com.microsoft.openai.samples.rag.ask.approaches.PlainJavaAskApproach;
4+
import com.microsoft.openai.samples.rag.ask.approaches.semantickernel.JavaSemanticKernelAskApproach;
5+
import com.microsoft.openai.samples.rag.chat.approaches.PlainJavaChatApproach;
66
import org.springframework.context.ApplicationContext;
77
import org.springframework.context.ApplicationContextAware;
88
import org.springframework.stereotype.Component;
99

1010
@Component
1111
public class RAGApproachFactorySpringBootImpl implements RAGApproachFactory, ApplicationContextAware {
1212

13-
private static final String READ_RETRIEVE_READ = "rrr";
14-
private static final String RETRIEVE_THEN_READ = "rtr";
13+
private static final String JAVA_OPENAI_SDK = "jos";
14+
private static final String JAVA_SEMANTIC_KERNEL = "jsk";
15+
16+
private static final String JAVA_SEMANTIC_KERNEL_VECTORS = "jskv";
1517
private ApplicationContext applicationContext;
1618

1719
/**
@@ -23,14 +25,14 @@ public class RAGApproachFactorySpringBootImpl implements RAGApproachFactory, App
2325
@Override
2426
public RAGApproach createApproach(String approachName, RAGType ragType) {
2527

26-
if (ragType.equals(RAGType.CHAT) && READ_RETRIEVE_READ.equals(approachName)) {
27-
return applicationContext.getBean(ChatReadRetrieveReadApproach.class);
28+
if (ragType.equals(RAGType.CHAT) && JAVA_OPENAI_SDK.equals(approachName)) {
29+
return applicationContext.getBean(PlainJavaChatApproach.class);
2830

2931
} else if (ragType.equals(RAGType.ASK)) {
30-
if (RETRIEVE_THEN_READ.equals(approachName))
31-
return applicationContext.getBean(RetrieveThenReadApproach.class);
32-
else if (READ_RETRIEVE_READ.equals(approachName))
33-
return applicationContext.getBean(ReadRetrieveReadApproach.class);
32+
if (JAVA_OPENAI_SDK.equals(approachName))
33+
return applicationContext.getBean(PlainJavaAskApproach.class);
34+
else if (JAVA_SEMANTIC_KERNEL.equals(approachName))
35+
return applicationContext.getBean(JavaSemanticKernelAskApproach.class);
3436
}
3537
//if this point is reached then the combination of approach and rag type is not supported
3638
throw new IllegalArgumentException("Invalid combination for approach[%s] and rag type[%s]: ".formatted(approachName, ragType));

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
public class RAGOptions {
44

5+
private RetrievalMode retrievalMode;
6+
57
private boolean semanticRanker;
68
private boolean semanticCaptions;
79
private boolean suggestFollowupQuestions;
@@ -12,6 +14,9 @@ public class RAGOptions {
1214
private RAGOptions() {
1315
}
1416

17+
public RetrievalMode getRetrievalMode() {
18+
return retrievalMode;
19+
}
1520
public boolean isSemanticRanker() {
1621
return semanticRanker;
1722
}
@@ -37,6 +42,7 @@ public boolean isSuggestFollowupQuestions() {
3742
}
3843

3944
public static class Builder {
45+
private RetrievalMode retrievalMode;
4046
private boolean semanticRanker;
4147
private boolean semanticCaptions;
4248
private String excludeCategory;
@@ -45,6 +51,10 @@ public static class Builder {
4551

4652
private boolean suggestFollowupQuestions;
4753

54+
public Builder retrievialMode(String retrievialMode) {
55+
this.retrievalMode = RetrievalMode.valueOf(retrievialMode);
56+
return this;
57+
}
4858
public Builder semanticRanker(boolean semanticRanker) {
4959
this.semanticRanker = semanticRanker;
5060
return this;
@@ -77,12 +87,12 @@ public Builder top(Integer top) {
7787

7888
public RAGOptions build() {
7989
RAGOptions ragOptions = new RAGOptions();
90+
ragOptions.retrievalMode = this.retrievalMode;
8091
ragOptions.semanticRanker = this.semanticRanker;
8192
ragOptions.semanticCaptions = this.semanticCaptions;
8293
ragOptions.suggestFollowupQuestions = this.suggestFollowupQuestions;
8394
ragOptions.excludeCategory = this.excludeCategory;
8495
ragOptions.promptTemplate = this.promptTemplate;
85-
8696
ragOptions.top = this.top;
8797
return ragOptions;
8898
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.microsoft.openai.samples.rag.approaches;
2+
3+
public enum RetrievalMode {
4+
hybrid, vectors, text;
5+
6+
}
Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,29 @@
66
import com.microsoft.openai.samples.rag.approaches.RAGOptions;
77
import com.microsoft.openai.samples.rag.approaches.RAGResponse;
88
import com.microsoft.openai.samples.rag.common.ChatGPTUtils;
9+
import com.microsoft.openai.samples.rag.retrieval.FactsRetrieverProvider;
910
import com.microsoft.openai.samples.rag.proxy.OpenAIProxy;
10-
import com.microsoft.openai.samples.rag.retrieval.CognitiveSearchRetriever;
1111
import com.microsoft.openai.samples.rag.retrieval.Retriever;
1212
import org.slf4j.Logger;
1313
import org.slf4j.LoggerFactory;
14-
import org.springframework.context.ApplicationContext;
15-
import org.springframework.context.ApplicationContextAware;
1614
import org.springframework.stereotype.Component;
1715

1816
import java.util.List;
1917

2018
/**
21-
* Simple retrieve-then-read implementation, using the Cognitive Search and OpenAI APIs directly. It first retrieves
19+
* Simple retrieve-then-read java implementation, using the Cognitive Search and OpenAI APIs directly. It first retrieves
2220
* top documents from search, then constructs a prompt with them, and then uses OpenAI to generate a completion
2321
* (answer) with that prompt.
2422
*/
2523
@Component
26-
public class RetrieveThenReadApproach implements RAGApproach<String, RAGResponse>, ApplicationContextAware {
24+
public class PlainJavaAskApproach implements RAGApproach<String, RAGResponse> {
2725

28-
private static final Logger LOGGER = LoggerFactory.getLogger(RetrieveThenReadApproach.class);
29-
private ApplicationContext applicationContext;
26+
private static final Logger LOGGER = LoggerFactory.getLogger(PlainJavaAskApproach.class);
3027
private final OpenAIProxy openAIProxy;
31-
private final Retriever factsRetriever;
28+
private final FactsRetrieverProvider factsRetrieverProvider;
3229

33-
public RetrieveThenReadApproach(Retriever factsRetriever, OpenAIProxy openAIProxy) {
34-
this.factsRetriever = factsRetriever;
30+
public PlainJavaAskApproach(FactsRetrieverProvider factsRetrieverProvider, OpenAIProxy openAIProxy) {
31+
this.factsRetrieverProvider = factsRetrieverProvider;
3532
this.openAIProxy = openAIProxy;
3633
}
3734

@@ -44,7 +41,8 @@ public RetrieveThenReadApproach(Retriever factsRetriever, OpenAIProxy openAIProx
4441
public RAGResponse run(String question, RAGOptions options) {
4542
//TODO exception handling
4643

47-
Retriever factsRetriever = getFactsRetriever(options);
44+
//Get instance of retriever based on the retrieval mode: hybryd, text, vectors
45+
Retriever factsRetriever = factsRetrieverProvider.getFactsRetriever(options);
4846
List<ContentSource> sources = factsRetriever.retrieveFromQuestion(question, options);
4947
LOGGER.info("Total {} sources found in cognitive search for keyword search query[{}]", sources.size(),
5048
question);
@@ -80,19 +78,5 @@ public RAGResponse run(String question, RAGOptions options) {
8078

8179
}
8280

83-
/**
84-
*
85-
* @param options rag options containing search types(Cognitive Semantic Search, Cognitive Vector Search, Cognitive Hybrid Search, Semantic Kernel Memory) )
86-
* @return retriever implementation
87-
*/
88-
private CognitiveSearchRetriever getFactsRetriever(RAGOptions options) {
89-
//default to Cognitive Semantic Search for MVP.
90-
return this.applicationContext.getBean(CognitiveSearchRetriever.class);
91-
92-
}
93-
94-
public void setApplicationContext(ApplicationContext applicationContext) {
95-
this.applicationContext = applicationContext;
96-
}
9781

9882
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
* Accomplish the same task as in the Retrieve-then-read approach but using Semantic Kernel framework and Planner goal oriented concept.
2626
*/
2727
@Component
28-
public class ReadRetrieveReadApproach implements RAGApproach<String, RAGResponse> {
28+
public class JavaSemanticKernelAskApproach implements RAGApproach<String, RAGResponse> {
2929

30-
private static final Logger LOGGER = LoggerFactory.getLogger(ReadRetrieveReadApproach.class);
30+
private static final Logger LOGGER = LoggerFactory.getLogger(JavaSemanticKernelAskApproach.class);
3131
private static final String PLAN_PROMPT = """
3232
Take the input as a question and answer it finding any information needed
3333
""";
@@ -37,7 +37,7 @@ public class ReadRetrieveReadApproach implements RAGApproach<String, RAGResponse
3737
@Value("${openai.chatgpt.deployment}")
3838
private String gptChatDeploymentModelId;
3939

40-
public ReadRetrieveReadApproach(CognitiveSearchProxy cognitiveSearchProxy, OpenAIAsyncClient openAIAsyncClient) {
40+
public JavaSemanticKernelAskApproach(CognitiveSearchProxy cognitiveSearchProxy, OpenAIAsyncClient openAIAsyncClient) {
4141
this.cognitiveSearchProxy = cognitiveSearchProxy;
4242
this.openAIAsyncClient = openAIAsyncClient;
4343
}
@@ -92,6 +92,7 @@ private Kernel buildSemanticKernel( RAGOptions options) {
9292
.build())
9393
.build();
9494

95+
//TODO: should be refactored to reuse the facts retriever provider injected by spring
9596
kernel.importSkill(new CognitiveSearchPlugin(this.cognitiveSearchProxy, CognitiveSearchPlugin.buildSearchOptions(options),options), "CognitiveSearchPlugin");
9697

9798
kernel.importSkillFromResources(

app/backend/src/main/java/com/microsoft/openai/samples/rag/ask/controller/AskController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public ResponseEntity<AskResponse> openAIAsk(@RequestBody AskRequest askRequest)
4646
}
4747

4848
var ragOptions = new RAGOptions.Builder()
49+
.retrievialMode(askRequest.getOverrides().getRetrievalMode())
4950
.semanticRanker(askRequest.getOverrides().isSemanticRanker())
5051
.semanticCaptions(askRequest.getOverrides().isSemanticCaptions())
5152
.excludeCategory(askRequest.getOverrides().getExcludeCategory())
Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,32 @@
77
import com.microsoft.openai.samples.rag.approaches.RAGResponse;
88
import com.microsoft.openai.samples.rag.common.ChatGPTConversation;
99
import com.microsoft.openai.samples.rag.common.ChatGPTUtils;
10+
import com.microsoft.openai.samples.rag.retrieval.FactsRetrieverProvider;
1011
import com.microsoft.openai.samples.rag.proxy.OpenAIProxy;
11-
import com.microsoft.openai.samples.rag.retrieval.CognitiveSearchRetriever;
1212
import com.microsoft.openai.samples.rag.retrieval.Retriever;
1313
import org.slf4j.Logger;
1414
import org.slf4j.LoggerFactory;
1515
import org.springframework.context.ApplicationContext;
16-
import org.springframework.context.ApplicationContextAware;
1716
import org.springframework.stereotype.Component;
1817

1918
import java.util.*;
2019

20+
/**
21+
* Simple chat-read-retrieve-read java implementation, using the Cognitive Search and OpenAI APIs directly.
22+
* It uses the ChatGPT API to turn the user question into a good search query.
23+
* It queries Azure Cognitive Search for search results for that query (optionally using the vector embeddings for that query).
24+
* 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).
25+
*/
2126
@Component
22-
public class ChatReadRetrieveReadApproach implements RAGApproach<ChatGPTConversation, RAGResponse>, ApplicationContextAware {
27+
public class PlainJavaChatApproach implements RAGApproach<ChatGPTConversation, RAGResponse> {
2328

24-
private static final Logger LOGGER = LoggerFactory.getLogger(ChatReadRetrieveReadApproach.class);
29+
private static final Logger LOGGER = LoggerFactory.getLogger(PlainJavaChatApproach.class);
2530
private ApplicationContext applicationContext;
2631
private final OpenAIProxy openAIProxy;
27-
private final Retriever factsRetriever;
32+
private final FactsRetrieverProvider factsRetrieverProvider;
2833

29-
public ChatReadRetrieveReadApproach(Retriever factsRetriever, OpenAIProxy openAIProxy) {
30-
this.factsRetriever = factsRetriever;
34+
public PlainJavaChatApproach(FactsRetrieverProvider factsRetrieverProvider, OpenAIProxy openAIProxy) {
35+
this.factsRetrieverProvider = factsRetrieverProvider;
3136
this.openAIProxy = openAIProxy;
3237
}
3338

@@ -39,7 +44,7 @@ public ChatReadRetrieveReadApproach(Retriever factsRetriever, OpenAIProxy openAI
3944
@Override
4045
public RAGResponse run(ChatGPTConversation questionOrConversation, RAGOptions options) {
4146

42-
Retriever factsRetriever = getFactsRetriever(options);
47+
Retriever factsRetriever = factsRetrieverProvider.getFactsRetriever(options);
4348
List<ContentSource> sources = factsRetriever.retrieveFromConversation(questionOrConversation, options);
4449
LOGGER.info("Total {} sources retrieved", sources.size());
4550

@@ -65,18 +70,4 @@ public RAGResponse run(ChatGPTConversation questionOrConversation, RAGOptions op
6570
}
6671

6772

68-
/**
69-
*
70-
* @param options rag options containing search types(Cognitive Semantic Search, Cognitive Vector Search, Cognitive Hybrid Search, Semantic Kernel Memory) )
71-
* @return retriever implementation
72-
*/
73-
private CognitiveSearchRetriever getFactsRetriever(RAGOptions options) {
74-
//default to Cognitive Semantic Search for MVP.
75-
return this.applicationContext.getBean(CognitiveSearchRetriever.class);
76-
77-
}
78-
public void setApplicationContext(ApplicationContext applicationContext) {
79-
this.applicationContext = applicationContext;
80-
}
81-
8273
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ public class SemanticSearchChat {
1919
private Boolean replacePrompt = false;
2020

2121
private static final String FOLLOW_UP_QUESTIONS_TEMPLATE = """
22-
Generate three very brief follow-up questions that the user would likely ask next about their healthcare plan and employee handbook.
22+
After answering question, also generate three very brief follow-up questions that the user would likely ask next.
2323
Use double angle brackets to reference the questions, e.g. <<Are there exclusions for prescriptions?>>.
2424
Try not to repeat questions that have already been asked.
2525
Only generate questions and do not generate any text before or after the questions, such as 'Next Questions'
26-
""";
26+
""";
2727
private static final String SYSTEM_CHAT_MESSAGE_TEMPLATE = """
2828
Assistant helps the company employees with their healthcare plan questions, and questions about the employee handbook. Be brief in your answers.
2929
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.

app/backend/src/main/java/com/microsoft/openai/samples/rag/chat/controller/ChatController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public ResponseEntity<ChatResponse> openAIAsk(@RequestBody ChatRequest chatReque
5050
}
5151

5252
var ragOptions = new RAGOptions.Builder()
53+
.retrievialMode(chatRequest.getOverrides().getRetrievalMode())
5354
.semanticRanker(chatRequest.getOverrides().isSemanticRanker())
5455
.semanticCaptions(chatRequest.getOverrides().isSemanticCaptions())
5556
.suggestFollowupQuestions(chatRequest.getOverrides().isSuggestFollowupQuestions())

0 commit comments

Comments
 (0)