Skip to content

Commit 02d811a

Browse files
committed
experimental use of java semantic kernel for rag scenario
1 parent 8cc1958 commit 02d811a

File tree

16 files changed

+535
-52
lines changed

16 files changed

+535
-52
lines changed

app/backend/pom.xml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<spring-cloud-azure.version>4.9.0</spring-cloud-azure.version>
1919
<azure-search.version>11.6.0-beta.6</azure-search.version>
2020
<azure-openai.version>1.0.0-beta.2</azure-openai.version>
21+
<semantic-kernel.version>0.2.8-alpha-SNAPSHOT</semantic-kernel.version>
2122
</properties>
2223

2324
<dependencyManagement>
@@ -67,9 +68,25 @@
6768
</dependency>
6869

6970
<dependency>
70-
<groupId>com.microsoft.semantic-kernel</groupId>
71-
<artifactId>semantickernel-core</artifactId>
72-
<version>0.2.6-alpha</version>
71+
<groupId>com.microsoft.semantic-kernel</groupId>
72+
<artifactId>semantickernel-api</artifactId>
73+
<version>${semantic-kernel.version}</version>
74+
</dependency>
75+
<dependency>
76+
<groupId>com.microsoft.semantic-kernel</groupId>
77+
<artifactId>semantickernel-core</artifactId>
78+
<scope>runtime</scope>
79+
<version>${semantic-kernel.version}</version>
80+
</dependency>
81+
<dependency>
82+
<groupId>com.microsoft.semantic-kernel</groupId>
83+
<artifactId>semantickernel-connectors-ai-openai</artifactId>
84+
<version>${semantic-kernel.version}</version>
85+
</dependency>
86+
<dependency>
87+
<groupId>com.microsoft.semantic-kernel</groupId>
88+
<artifactId>semantickernel-planners</artifactId>
89+
<version>${semantic-kernel.version}</version>
7390
</dependency>
7491
</dependencies>
7592

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

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

