Skip to content

Commit ba31318

Browse files
committed
feat: update web search code
Signed-off-by: yuluo-yx <yuluo08290126@gmail.com>
1 parent 232c2d5 commit ba31318

File tree

12 files changed

+475
-98
lines changed

12 files changed

+475
-98
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
* 将 deepseek-r1 的 reasoning content 整合到输出中
1818
*/
1919

20-
public class ReasonContentAdvisor implements BaseAdvisor {
20+
public class ReasoningContentAdvisor implements BaseAdvisor {
2121

2222
private final int order;
2323

24-
public ReasonContentAdvisor(Integer order) {
24+
public ReasoningContentAdvisor(Integer order) {
2525
this.order = order != null ? order : 0;
2626
}
2727

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/config/AppConfig.java

Lines changed: 0 additions & 21 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package com.alibaba.cloud.ai.application.controller;
22

3-
import java.util.List;
4-
5-
import com.alibaba.cloud.ai.application.entity.result.Result;
6-
import com.alibaba.cloud.ai.application.service.SAAWebSearch;
3+
import com.alibaba.cloud.ai.application.service.SAAWebSearchService;
74
import com.alibaba.cloud.ai.application.utils.ValidText;
85
import io.swagger.v3.oas.annotations.tags.Tag;
6+
import jakarta.servlet.http.HttpServletResponse;
7+
import reactor.core.publisher.Flux;
98

10-
import org.springframework.ai.document.Document;
119
import org.springframework.web.bind.annotation.GetMapping;
1210
import org.springframework.web.bind.annotation.RequestMapping;
1311
import org.springframework.web.bind.annotation.RequestParam;
@@ -23,22 +21,25 @@
2321
@RequestMapping("/api/v1/")
2422
public class SAAWebSearchController {
2523

26-
private final SAAWebSearch webSearch;
24+
private final SAAWebSearchService webSearch;
2725

28-
public SAAWebSearchController(SAAWebSearch webSearch) {
26+
public SAAWebSearchController(SAAWebSearchService webSearch) {
2927
this.webSearch = webSearch;
3028
}
3129

3230
@GetMapping("/search")
33-
public Result<List<Document>> search(
34-
@RequestParam(value = "query") String query
31+
public Flux<String> search(
32+
@RequestParam(value = "query") String query,
33+
HttpServletResponse response
3534
) {
3635

3736
if (!ValidText.isValidate(query)) {
38-
return Result.failed("Invalid query");
37+
return Flux.just("Invalid query");
3938
}
4039

41-
return Result.success(webSearch.search(query));
40+
response.setCharacterEncoding("UTF-8");
41+
42+
return webSearch.chat(query);
4243
}
4344

4445
}

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/service/SAAWebSearch.java

Lines changed: 0 additions & 41 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.alibaba.cloud.ai.application.service;
2+
3+
import com.alibaba.cloud.ai.application.advisor.ReasoningContentAdvisor;
4+
import com.alibaba.cloud.ai.application.websearch.core.IQSSearchEngine;
5+
import com.alibaba.cloud.ai.application.websearch.data.DataClean;
6+
import com.alibaba.cloud.ai.application.websearch.rag.WebSearchRetriever;
7+
import com.alibaba.cloud.ai.application.websearch.rag.join.ConcatenationDocumentJoiner;
8+
import com.alibaba.cloud.ai.application.websearch.rag.prompt.CustomContextQueryAugmenter;
9+
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
10+
import reactor.core.publisher.Flux;
11+
12+
import org.springframework.ai.chat.client.ChatClient;
13+
import org.springframework.ai.chat.client.advisor.RetrievalAugmentationAdvisor;
14+
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
15+
import org.springframework.ai.chat.prompt.PromptTemplate;
16+
import org.springframework.ai.rag.preretrieval.query.expansion.QueryExpander;
17+
import org.springframework.ai.rag.preretrieval.query.transformation.QueryTransformer;
18+
import org.springframework.beans.factory.annotation.Qualifier;
19+
import org.springframework.stereotype.Service;
20+
21+
/**
22+
* @author yuluo
23+
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
24+
*/
25+
26+
@Service
27+
public class SAAWebSearchService {
28+
29+
private final ChatClient chatClient;
30+
31+
private final SimpleLoggerAdvisor simpleLoggerAdvisor;
32+
33+
private final ReasoningContentAdvisor reasoningContentAdvisor;
34+
35+
private final QueryTransformer queryTransformer;
36+
37+
private final QueryExpander queryExpander;
38+
39+
private final PromptTemplate queryArgumentPromptTemplate;
40+
41+
private final WebSearchRetriever webSearchRetriever;
42+
43+
public SAAWebSearchService(
44+
ChatClient.Builder chatClientBuilder,
45+
QueryTransformer queryTransformer,
46+
QueryExpander queryExpander,
47+
@Qualifier("queryArgumentPromptTemplate") PromptTemplate queryArgumentPromptTemplate,
48+
IQSSearchEngine searchEngine,
49+
DataClean dataCleaner
50+
) {
51+
52+
this.queryTransformer = queryTransformer;
53+
this.queryExpander = queryExpander;
54+
this.queryArgumentPromptTemplate = queryArgumentPromptTemplate;
55+
56+
// 用于 DeepSeek-r1 的 reasoning content 整合到输出中
57+
this.reasoningContentAdvisor = new ReasoningContentAdvisor(1);
58+
59+
// 构建 chatClient
60+
this.chatClient = chatClientBuilder
61+
.defaultOptions(
62+
DashScopeChatOptions.builder()
63+
.withModel("deepseek-r1")
64+
.withIncrementalOutput(false)
65+
.build())
66+
.build();
67+
68+
// 日志
69+
this.simpleLoggerAdvisor = new SimpleLoggerAdvisor(100);
70+
71+
this.webSearchRetriever = WebSearchRetriever.builder()
72+
.searchEngine(searchEngine)
73+
.dataCleaner(dataCleaner)
74+
.maxResults(2)
75+
.build();
76+
}
77+
78+
// 处理用户输入
79+
public Flux<String> chat(String prompt) {
80+
81+
return chatClient.prompt()
82+
.advisors(
83+
createRetrievalAugmentationAdvisor(),
84+
reasoningContentAdvisor,
85+
simpleLoggerAdvisor
86+
).user(prompt)
87+
.stream()
88+
.content();
89+
}
90+
91+
private RetrievalAugmentationAdvisor createRetrievalAugmentationAdvisor() {
92+
93+
return RetrievalAugmentationAdvisor.builder()
94+
.documentRetriever(webSearchRetriever)
95+
.queryTransformers(queryTransformer)
96+
.queryAugmenter(
97+
new CustomContextQueryAugmenter(
98+
queryArgumentPromptTemplate,
99+
null,
100+
true))
101+
.queryExpander(queryExpander).documentJoiner(new ConcatenationDocumentJoiner())
102+
.build();
103+
}
104+
105+
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
package com.alibaba.cloud.ai.application.websearch.config;
22

33
import com.alibaba.cloud.ai.application.websearch.rag.postretrieval.DashScopeDocumentRanker;
4+
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
45
import com.alibaba.cloud.ai.model.RerankModel;
56

6-
import org.springframework.boot.autoconfigure.AutoConfiguration;
7+
import org.springframework.ai.chat.client.ChatClient;
8+
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
9+
import org.springframework.ai.chat.prompt.PromptTemplate;
10+
import org.springframework.ai.rag.preretrieval.query.expansion.MultiQueryExpander;
11+
import org.springframework.ai.rag.preretrieval.query.expansion.QueryExpander;
12+
import org.springframework.ai.rag.preretrieval.query.transformation.QueryTransformer;
13+
import org.springframework.ai.rag.preretrieval.query.transformation.RewriteQueryTransformer;
14+
import org.springframework.beans.factory.annotation.Qualifier;
715
import org.springframework.context.annotation.Bean;
16+
import org.springframework.context.annotation.Configuration;
817

918
/**
1019
* @author yuluo
1120
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
1221
*/
1322

14-
@AutoConfiguration
23+
@Configuration
1524
public class WeSearchConfiguration {
1625

1726
@Bean
@@ -21,4 +30,46 @@ public DashScopeDocumentRanker dashScopeDocumentRanker(
2130
return new DashScopeDocumentRanker(rerankModel);
2231
}
2332

33+
@Bean
34+
public QueryTransformer queryTransformer(
35+
ChatClient.Builder chatClientBuilder,
36+
@Qualifier("transformerPromptTemplate") PromptTemplate transformerPromptTemplate
37+
) {
38+
39+
ChatClient chatClient = chatClientBuilder.defaultOptions(
40+
DashScopeChatOptions.builder()
41+
.withModel("qwen-plus")
42+
.build()
43+
).build();
44+
45+
return RewriteQueryTransformer.builder()
46+
.chatClientBuilder(chatClient.mutate())
47+
.promptTemplate(transformerPromptTemplate)
48+
.targetSearchSystem("Web search")
49+
.build();
50+
}
51+
52+
@Bean
53+
public QueryExpander queryExpander(
54+
ChatClient.Builder chatClientBuilder
55+
) {
56+
57+
ChatClient chatClient = chatClientBuilder.defaultOptions(
58+
DashScopeChatOptions.builder()
59+
.withModel("qwen-plus")
60+
.build()
61+
).build();
62+
63+
return MultiQueryExpander.builder()
64+
.chatClientBuilder(chatClient.mutate())
65+
.numberOfQueries(2)
66+
.build();
67+
}
68+
69+
@Bean
70+
public SimpleLoggerAdvisor simpleLoggerAdvisor() {
71+
72+
return new SimpleLoggerAdvisor();
73+
}
74+
2475
}

spring-ai-alibaba-integration-example/backend/src/main/java/com/alibaba/cloud/ai/application/websearch/rag/WebSearchRetriever.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package com.alibaba.cloud.ai.application.websearch.rag;
22

3-
import java.util.ArrayList;
43
import java.util.List;
54

65
import com.alibaba.cloud.ai.application.websearch.core.IQSSearchEngine;
76
import com.alibaba.cloud.ai.application.websearch.data.DataClean;
87
import com.alibaba.cloud.ai.application.websearch.entity.GenericSearchResult;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
910

1011
import org.springframework.ai.document.Document;
1112
import org.springframework.ai.rag.Query;
1213
import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
14+
import org.springframework.lang.Nullable;
1315

1416
/**
1517
* @author yuluo
@@ -18,6 +20,8 @@
1820

1921
public class WebSearchRetriever implements DocumentRetriever {
2022

23+
private static final Logger logger = LoggerFactory.getLogger(WebSearchRetriever.class);
24+
2125
private final IQSSearchEngine searchEngine;
2226

2327
private final int maxResults;
@@ -32,18 +36,20 @@ private WebSearchRetriever(Builder builder) {
3236
}
3337

3438
@Override
35-
public List<Document> retrieve(Query query) {
36-
37-
List<Document> documents = new ArrayList<>();
39+
public List<Document> retrieve(
40+
@Nullable Query query
41+
) {
3842

3943
// 搜索
4044
GenericSearchResult searchResp = searchEngine.search(query.text());
45+
logger.debug("search response: {}", searchResp);
4146

4247
// 清洗数据
43-
dataCleaner.getData(searchResp);
48+
List<Document> cleanerData = dataCleaner.getData(searchResp);
49+
logger.debug("cleaner data: {}", cleanerData);
4450

4551
// 返回结果
46-
return dataCleaner.limitResults(documents, maxResults);
52+
return dataCleaner.limitResults(cleanerData, maxResults);
4753
}
4854

4955
public static WebSearchRetriever.Builder builder() {

0 commit comments

Comments
 (0)