From 4efee316c1411c596a4d333740c3921742f08116 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 25 Aug 2025 16:41:04 +0800
Subject: [PATCH] feat(ai): 添加流式回答接口 V2 版本

---
 src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java          |   85 ++++++++++++++++++++++++++++
 src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java  |   14 ++++
 src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java |   11 +++
 src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java      |    6 ++
 src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java           |    4 +
 src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java               |    3 +
 src/main/java/cc/mrbird/febs/ai/req/talk/AiTalkAnswerStream.java         |   24 ++++++++
 src/main/java/cc/mrbird/febs/ai/service/AiService.java                   |    3 +
 8 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java b/src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java
index 10d9e8a..adf10b1 100644
--- a/src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java
+++ b/src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java
@@ -3,6 +3,7 @@
 import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkAnswerDto;
 import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkDto;
 import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkItemPageDto;
+import cc.mrbird.febs.ai.req.talk.AiTalkAnswerStream;
 import cc.mrbird.febs.ai.req.talk.ApiTalkDto;
 import cc.mrbird.febs.ai.req.talk.ApiTalkItemPageDto;
 import cc.mrbird.febs.ai.req.talk.ApiTalkPageDto;
@@ -77,4 +78,14 @@
 
         return aiTalkService.answerStream(question);
     }
+
+    @ApiOperation("提问AI(流式)V2")
+    @ApiResponses({
+            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
+    })
+    @PostMapping("/answer-streamV2")
+    public Flux<FebsResponse> answerStreamV2(@RequestBody @Validated AiTalkAnswerStream dto) {
+
+        return aiTalkService.answerStreamV2(dto);
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/req/talk/AiTalkAnswerStream.java b/src/main/java/cc/mrbird/febs/ai/req/talk/AiTalkAnswerStream.java
new file mode 100644
index 0000000..5de4cef
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/req/talk/AiTalkAnswerStream.java
@@ -0,0 +1,24 @@
+package cc.mrbird.febs.ai.req.talk;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author Administrator
+ */
+@Data
+@ApiModel(value = "AiTalkAnsStream", description = "参数")
+public class AiTalkAnswerStream {
+
+    @NotBlank(message = "会话ID不能为空")
+    @ApiModelProperty(value = "会话ID", example = "10")
+    private String talkId;
+
+
+    @NotBlank(message = "内容不能为空")
+    @ApiModelProperty(value = "内容", example = "10")
+    private String question;
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiService.java b/src/main/java/cc/mrbird/febs/ai/service/AiService.java
index b661f34..e5f99dc 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiService.java
@@ -2,6 +2,7 @@
 
 import cc.mrbird.febs.ai.req.ai.AiMessage;
 import cc.mrbird.febs.ai.req.ai.AiRequest;
+import cc.mrbird.febs.ai.req.talk.AiTalkAnswerStream;
 import cc.mrbird.febs.ai.res.ai.AiResponse;
 import cc.mrbird.febs.ai.res.ai.Report;
 import cc.mrbird.febs.common.entity.FebsResponse;
@@ -30,4 +31,6 @@
     Report extractReportData(String modelOutput);
 
     Flux<FebsResponse> answerStream(String question);
+
+    Flux<FebsResponse> answerStreamV2(AiTalkAnswerStream dto);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java b/src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java
index 3162531..d62b11b 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java
@@ -6,9 +6,13 @@
 import cn.hutool.core.date.DateTime;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
+
 public interface AiTalkItemService extends IService<AiTalkItem> {
 
     void add(String id, int code, String context, String memberUuid, DateTime date);
 
     FebsResponse historyPage(ApiTalkItemPageDto dto);
+
+    List<AiTalkItem> getListByTalkId(String talkId);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java b/src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java
index f6dc5f9..01f286a 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java
@@ -1,6 +1,7 @@
 package cc.mrbird.febs.ai.service;
 
 import cc.mrbird.febs.ai.entity.AiTalk;
+import cc.mrbird.febs.ai.req.talk.AiTalkAnswerStream;
 import cc.mrbird.febs.ai.req.talk.ApiTalkDto;
 import cc.mrbird.febs.ai.req.talk.ApiTalkItemPageDto;
 import cc.mrbird.febs.ai.req.talk.ApiTalkPageDto;
@@ -21,4 +22,6 @@
     FebsResponse talkList(ApiTalkPageDto dto);
 
     FebsResponse historyPage(ApiTalkItemPageDto dto);
+
+    Flux<FebsResponse> answerStreamV2(AiTalkAnswerStream dto);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java
index fab59c1..b059230 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java
@@ -1,15 +1,18 @@
 package cc.mrbird.febs.ai.service.impl;
 
+import cc.mrbird.febs.ai.entity.AiTalkItem;
 import cc.mrbird.febs.ai.enumerates.AiTypeEnum;
 import cc.mrbird.febs.ai.entity.AiProductRole;
 import cc.mrbird.febs.ai.req.ai.AiMessage;
 import cc.mrbird.febs.ai.req.ai.AiRequest;
+import cc.mrbird.febs.ai.req.talk.AiTalkAnswerStream;
 import cc.mrbird.febs.ai.res.ai.AiResponse;
 import cc.mrbird.febs.ai.res.ai.RadarDataItem;
 import cc.mrbird.febs.ai.res.ai.Report;
 import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo;
 import cc.mrbird.febs.ai.service.AiProductRoleService;
 import cc.mrbird.febs.ai.service.AiService;
+import cc.mrbird.febs.ai.service.AiTalkItemService;
 import cc.mrbird.febs.common.entity.FebsResponse;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.json.JSONUtil;
@@ -69,6 +72,7 @@
 
     private final AiProductRoleService aiProductRoleService;
     private final ObjectMapper objectMapper;
+    private final AiTalkItemService aiTalkItemService;
 
     @Value("${ai.service.ak}")
     private String ak;
@@ -418,6 +422,87 @@
                 });
     }
 
