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