33
import com.microsoft.openai.samples.rag.ask.approaches.RetrieveThenReadApproach;
4+
import com.microsoft.openai.samples.rag.ask.approaches.semantickernel.ReadRetrieveReadApproach;
45
import com.microsoft.openai.samples.rag.chat.approaches.ChatReadRetrieveReadApproach;
56
import org.springframework.context.ApplicationContext;
67
import org.springframework.context.ApplicationContextAware;
@@ -9,7 +10,8 @@
910
@Component
1011
public class RAGApproachFactorySpringBootImpl implements RAGApproachFactory, ApplicationContextAware {
1112
private ApplicationContext applicationContext;
12-
private static String CHAT_READ_RETRIEVE_READ = "rrr";
13+
private static String CHAT_READ_RETRIEVE_READ = "crrr";
14+
private static String READ_RETRIEVE_READ = "rrr";
1315
private static String RETRIEVE_THEN_READ = "rtr";
1416

1517
public void setApplicationContext(ApplicationContext applicationContext) {
@@ -27,7 +29,9 @@ public RAGApproach createApproach(String approachName) {
2729
return applicationContext.getBean(RetrieveThenReadApproach.class);
2830
} else if(CHAT_READ_RETRIEVE_READ.equals(approachName)) {
2931
return applicationContext.getBean(ChatReadRetrieveReadApproach.class);
30-
} else {
32+
} else if(READ_RETRIEVE_READ.equals(approachName)) {
33+
return applicationContext.getBean(ReadRetrieveReadApproach.class);}
34+
else {
3135
throw new IllegalArgumentException("Invalid approach name: " + approachName);
3236
}
3337
}

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

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public class RAGResponse {
66

77
private String question;
88
private List<ContentSource> sources;
9+
private String sourcesAsText;
910
private String answer;
1011
private String prompt;
1112

@@ -14,12 +15,33 @@ private RAGResponse(Builder builder) {
1415
this.sources = builder.sources;
1516
this.answer = builder.answer;
1617
this.prompt = builder.prompt;
18+
this.sourcesAsText =builder.sourcesAsText;
1719
}
1820

1921

22+
public String getQuestion() {
23+
return question;
24+
}
25+
26+
public List<ContentSource> getSources() {
27+
return sources;
28+
}
29+
30+
public String getSourcesAsText() { return sourcesAsText;}
31+
32+
public String getAnswer() {
33+
return answer;
34+
}
35+
36+
37+
public String getPrompt() {
38+
return prompt;
39+
}
40+
2041
public static class Builder {
2142
private String question;
2243
private List<ContentSource> sources;
44+
private String sourcesAsText;
2345
private String answer;
2446
private String prompt;
2547

@@ -34,6 +56,11 @@ public Builder sources(List<ContentSource> sources) {
3456
return this;
3557
}
3658

59+
public Builder sourcesAsText(String sourcesAsText) {
60+
this.sourcesAsText = sourcesAsText;
61+
return this;
62+
}
63+
3764
public Builder answer(String answer) {
3865
this.answer = answer;
3966
return this;
@@ -50,23 +77,6 @@ public RAGResponse build() {
5077
}
5178

5279

53-
public String getQuestion() {
54-
return question;
55-
}
56-
57-
public List<ContentSource> getSources() {
58-
return sources;
59-
}
60-
61-
62-
public String getAnswer() {
63-
return answer;
64-
}
65-
66-
67-
public String getPrompt() {
68-
return prompt;
69-
}
7080

7181

7282
}

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

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@
2727
@Component
2828
public class RetrieveThenReadApproach implements RAGApproach<String, RAGResponse> {
2929
private static final Logger logger = LoggerFactory.getLogger(RetrieveThenReadApproach.class);
30-
private String indexContentFieldName = "content";
31-
private String indexSourcePageFieldName = "sourcepage";
32-
private String indexCategoryFieldName = "category";
3330

3431
private CognitiveSearchProxy cognitiveSearchProxy;
3532
private OpenAIProxy openAIProxy;
@@ -45,7 +42,7 @@ public RetrieveThenReadApproach(CognitiveSearchProxy cognitiveSearchProxy, OpenA
4542
*/
4643
@Override
4744
public RAGResponse run(String question, RAGOptions options) {
48-
//TODO exception handling and logging
45+
//TODO exception handling
4946
SearchPagedIterable searchResults = getCognitiveSearchResults(question, options);
5047

5148
List<ContentSource> sources = buildSourcesFromSearchResults(options, searchResults);
@@ -80,7 +77,7 @@ private CompletionsOptions buildCompletionsOptions(SemanticSearchAskPrompt retr
8077
// Due to a potential bug when using JVM 17 and java openai SDK 1.0.0-beta.2, we need to provide default for all properties to avoid 404 bad Request on the server
8178
completionsOptions.setMaxTokens(1024);
8279
completionsOptions.setTemperature(0.3);
83-
completionsOptions.setStop(new ArrayList<>( Arrays.asList("\n")));
80+
completionsOptions.setStop(Arrays.asList("\n"));
8481
completionsOptions.setLogitBias(new HashMap<>());
8582
completionsOptions.setEcho(false);
8683
completionsOptions.setN(1);
@@ -147,15 +144,4 @@ private List<ContentSource> buildSourcesFromSearchResults(RAGOptions options, Se
147144
return sources;
148145
}
149146

150-
public void setIndexContentFieldName(String indexContentFieldName) {
151-
this.indexContentFieldName = indexContentFieldName;
152-
}
153-
154-
public void setIndexSourcePageFieldName(String indexSourcePageFieldName) {
155-
this.indexSourcePageFieldName = indexSourcePageFieldName;
156-
}
157-
158-
public void setIndexCategoryFieldName(String indexCategoryFieldName) {
159-
this.indexCategoryFieldName = indexCategoryFieldName;
160-
}
161147
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.microsoft.openai.samples.rag.ask.approaches.semantickernel;
2+
3+
import com.azure.core.util.Context;
4+
import com.azure.search.documents.SearchDocument;
5+
import com.azure.search.documents.models.SearchOptions;
6+
import com.azure.search.documents.util.SearchPagedIterable;
7+
import com.microsoft.openai.samples.rag.approaches.RAGOptions;
8+
import com.microsoft.openai.samples.rag.proxy.CognitiveSearchProxy;
9+
import com.microsoft.semantickernel.skilldefinition.annotations.DefineSKFunction;
10+
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionInputAttribute;
11+
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionParameters;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
import reactor.core.publisher.Mono;
15+
16+
import java.util.concurrent.atomic.AtomicInteger;
17+
import java.util.concurrent.atomic.AtomicReference;
18+
19+
20+
public class CognitiveSearchPlugin {
21+
private static final Logger logger = LoggerFactory.getLogger(CognitiveSearchPlugin.class);
22+
private CognitiveSearchProxy cognitiveSearchProxy;
23+
private SearchOptions searchOptions;
24+
25+
private RAGOptions options;
26+
27+
public CognitiveSearchPlugin(CognitiveSearchProxy cognitiveSearchProxy, SearchOptions searchOptions, RAGOptions options) {
28+
this.cognitiveSearchProxy = cognitiveSearchProxy;
29+
this.searchOptions = searchOptions;
30+
this.options = options;
31+
}
32+
33+
34+
@DefineSKFunction(name = "Search", description = "Search information relevant to answering a given query")
35+
public Mono<String> search(
36+
@SKFunctionInputAttribute(description = "the query to answer")
37+
String query
38+
) {
39+
SearchPagedIterable searchResults = this.cognitiveSearchProxy.search(query, this.searchOptions, Context.NONE);
40+
41+
StringBuffer sourcesText = new StringBuffer();
42+
AtomicInteger resultsCount = new AtomicInteger(0);
43+
44+
searchResults.iterator().forEachRemaining(result ->
45+
{
46+
var searchDocument = result.getDocument(SearchDocument.class);
47+
48+
/**
49+
If captions is enabled the content source is taken from the captions generated by the semantic ranker.
50+
Captions are appended sequentially and separated by a dot.
51+
*/
52+
if(options.isSemanticCaptions()) {
53+
StringBuffer sourcesContentBuffer = new StringBuffer();
54+
55+
result.getCaptions().forEach(caption -> {
56+
sourcesContentBuffer.append(caption.getText()).append(".");
57+
});
58+
59+
sourcesText.append((String)searchDocument.get("sourcepage"))
60+
.append(": ")
61+
.append(sourcesContentBuffer.toString())
62+
.append("\n");
63+
} else {
64+
//If captions is disabled the content source is taken from the cognitive search index field "content"
65+
sourcesText.append((String)searchDocument.get("sourcepage"))
66+
.append(": ")
67+
.append(((String) searchDocument.get("content")).replace("\n", ""))
68+
.append("\n");
69+
}
70+
resultsCount.getAndIncrement();
71+
});
72+
logger.info("Total %s sources found in cognitive search for keyword search query[%s]".formatted(resultsCount,query));
73+
74+
return Mono.just(sourcesText.toString());
75+
76+
}
77+
78+
}

0 commit comments

Comments
 (0)