+    @Override
+    public Flux<FebsResponse> answerStreamV2(AiTalkAnswerStream dto) {
+        String question = dto.getQuestion();
+        //获取消息记录
+        List<AiTalkItem> aiTalkItems = aiTalkItemService.getListByTalkId(dto.getTalkId());
+        log.info("----- standard request -----");
+
+        final ChatMessage systemMessage = ChatMessage.builder()
+                .role(ChatMessageRole.SYSTEM)
+                .content("你是豆包,是由字节跳动开发的 AI 人工智能助手")
+                .build();
+
+        List<ChatMessage> messages = Arrays.asList(systemMessage);
+        if(CollUtil.isNotEmpty(aiTalkItems)){
+            for (AiTalkItem aiTalkItem : aiTalkItems){
+                if (aiTalkItem.getType() == 1){
+                    ChatMessage userMessage = ChatMessage.builder()
+                            .role(ChatMessageRole.USER)
+                            .content(aiTalkItem.getContext())
+                            .build();
+                    messages.add(userMessage);
+                }
+                if (aiTalkItem.getType() == 2){
+                    ChatMessage assistantMessage = ChatMessage.builder()
+                            .role(ChatMessageRole.ASSISTANT)
+                            .content(aiTalkItem.getContext())
+                            .build();
+                    messages.add(assistantMessage);
+                }
+            }
+        }
+
+        final ChatMessage userMessage = ChatMessage.builder()
+                .role(ChatMessageRole.USER)
+                .content(question)
+                .build();
+        messages.add(userMessage);
+
+        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
+                .model("ep-20250805124033-lhxbf")
+                .messages(messages)
+                .stream(true)
+                .thinking(new ChatCompletionRequest.ChatCompletionRequestThinking("enabled"))
+                .temperature(0.7)
+                .topP(0.9)
+                .maxTokens(2048)
+                .frequencyPenalty(0.0)
+                .build();
+
+        return Flux.from(service.streamChatCompletion(chatCompletionRequest))
+                .map(response -> {
+                    if (response == null || response.getChoices() == null || response.getChoices().isEmpty()) {
+                        return new FebsResponse().success().data("END");
+                    }
+
+                    ChatCompletionChoice choice = response.getChoices().get(0);
+                    if (choice == null || choice.getMessage() == null) {
+                        return new FebsResponse().success().data("END");
+                    }
+
+
+                    ApiMemberTalkStreamVo apiMemberTalkStreamVo = new ApiMemberTalkStreamVo();
+                    // 判断是否触发深度思考,触发则打印模型输出的思维链内容
+                    ChatMessage message = choice.getMessage();
+                    if (message.getReasoningContent()!= null &&!message.getReasoningContent().isEmpty()) {
+                        apiMemberTalkStreamVo.setReasoningContent(message.getReasoningContent());
+                        System.out.print(message.getReasoningContent());
+                    }
+
+                    String content = message.getContent() == null ? "" : message.getContent().toString();
+                    apiMemberTalkStreamVo.setContent(content);
+                    System.out.print(content);
+                    return new FebsResponse().success().data(apiMemberTalkStreamVo);
+                })
+                .onErrorResume(throwable -> {
+                    log.error("流式调用AI服务失败,问题输入: {}", question, throwable);
+                    FebsResponse errorResponse = new FebsResponse().message("AI服务调用失败");
+                    return Flux.just(errorResponse);
+                });
+    }
+
 
     private Report tryRepairTruncatedJson(String truncatedJson) {
         String[] repairAttempts = {
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java
index 7197ab6..e4a6b73 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java
@@ -9,11 +9,15 @@
 import cc.mrbird.febs.ai.utils.UUID;
 import cc.mrbird.febs.common.entity.FebsResponse;
 import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+
+import java.util.List;
 
 @Slf4j
 @Service
@@ -40,4 +44,14 @@
         Page<ApiTalkItemPageVo> pageListByQuery = aiTalkItemMapper.getPageListByQuery(page, dto);
         return new FebsResponse().success().data(pageListByQuery);
     }
+
+    @Override
+    public List<AiTalkItem> getListByTalkId(String talkId) {
+        LambdaQueryWrapper<AiTalkItem> queryWrapper = Wrappers.lambdaQuery(AiTalkItem.class);
+        queryWrapper.eq(AiTalkItem::getTalkId, talkId);
+        queryWrapper.orderByDesc(AiTalkItem::getCreatedTime);
+        queryWrapper.last("limit 10");
+        List<AiTalkItem> list = aiTalkItemMapper.selectList(queryWrapper);
+        return list;
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java
index 539dbac..81c8ed3 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java
@@ -2,6 +2,7 @@
 
 import cc.mrbird.febs.ai.entity.AiTalk;
 import cc.mrbird.febs.ai.mapper.AiTalkMapper;
+import cc.mrbird.febs.ai.req.talk.AiTalkAnswerStream;
 import cc.mrbird.febs.ai.req.talk.ApiTalkDto;
 import cc.mrbird.febs.ai.req.talk.ApiTalkItemPageDto;
 import cc.mrbird.febs.ai.req.talk.ApiTalkPageDto;
@@ -88,4 +89,9 @@
         return aiTalkItemService.historyPage(dto);
     }
 
+    @Override
+    public Flux<FebsResponse> answerStreamV2(AiTalkAnswerStream dto) {
+        return aiService.answerStreamV2(dto);
+    }
+
 }

--
Gitblit v1.9.1