优化llm接口

This commit is contained in:
Luke.Ye 2025-07-08 08:50:21 +08:00
parent 54a9241dbb
commit d3fb2d829a
5 changed files with 59 additions and 11 deletions

View File

@ -1,5 +1,6 @@
package com.knowledge.base.application.service; package com.knowledge.base.application.service;
import com.knowledge.base.infrastructure.north.dto.llm.WorkspaceAttachment;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.List; import java.util.List;
@ -30,9 +31,10 @@ public interface LLMAppService {
* @param llmToken LLM鉴权token * @param llmToken LLM鉴权token
* @param workspaceSlug 当前工作区slug * @param workspaceSlug 当前工作区slug
* @param keywords 搜索关键词 * @param keywords 搜索关键词
* @param question 当前的问题
* @return 本次被pin的docPath集合 * @return 本次被pin的docPath集合
*/ */
List<String> pinDocsByKeywords(String llmToken, String workspaceSlug, List<String> keywords); List<WorkspaceAttachment> pinDocsByKeywords(String llmToken, String workspaceSlug, List<String> keywords, String question);
} }

View File

@ -22,6 +22,8 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -37,6 +39,12 @@ public class LLMAppServiceImpl implements LLMAppService {
private final FileElasticsearchGateway esGateway; private final FileElasticsearchGateway esGateway;
/**
* pin和unpin的并发控制
*/
private static boolean PROCESSING = false;
private static String PROCESSING_QUESTION = StrUtil.EMPTY;
@Override @Override
public String getToken(String password) throws Exception { public String getToken(String password) throws Exception {
@ -46,8 +54,7 @@ public class LLMAppServiceImpl implements LLMAppService {
@Override @Override
public SseEmitter ask(String llmToken, String question, Map<String, Object> params) throws Exception { public SseEmitter ask(String llmToken, String question, Map<String, Object> params) throws Exception {
SseEmitter emitter = new SseEmitter(300 * 1000L); // 超时时间设为5分钟 SseEmitter emitter = new SseEmitter(300 * 1000L); // 超时时间设为5分钟
String keyword = (String) params.get("keyword"); List<WorkspaceAttachment> pinnedDocs = pinDocsByKeywords(llmToken, ConstantConfig.DEFAULT_SLUG_ID, extractKeywords(question), question);
pinDocsByKeywords(llmToken, ConstantConfig.DEFAULT_SLUG_ID, Lists.newArrayList(keyword));
ThreadPoolUtil.execute(() -> { ThreadPoolUtil.execute(() -> {
try { try {
@ -63,6 +70,16 @@ public class LLMAppServiceImpl implements LLMAppService {
} catch (IOException ioException) { } catch (IOException ioException) {
log.warn("SSE发送错误信息失败", ioException); log.warn("SSE发送错误信息失败", ioException);
} }
} finally {
// 问题结束后需要unpin掉
pinnedDocs.forEach(doc -> {
try {
anythingLLMService.updatePin(llmToken, ConstantConfig.DEFAULT_SLUG_ID, doc.getDocpath(), false);
} catch (Exception e) {
log.warn("unpin失败: {}", doc, e);
}
});
PROCESSING = false;
} }
}, ThreadPoolConfig.SSE_POOL); }, ThreadPoolConfig.SSE_POOL);
@ -70,11 +87,16 @@ public class LLMAppServiceImpl implements LLMAppService {
} }
@Override @Override
public List<String> pinDocsByKeywords(String llmToken, String workspaceSlug, List<String> keywords) { public List<WorkspaceAttachment> pinDocsByKeywords(String llmToken, String workspaceSlug, List<String> keywords, String question) {
if(CollectionUtil.isEmpty(keywords)) { if(CollectionUtil.isEmpty(keywords)) {
return Collections.emptyList(); return Collections.emptyList();
} }
if(PROCESSING) {
log.info("有问题正在处理请稍等question: ", PROCESSING_QUESTION);
return Collections.emptyList();
}
List<FileEsModel> fileEsModels = esGateway.searchByKeywords(keywords); List<FileEsModel> fileEsModels = esGateway.searchByKeywords(keywords);
Set<String> esFilepaths = fileEsModels.stream() Set<String> esFilepaths = fileEsModels.stream()
.map(FileEsModel::getFilepath) .map(FileEsModel::getFilepath)
@ -86,6 +108,9 @@ public class LLMAppServiceImpl implements LLMAppService {
return Lists.newArrayList(); return Lists.newArrayList();
} }
// 将当前问题标注为处理中
markQuestionProcessing(question);
// 2. 获取当前工作区所有附件docPath -> url // 2. 获取当前工作区所有附件docPath -> url
List<WorkspaceAttachment> attachments = anythingLLMService.fetchAttachments(llmToken, workspaceSlug); List<WorkspaceAttachment> attachments = anythingLLMService.fetchAttachments(llmToken, workspaceSlug);
if (attachments == null || attachments.isEmpty()) { if (attachments == null || attachments.isEmpty()) {
@ -94,9 +119,9 @@ public class LLMAppServiceImpl implements LLMAppService {
} }
// 3. 找到需要pin的docPath集合 // 3. 找到需要pin的docPath集合
Set<WorkspaceAttachment> toPinDocs = attachments.stream() List<WorkspaceAttachment> toPinDocs = attachments.stream()
.filter(att -> esFilepaths.stream().anyMatch(f -> att.getUrl().endsWith(f))) .filter(att -> esFilepaths.stream().anyMatch(f -> att.getUrl().endsWith(f)))
.collect(Collectors.toSet()); .collect(Collectors.toList());
// 4. 只unpin之前已pin的文档 // 4. 只unpin之前已pin的文档
attachments.stream() attachments.stream()
@ -121,6 +146,26 @@ public class LLMAppServiceImpl implements LLMAppService {
log.info("[pinDocsByKeywords] 共pin住文档{}条: {}", toPinDocs.size(), JSONUtil.toJsonStr(toPinDocs)); log.info("[pinDocsByKeywords] 共pin住文档{}条: {}", toPinDocs.size(), JSONUtil.toJsonStr(toPinDocs));
return toPinDocs.stream().map(WorkspaceAttachment::getUrl).collect(Collectors.toList()); return toPinDocs;
} }
/**
* 提取所有被LEFT & RIGHT 包裹的子串返回数组
*/
public static List<String> extractKeywords(String text) {
List<String> keywords = new ArrayList<>();
Pattern pattern = Pattern.compile(ConstantConfig.KEYWORD_PATTERN_LEFT + "(.*?)" + ConstantConfig.KEYWORD_PATTERN_RIGHT);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
keywords.add(matcher.group(1));
}
return keywords;
}
public static void markQuestionProcessing(String question) {
PROCESSING = true;
PROCESSING_QUESTION = question;
}
} }

View File

@ -34,6 +34,6 @@ public class ConstantConfig {
* 以下是AnythingLLM相关 * 以下是AnythingLLM相关
*/ */
public static final String DEFAULT_SLUG_ID = "87e14982-a821-48d8-9c6b-3557d0bb2f96"; public static final String DEFAULT_SLUG_ID = "87e14982-a821-48d8-9c6b-3557d0bb2f96";
public static final String KEYWORD_PATTERN_LEFT = "[";
public static final String KEYWORD_PATTERN_RIGHT = "]";
} }

View File

@ -4,6 +4,7 @@ import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.knowledge.base.infrastructure.config.ConstantConfig; import com.knowledge.base.infrastructure.config.ConstantConfig;
import com.knowledge.base.infrastructure.north.dto.llm.WorkspaceAttachment;
import com.knowledge.base.infrastructure.south.llm.AnythingLLMServiceImpl; import com.knowledge.base.infrastructure.south.llm.AnythingLLMServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -46,7 +47,7 @@ public class LLMAppServiceImplTest {
@Test @Test
public void testPinDocsByKeywords() throws Exception { public void testPinDocsByKeywords() throws Exception {
try { try {
List<String> result = llmAppService.pinDocsByKeywords(token, getSlug(), Lists.newArrayList("商业")); List<WorkspaceAttachment> result = llmAppService.pinDocsByKeywords(token, getSlug(), Lists.newArrayList("商业"), "[[商业]] 的本质是什么?");
log.info("result: {}", JSONUtil.toJsonStr(result)); log.info("result: {}", JSONUtil.toJsonStr(result));
} catch (Exception e) { } catch (Exception e) {
log.warn("测试 pinDocsByKeywords 失败!: {}", e.getMessage(), e); log.warn("测试 pinDocsByKeywords 失败!: {}", e.getMessage(), e);

View File

@ -149,7 +149,7 @@ public class AnythingLLMServiceTest {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put("wsName", "部门知识库"); params.put("wsName", "部门知识库");
llmService.streamAnswer(token, "介绍一下黄金圈法则", params, writer); // llmService.streamAnswer(token, "介绍一下黄金圈法则", params, writer);
} catch (Exception e) { } catch (Exception e) {
log.warn("测试 streamAnswer 异常", e); log.warn("测试 streamAnswer 异常", e);
} }