diff --git a/modules/ai/src/main/java/com/bytedesk/ai/springai/service/BaseSpringAIService.java b/modules/ai/src/main/java/com/bytedesk/ai/springai/service/BaseSpringAIService.java index 0b65073bda..25635afb66 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/springai/service/BaseSpringAIService.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/springai/service/BaseSpringAIService.java @@ -34,7 +34,7 @@ import com.bytedesk.core.message.MessagePersistCache; import com.bytedesk.core.message.MessageProtobuf; import com.bytedesk.core.message.MessageRestService; import com.bytedesk.core.message.MessageTypeEnum; -import com.bytedesk.core.message.content.RobotStreamContent; +import com.bytedesk.core.message.content.StreamContent; import com.bytedesk.core.thread.ThreadRestService; import com.bytedesk.core.uid.UidUtils; import com.bytedesk.kbase.llm_chunk.elastic.ChunkElastic; @@ -127,10 +127,10 @@ public abstract class BaseSpringAIService implements SpringAIService { */ public static class SearchResultWithSources { private final List searchResults; - private final List sourceReferences; + private final List sourceReferences; public SearchResultWithSources(List searchResults, - List sourceReferences) { + List sourceReferences) { this.searchResults = searchResults; this.sourceReferences = sourceReferences; } @@ -139,7 +139,7 @@ public abstract class BaseSpringAIService implements SpringAIService { return searchResults; } - public List getSourceReferences() { + public List getSourceReferences() { return sourceReferences; } } @@ -596,7 +596,7 @@ public abstract class BaseSpringAIService implements SpringAIService { // 创建搜索结果列表和源引用列表 List searchResultList = new ArrayList<>(); - List sourceReferences = new ArrayList<>(); + List sourceReferences = new ArrayList<>(); // 根据搜索类型执行相应的搜索 String searchType = robot.getLlm().getSearchType(); @@ -754,7 +754,7 @@ public abstract class BaseSpringAIService implements SpringAIService { * 执行全文搜索并收集源引用信息 */ private void executeFulltextSearchWithSources(String query, String kbUid, List searchResultList, - List sourceReferences) { + List sourceReferences) { List searchResults = faqElasticService.searchFaq(query, kbUid, null, null); for (FaqElasticSearchResult withScore : searchResults) { FaqElastic faq = withScore.getFaqElastic(); @@ -762,8 +762,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建FAQ源引用 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.FAQ) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.FAQ) .sourceUid(faq.getUid()) .sourceName(faq.getQuestion()) .contentSummary(getContentSummary(faq.getAnswer(), 200)) @@ -780,8 +780,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建文本源引用 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.TEXT) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.TEXT) .sourceUid(text.getUid()) .sourceName(text.getTitle()) .contentSummary(getContentSummary(text.getContent(), 200)) @@ -798,8 +798,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建Chunk源引用,包含文件信息 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.CHUNK) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.CHUNK) .sourceUid(chunk.getUid()) .sourceName(chunk.getName()) .fileName(chunk.getFileName()) @@ -819,8 +819,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建网页源引用 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.WEBPAGE) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.WEBPAGE) .sourceUid(webpage.getUid()) .sourceName(webpage.getTitle()) .contentSummary(getContentSummary(webpage.getContent(), 200)) @@ -835,7 +835,7 @@ public abstract class BaseSpringAIService implements SpringAIService { * 执行向量搜索并收集源引用信息 */ private void executeVectorSearchWithSources(String query, String kbUid, List searchResultList, - List sourceReferences) { + List sourceReferences) { // 检查 FaqVectorService 是否可用 if (faqVectorService != null) { try { @@ -847,8 +847,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建FAQ向量源引用 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.FAQ) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.FAQ) .sourceUid(faqVector.getUid()) .sourceName(faqVector.getQuestion()) .contentSummary(getContentSummary(faqVector.getAnswer(), 200)) @@ -873,8 +873,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建文本向量源引用 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.TEXT) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.TEXT) .sourceUid(textVector.getUid()) .sourceName(textVector.getTitle()) .contentSummary(getContentSummary(textVector.getContent(), 200)) @@ -899,8 +899,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建Chunk向量源引用,包含文件信息 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.CHUNK) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.CHUNK) .sourceUid(chunkVector.getUid()) .sourceName(chunkVector.getName()) .fileName(chunkVector.getFileName()) @@ -928,8 +928,8 @@ public abstract class BaseSpringAIService implements SpringAIService { searchResultList.add(faqProtobuf); // 创建网页向量源引用 - RobotStreamContent.SourceReference sourceRef = RobotStreamContent.SourceReference.builder() - .sourceType(RobotStreamContent.SourceTypeEnum.WEBPAGE) + StreamContent.SourceReference sourceRef = StreamContent.SourceReference.builder() + .sourceType(StreamContent.SourceTypeEnum.WEBPAGE) .sourceUid(webpageVector.getUid()) .sourceName(webpageVector.getTitle()) .contentSummary(getContentSummary(webpageVector.getContent(), 200)) @@ -968,15 +968,15 @@ public abstract class BaseSpringAIService implements SpringAIService { * @return RobotStreamContent JSON字符串 */ protected String createRobotStreamContentAnswer(String question, String answer, - List sourceReferences, RobotProtobuf robot) { + List sourceReferences, RobotProtobuf robot) { // 构建上下文信息用于重新生成答案 StringBuilder contextBuilder = new StringBuilder(); - for (RobotStreamContent.SourceReference source : sourceReferences) { + for (StreamContent.SourceReference source : sourceReferences) { contextBuilder.append("Source: ").append(source.getSourceName()).append("\n"); contextBuilder.append("Content: ").append(source.getContentSummary()).append("\n\n"); } - RobotStreamContent streamContent = RobotStreamContent.builder() + StreamContent streamContent = StreamContent.builder() .question(question) .answer(answer) .sources(sourceReferences) @@ -1000,7 +1000,7 @@ public abstract class BaseSpringAIService implements SpringAIService { // 搜索知识库并获取源引用 SearchResultWithSources searchResult = searchKnowledgeBaseWithSources(query, robot); List searchResultList = searchResult.getSearchResults(); - List sourceReferences = searchResult.getSourceReferences(); + List sourceReferences = searchResult.getSourceReferences(); if (searchResultList.isEmpty()) { // 未找到相关内容,使用默认回复 @@ -1029,7 +1029,7 @@ public abstract class BaseSpringAIService implements SpringAIService { * 创建并处理包含源引用的提示词 */ private void createAndProcessPromptWithSources(String query, String context, - List sourceReferences, + List sourceReferences, RobotProtobuf robot, MessageProtobuf messageProtobufQuery, MessageProtobuf messageProtobufReply, @@ -1075,8 +1075,8 @@ public abstract class BaseSpringAIService implements SpringAIService { // 对于机器人回复,提取实际的回答文本 if (MessageTypeEnum.ROBOT_STREAM.name().equals(messageEntity.getType())) { try { - RobotStreamContent robotContent = RobotStreamContent.fromJson(content, - RobotStreamContent.class); + StreamContent robotContent = StreamContent.fromJson(content, + StreamContent.class); if (robotContent != null && robotContent.getAnswer() != null) { content = robotContent.getAnswer(); } @@ -1118,7 +1118,7 @@ public abstract class BaseSpringAIService implements SpringAIService { * 这个方法提供了一个默认实现,处理带有源引用的流式响应 * 子类可以重写此方法以提供特定AI服务的优化实现 */ - protected void processPromptSseWithSources(Prompt prompt, List sourceReferences, + protected void processPromptSseWithSources(Prompt prompt, List sourceReferences, RobotProtobuf robot, MessageProtobuf messageProtobufQuery, MessageProtobuf messageProtobufReply, SseEmitter emitter, String fullPromptContent) { @@ -1484,13 +1484,13 @@ public abstract class BaseSpringAIService implements SpringAIService { try { if (StringUtils.hasLength(content) && !isEmitterCompleted(emitter)) { // 使用 RobotStreamContent 包装流式片段,类型改为 ROBOT_STREAM - RobotStreamContent.RobotStreamContentBuilder builder = RobotStreamContent.builder() + StreamContent.StreamContentBuilder builder = StreamContent.builder() .answer(content); if (StringUtils.hasLength(reasonContent)) { builder.reasonContent(reasonContent); } - RobotStreamContent robotStream = builder.build(); - messageProtobufReply.setContent(robotStream.toJson()); + StreamContent streamContent = builder.build(); + messageProtobufReply.setContent(streamContent.toJson()); messageProtobufReply.setType(MessageTypeEnum.ROBOT_STREAM); // 保存消息到数据库 persistMessage(messageProtobufQuery, messageProtobufReply, false); @@ -1555,7 +1555,7 @@ public abstract class BaseSpringAIService implements SpringAIService { * 发送带有源引用的流式开始消息 */ protected void sendStreamStartMessageWithSources(MessageProtobuf messageProtobufReply, SseEmitter emitter, - String fullPromptContent, List sourceReferences, RobotProtobuf robot) { + String fullPromptContent, List sourceReferences, RobotProtobuf robot) { // 创建带有源引用的开始消息 String robotStreamContent = createRobotStreamContentAnswer( @@ -1588,7 +1588,7 @@ public abstract class BaseSpringAIService implements SpringAIService { */ protected void sendStreamMessageWithSources(MessageProtobuf messageProtobufQuery, MessageProtobuf messageProtobufReply, - SseEmitter emitter, String content, List sourceReferences, + SseEmitter emitter, String content, List sourceReferences, RobotProtobuf robot, String query) { log.info("BaseSpringAIService sendStreamMessageWithSources content {}", content); @@ -1624,7 +1624,7 @@ public abstract class BaseSpringAIService implements SpringAIService { */ protected void sendStreamEndMessageWithSources(MessageProtobuf messageProtobufQuery, MessageProtobuf messageProtobufReply, - SseEmitter emitter, List sourceReferences, RobotProtobuf robot, + SseEmitter emitter, List sourceReferences, RobotProtobuf robot, String query, String finalAnswer, long promptTokens, long completionTokens, long totalTokens, String prompt, String aiProvider, String aiModel) { diff --git a/modules/core/src/main/java/com/bytedesk/core/message/MessagePersistService.java b/modules/core/src/main/java/com/bytedesk/core/message/MessagePersistService.java index a7f00999cc..94a12b969e 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/MessagePersistService.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/MessagePersistService.java @@ -21,7 +21,7 @@ import org.springframework.stereotype.Service; import com.bytedesk.core.thread.ThreadEntity; import com.bytedesk.core.thread.ThreadRestService; -import com.bytedesk.core.message.content.RobotStreamContent; +import com.bytedesk.core.message.content.StreamContent; import jakarta.annotation.Nonnull; import lombok.AllArgsConstructor; @@ -62,18 +62,18 @@ public class MessagePersistService { String existingJson = message.getContent(); String incomingJson = messageProtobuf.getContent(); - RobotStreamContent existing = null; - RobotStreamContent incoming = null; + StreamContent existing = null; + StreamContent incoming = null; try { if (existingJson != null && !existingJson.isEmpty()) { - existing = RobotStreamContent.fromJson(existingJson, RobotStreamContent.class); + existing = StreamContent.fromJson(existingJson, StreamContent.class); } } catch (Exception ignore) { // 旧数据或非JSON,忽略 } try { if (incomingJson != null && !incomingJson.isEmpty()) { - incoming = RobotStreamContent.fromJson(incomingJson, RobotStreamContent.class); + incoming = StreamContent.fromJson(incomingJson, StreamContent.class); } } catch (Exception ignore) { } @@ -87,7 +87,7 @@ public class MessagePersistService { String mergedReason = concatSafe(existing.getReasonContent(), incoming.getReasonContent()); // 沿用已有的其它字段(question、sources、kbUid、robotUid、regenerationContext) - RobotStreamContent merged = RobotStreamContent.builder() + StreamContent merged = StreamContent.builder() .question(existing.getQuestion() != null ? existing.getQuestion() : incoming.getQuestion()) .answer(mergedAnswer) diff --git a/modules/core/src/main/java/com/bytedesk/core/message/MessageTypeEnum.java b/modules/core/src/main/java/com/bytedesk/core/message/MessageTypeEnum.java index da6f8d374a..d97bbbc5b3 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/MessageTypeEnum.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/MessageTypeEnum.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-06-05 21:50:54 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-09-12 12:28:28 + * @LastEditTime: 2025-09-24 08:55:22 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -84,15 +84,12 @@ public enum MessageTypeEnum { ROBOT, // 机器人消息 ROBOT_QUESTION, // 机器人问题 ROBOT_ANSWER, // 机器人答案 - ROBOT_STREAM, // 机器人流式响应(包含源引用) - ROBOT_STREAM_START, // 机器人流式响应开始 - ROBOT_STREAM_END, // 机器人流式响应结束 ROBOT_UP, // 点赞 ROBOT_DOWN, // 点踩 // - // STREAM, // 流式消息TEXT,大模型回复,改为 ROBOT_STREAM - // STREAM_START, // 流式消息开始 - // STREAM_END, // 流式消息结束 + ROBOT_STREAM, // 机器人流式响应(包含源引用) + ROBOT_STREAM_START, // 机器人流式响应开始 + ROBOT_STREAM_END, // 机器人流式响应结束 // ARTICLE, // 文章 // diff --git a/modules/core/src/main/java/com/bytedesk/core/message/content/RobotStreamContent.java b/modules/core/src/main/java/com/bytedesk/core/message/content/StreamContent.java similarity index 98% rename from modules/core/src/main/java/com/bytedesk/core/message/content/RobotStreamContent.java rename to modules/core/src/main/java/com/bytedesk/core/message/content/StreamContent.java index 5d87678d5b..e61cffac98 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/content/RobotStreamContent.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/content/StreamContent.java @@ -22,7 +22,7 @@ import lombok.experimental.SuperBuilder; @SuperBuilder @AllArgsConstructor @NoArgsConstructor -public class RobotStreamContent extends BaseContent { +public class StreamContent extends BaseContent { private String question;