Commit 9a3bfe89e51de755b4168f945866398341d93e7b

Authored by YunaiV
1 parent be56c834

feat:【ai】226 修改知识库和联网搜索仅初始化一次

yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
@@ -59,6 +59,8 @@ import reactor.core.publisher.Flux; @@ -59,6 +59,8 @@ import reactor.core.publisher.Flux;
59 59
60 import java.time.LocalDateTime; 60 import java.time.LocalDateTime;
61 import java.util.*; 61 import java.util.*;
  62 +import java.util.concurrent.atomic.AtomicBoolean;
  63 +import java.util.concurrent.atomic.AtomicReference;
62 import java.util.stream.Collectors; 64 import java.util.stream.Collectors;
63 65
64 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 66 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -231,20 +233,24 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -231,20 +233,24 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
231 // 4.3 流式返回 233 // 4.3 流式返回
232 StringBuffer contentBuffer = new StringBuffer(); 234 StringBuffer contentBuffer = new StringBuffer();
233 StringBuffer reasoningContentBuffer = new StringBuffer(); 235 StringBuffer reasoningContentBuffer = new StringBuffer();
  236 +
  237 + // 防止执行多次知识库和联网搜索
  238 + AtomicBoolean firstExecuteFlag = new AtomicBoolean(true);
  239 + AtomicReference<List<AiChatMessageRespVO.KnowledgeSegment>> cacheSegments = new AtomicReference<>();
  240 + AtomicReference<List<AiWebSearchResponse.WebPage>> cacheWebSearchPages = new AtomicReference<>();
234 return streamResponse.map(chunk -> { 241 return streamResponse.map(chunk -> {
235 // 仅首次:返回知识库、联网搜索 242 // 仅首次:返回知识库、联网搜索
236 - List<AiChatMessageRespVO.KnowledgeSegment> segments = null;  
237 - List<AiWebSearchResponse.WebPage> webSearchPages = null;  
238 if (StrUtil.isEmpty(contentBuffer)) { 243 if (StrUtil.isEmpty(contentBuffer)) {
239 - Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() ->  
240 - knowledgeDocumentService.getKnowledgeDocumentMap(  
241 - convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)));  
242 - segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, segment -> {  
243 - AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());  
244 - segment.setDocumentName(document != null ? document.getName() : null);  
245 - });  
246 - if (webSearchResponse != null) {  
247 - webSearchPages = webSearchResponse.getLists(); 244 + if (firstExecuteFlag.compareAndSet(true, false)) { // CAS 操作,确保仅执行一次
  245 + Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() -> knowledgeDocumentService.getKnowledgeDocumentMap(
  246 + convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)));
  247 + cacheSegments.set(BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, segment -> {
  248 + AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
  249 + segment.setDocumentName(document != null ? document.getName() : null);
  250 + }));
  251 + if (webSearchResponse != null) {
  252 + cacheWebSearchPages.set(webSearchResponse.getLists());
  253 + }
248 } 254 }
249 } 255 }
250 // 响应结果 256 // 响应结果
@@ -261,7 +267,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -261,7 +267,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
261 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) 267 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
262 .setContent(StrUtil.nullToDefault(newContent, "")) // 避免 null 的 情况 268 .setContent(StrUtil.nullToDefault(newContent, "")) // 避免 null 的 情况
263 .setReasoningContent(StrUtil.nullToDefault(newReasoningContent, "")) // 避免 null 的 情况 269 .setReasoningContent(StrUtil.nullToDefault(newReasoningContent, "")) // 避免 null 的 情况
264 - .setSegments(segments).setWebSearchPages(webSearchPages))); // 知识库 + 联网搜索 270 + .setSegments(cacheSegments.get()).setWebSearchPages(cacheWebSearchPages.get()))); // 知识库 + 联网搜索
265 }).doOnComplete(() -> { 271 }).doOnComplete(() -> {
266 // 忽略租户,因为 Flux 异步无法透传租户 272 // 忽略租户,因为 Flux 异步无法透传租户
267 TenantUtils.executeIgnore(() -> chatMessageMapper.updateById( 273 TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(