From 2fcfbcefee1a6ad5b51e23223ebe77093b847c3a Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Thu, 30 Oct 2025 14:45:53 +0800
Subject: [PATCH] feat(ai):优化对话流初始化逻辑 - 调整对话记录查询顺序,确保获取最新进行中的对话 - 在对话不存在时创建新对话并初始化问题 - 若对话已存在,则查询最近的对话项作为标题来源 - 修复可能因查询顺序导致的标题加载异常问题
---
src/main/java/cc/mrbird/febs/ai/controller/TestController.java | 217 +++++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 173 insertions(+), 44 deletions(-)
diff --git a/src/main/java/cc/mrbird/febs/ai/controller/TestController.java b/src/main/java/cc/mrbird/febs/ai/controller/TestController.java
index 4d63bf5..4df6fa5 100644
--- a/src/main/java/cc/mrbird/febs/ai/controller/TestController.java
+++ b/src/main/java/cc/mrbird/febs/ai/controller/TestController.java
@@ -1,11 +1,18 @@
package cc.mrbird.febs.ai.controller;
import cc.mrbird.febs.ai.entity.AiMember;
+import cc.mrbird.febs.ai.enumerates.AiPromptEnum;
import cc.mrbird.febs.ai.mapper.AiMemberMapper;
import cc.mrbird.febs.ai.req.talk.AiTalkAnswerStream;
-import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo;
+import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVoOld;
+import cc.mrbird.febs.ai.service.AiService;
+import cc.mrbird.febs.ai.strategy.enumerates.LlmStrategyContextEnum;
+import cc.mrbird.febs.ai.strategy.enumerates.LlmStrategyEnum;
+import cc.mrbird.febs.ai.strategy.LlmStrategyFactory;
+import cc.mrbird.febs.ai.strategy.param.LlmStrategyDto;
import cc.mrbird.febs.ai.utils.UUID;
import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.exception.FebsException;
import cc.mrbird.febs.common.utils.AppContants;
import cc.mrbird.febs.common.utils.RedisUtils;
import cc.mrbird.febs.mall.entity.MallMember;
@@ -15,8 +22,11 @@
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
-import cn.hutool.json.JSONUtil;
-import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
+import com.alibaba.dashscope.app.Application;
+import com.alibaba.dashscope.app.ApplicationParam;
+import com.alibaba.dashscope.app.ApplicationResult;
+import com.alibaba.dashscope.app.FlowStreamMode;
+import com.alibaba.dashscope.utils.JsonUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.reactivex.Flowable;
@@ -39,9 +49,7 @@
import com.alibaba.dashscope.exception.NoApiKeyException;
import reactor.core.publisher.Flux;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
/**
* @author Administrator
@@ -56,7 +64,9 @@
private final MallMemberMapper mallMemberMapper;
private final AiMemberMapper aiMemberMapper;
+ private final AiService aiService;
private final RedisUtils redisUtils;
+ private final LlmStrategyFactory llmStrategyFactory;
@ApiOperation(value = "登录测试", notes = "登录测试")
@GetMapping(value = "/login")
public FebsResponse info() {
@@ -103,19 +113,21 @@
@ApiOperation("提问AI(流式)V2")
@ApiResponses({
- @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
+ @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
})
@PostMapping("/answer-streamV2")
public Flux<FebsResponse> answerStreamV2(@RequestBody @Validated AiTalkAnswerStream dto) {
if (StrUtil.isEmpty(dto.getQuestion())){
return Flux.just(new FebsResponse().fail().message("请输入问题"));
}
+ String question = dto.getQuestion();
+ String prompt = dto.getPrompt() ;
long startTime = System.currentTimeMillis();
Flowable<GenerationResult> result;
try {
- result = callWithMessage();
+ result = callWithMessageStream(question,prompt);
} catch (NoApiKeyException | InputRequiredException e) {
e.printStackTrace();
return Flux.just(new FebsResponse().fail().message("调用AI服务失败: " + e.getMessage()));
@@ -125,7 +137,7 @@
.map(message -> {
String content = message.getOutput().getChoices().get(0).getMessage().getContent();
System.out.print(content);
- return new FebsResponse().success().message(content);
+ return new FebsResponse().success().data(content);
})
.doOnComplete(() -> {
long endTime = System.currentTimeMillis();
@@ -138,74 +150,191 @@
}
- public static Flowable<GenerationResult> callWithMessage() throws NoApiKeyException, InputRequiredException {
+
+ @ApiOperation("提问AI(流式)V3")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
+ })
+ @PostMapping("/answerStreamV3")
+ public Flux<FebsResponse> answerStreamV3(@RequestBody @Validated AiTalkAnswerStream dto) {
+
+ return llmInvokeStreamingWithThink();
+ }
+
+ private Flux<FebsResponse> llmInvokeStreamingWithThink(){
+
+ long startTime = System.currentTimeMillis();
+ ApplicationParam param = ApplicationParam.builder()
+ // 若没有配置环境变量,可用百炼API Key将下行替换为:.apiKey("sk-xxx")。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。
+ .apiKey("sk-babdcf8799144134915cee2683794b2f")
+ .appId("f94539c7e54d44e2a7cac5e85f2ae61d") //替换为实际的应用 ID
+ .flowStreamMode(FlowStreamMode.MESSAGE_FORMAT)
+ .prompt("你是谁?")
+ .enableThinking( true)
+ .hasThoughts( true)
+ .build();
+
+ Application application = new Application();
+ Flowable<ApplicationResult> result;
+ try {
+ result = application.streamCall(param);
+ } catch (NoApiKeyException | InputRequiredException e) {
+ throw new FebsException(StrUtil.format("百炼工作流输出失败:{}",e.getMessage()));
+ }
+
+ return Flux.from(result)
+ .map(message -> {
+ HashMap<String, String> stringStringHashMap = new HashMap<>();
+
+ System.out.print(message.getOutput().getThoughts());
+ if (!message.getOutput().getFinishReason().equals("stop")){
+ stringStringHashMap.put(LlmStrategyContextEnum.CONTENT.name(),message.getOutput().getWorkflowMessage().getMessage().getContent());
+ }
+ return new FebsResponse().success().data(stringStringHashMap);
+ })
+ .doOnComplete(() -> {
+ long endTime = System.currentTimeMillis();
+ System.out.println("百炼工作流输出:" + (endTime - startTime) + "毫秒");
+ })
+ .doOnError(error -> {
+ throw new FebsException(StrUtil.format("百炼工作流输出失败:{}",error));
+ });
+ }
+
+ private LlmStrategyDto buildLlmStrategyDtoList(String Str, Integer type){
+ LlmStrategyDto llmStrategyDto = new LlmStrategyDto();
+ if (type == 1){
+ llmStrategyDto.setRole(Role.SYSTEM.getValue());
+ }
+ if (type == 2){
+ llmStrategyDto.setRole(Role.USER.getValue());
+ }
+ if (type == 3){
+ llmStrategyDto.setRole(Role.ASSISTANT.getValue());
+ }
+ if (type == 4){
+ llmStrategyDto.setRole(Role.TOOL.getValue());
+ }
+ llmStrategyDto.setContent(Str);
+
+ return llmStrategyDto;
+ }
+
+
+
+ @ApiOperation("提问AI(非流式响应)V4")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "非流式响应", response = ApiMemberTalkStreamVoOld.class),
+ })
+ @PostMapping("/answerStreamV4")
+ public FebsResponse answerStreamV4(@RequestBody @Validated AiTalkAnswerStream dto) {
+ if (StrUtil.isEmpty(dto.getQuestion())){
+ return new FebsResponse().fail().message("请输入问题");
+ }
+ ArrayList<LlmStrategyDto> llmStrategyDtoList = new ArrayList<>();
+ if (dto.getPrompt() != null){
+ LlmStrategyDto llmStrategyDto = new LlmStrategyDto();
+ llmStrategyDto.setRole(Role.SYSTEM.getValue());
+ llmStrategyDto.setContent(dto.getPrompt());
+ llmStrategyDtoList.add(llmStrategyDto);
+ }
+ if (dto.getQuestion() != null){
+ LlmStrategyDto llmStrategyDto = new LlmStrategyDto();
+ llmStrategyDto.setRole(Role.USER.getValue());
+ llmStrategyDto.setContent(dto.getQuestion());
+ llmStrategyDtoList.add(llmStrategyDto);
+ }
+ String modelName = LlmStrategyEnum.getName(aiService.getSystemSetAiType());
+
+ return llmStrategyFactory.getCalculationStrategyMap().get(modelName).llmInvokeNonStreaming(llmStrategyDtoList);
+ }
+
+
+ public static Flowable<GenerationResult> callWithMessageStream(String question,String prompt) throws NoApiKeyException, InputRequiredException {
Generation gen = new Generation();
Message systemMsg = Message.builder()
.role(Role.SYSTEM.getValue())
- .content("You are a helpful assistant.")
+ .content(prompt)
.build();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
- .content("你是谁?")
+ .content(question)
.build();
GenerationParam param = GenerationParam.builder()
// 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:.apiKey("sk-xxx")
.apiKey("sk-babdcf8799144134915cee2683794b2f")
// 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
- .model("qwen-plus")
+ .model("qwen3-14b-ft-202509031002-7446")
// .model("deepseek-r1")
// .model("qwen-turbo-0624-ft-202508281725-c2dc")
.messages(Arrays.asList(systemMsg, userMsg))
// .resultFormat(GenerationParam.ResultFormat.TEXT)
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
+ .enableThinking( true)
.incrementalOutput(true)
.build();
return gen.streamCall(param);
}
+
+
+ public static GenerationResult callWithMessage(String question,String prompt) throws ApiException, NoApiKeyException, InputRequiredException {
+ Generation gen = new Generation();
+ Message systemMsg = Message.builder()
+ .role(Role.SYSTEM.getValue())
+ .content(prompt)
+ .build();
+ Message userMsg = Message.builder()
+ .role(Role.USER.getValue())
+ .content(question)
+ .build();
+ GenerationParam param = GenerationParam.builder()
+ // 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:.apiKey("sk-xxx")
+ .apiKey("sk-babdcf8799144134915cee2683794b2f")
+ // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
+ .model("qwen3-14b-ft-202509031002-7446")
+ .messages(Arrays.asList(systemMsg, userMsg))
+ .resultFormat(GenerationParam.ResultFormat.MESSAGE)
+ .enableThinking( false)
+ .build();
+ return gen.call(param);
+ }
public static void main(String[] args) {
//定义一个开始时间为启动这个main方法的开始时间,用于计算运行时间
long startTime = System.currentTimeMillis();
-
- Flowable<GenerationResult> result = null;
+ String question = "下属负责的项目高质量按期交付,除了表扬,你希望 下属要多注意和同事的沟通,应如何表达";
+ String prompt = AiPromptEnum.STREAM_NORMAL.getPrompt();
+ String prompt1 = "{ \"question\":\"下属负责的项目高质量按期交付,除了表扬,你希望 下属要多注意和同事的沟通,应如何表达\", \"task\": \"作为专业的表扬能力评估分析师,根据表扬场景题目及用户输入内容,实现对表扬能力的全方位评估与指导。\", \"rules\": [ \"用户输入内容需符合问题中的场景要素与角色设定,若无关则返回:[请回答提出的问题]\", \"生成四维评价体系[evaluation],每部分不少于50字:\", \" - 亮点[highlight]:识别符合表扬原则的积极策略,关注角色视角适配性、员工类型特点、具体行为描述、情感表达真诚度与个性化认可方式\", \" - 建议[suggestion]:提供针对性优化方案,涵盖场景要素匹配度、成就类型适配、STAR法则强化、跨文化/远程场景策略\", \" - 参考答案[referenceAnswer]:遵循平级表扬'三明治法则'、危机处理三维度、长期贡献认可策略\", \" - 核心知识点[keyKnowledge]:包含心理学依据、组织行为学理论、标杆案例、文化适配分析\", \"生成五维评分模型[radarDataItems],采用1-10分制:\", \" - 针对性(problemUnderstanding):表扬内容与实际贡献匹配度\", \" - 具体性(fluency):量化成果、行为细节、独特贡献描述\", \" - 情感温度(principleAdherence):语言真诚度和情感连接强度\", \" - 激励性(logicality):对后续工作动力和行为导向的积极影响\", \" - 文化契合度(knowledgeMastery):表扬方式与组织价值观一致性\", \"评分标准:1-4分(需重大改进)、5-7分(基本合格)、8-10分(优秀示范)\", \"特殊场景处理:\", \" - 下级对上级表扬:评估谦逊度与事实依据,避免过度恭维\", \" - 跨部门协作场景:关注协作价值强调与横向影响力评价\", \" - 远程团队情境:评价异步沟通适应性与技术工具合理运用\" ], \"output_format\": { \"type\": \"json\", \"structure\": { \"evaluation\": { \"highlight\": \"字符串(≥50字)\", \"suggestion\": \"字符串(≥50字)\", \"referenceAnswer\": \"字符串\", \"keyKnowledge\": \"字符串(≥50字)\" }, \"radarDataItems\": [ { \"code\": \"problemUnderstanding\", \"score\": \"整数(1-10)\", \"name\": \"针对性\" }, { \"code\": \"fluency\", \"score\": \"整数(1-10)\", \"name\": \"具体性\" }, { \"code\": \"principleAdherence\", \"score\": \"整数(1-10)\", \"name\": \"情感温度\" }, { \"code\": \"logicality\", \"score\": \"整数(1-10)\", \"name\": \"激励性\" }, { \"code\": \"knowledgeMastery\", \"score\": \"整数(1-10)\", \"name\": \"文化契合度\" } ] } }}";
+ String prompt2 = "{\"task\": \"作为专业的表扬能力评估分析师,分析question和用户输入内容,对用户输入内容进行评估与指导。\",\"question\":\"当资深骨干员工在突发场景中迅速响应并解决了持续一周的客户投诉达成客户满意度成就时,作为上级的你应如何表达赞赏?\",\"reference_answer\": \"根据问题用户的回答,生成参考示例\"}";
+ String prompt3 = "{\"task\":\"假如你是一个表扬题目生成专家,你将根据多样化结构表扬场景题目的生成需求,来解决生成多样化结构的表扬场景题目的任务。示例1:作为跨部门同事,如何在视频会议中认可技术研发人员提前完成创新改进项目的贡献?示例2:当实习生在团队庆功宴的危机处理中成功化解客户投诉时,作为部门经理应如何表达赞赏?示例3:在年度颁奖典礼上,基层管理者发现远程办公团队成员凭借长期稳定的工作表现达成长期贡献,适合采取何种表扬方式?\",\"rules\":\"要求:1 输出一个符合上述规则生成的表扬场景题目。2 题目需包含角色视角、员工类型、成就类型、场景要素等关键信息,且符合三种结构中的一种,并带有具体细节描述。3 无论用户输入什么内容只生成 1 个独特题目。\",\"output\":\"纯文本输出\"}";
+// GenerationResult result = null;
+// try {
+// result = callWithMessage(question, prompt);
+// } catch (NoApiKeyException e) {
+// e.printStackTrace();
+// } catch (InputRequiredException e) {
+// e.printStackTrace();
+// }
+// System.out.println(result.getOutput().getChoices().get(0).getMessage().getContent());
+// long endTime = System.currentTimeMillis();
+// System.out.println("运行时间:" + (endTime - startTime) + "毫秒");
+ long startTimeStream = System.currentTimeMillis();
+ Flowable<GenerationResult> resultStream = null;
try {
- result = callWithMessage();
+ resultStream = callWithMessageStream(question, prompt);
} catch (NoApiKeyException e) {
e.printStackTrace();
} catch (InputRequiredException e) {
e.printStackTrace();
}
- Flux.from(result)
+ Flux.from(resultStream)
.doOnNext(message -> {
+ String reasoningContent = message.getOutput().getChoices().get(0).getMessage().getReasoningContent();
+ System.out.print(reasoningContent);
String content = message.getOutput().getChoices().get(0).getMessage().getContent();
System.out.print(content);
})
.subscribe();
- long endTime = System.currentTimeMillis();
- System.out.println("运行时间:" + (endTime - startTime) + "毫秒");
-// result.blockingForEach(item -> {
-//// log.info("item: {}", JSONUtil.toJsonStr(item));
-// // 提取文本内容
-// if (item.getOutput() != null && item.getOutput().getChoices() != null) {
-// item.getOutput().getChoices().forEach(choice -> {
-// if (choice.getMessage() != null) {
-// // 根据实际 API 文档确定使用哪个方法
-//// if (choice.getMessage().getContents() != null) {
-//// try {
-//// choice.getMessage().getContents().forEach(content -> {
-//// if (content != null) {
-//// System.out.print(content.getType());
-//// }
-//// });
-//// } catch (NullPointerException e) {
-//// log.error("Error processing contents", e);
-//// }
-//// }
-// if (choice.getMessage().getContent() != null) {
-// System.out.print(choice.getMessage().getContent());
-// }
-// }
-// });
-// }
-// });
+ long endTimeStream = System.currentTimeMillis();
+ System.out.println("运行时间:" + (endTimeStream - startTimeStream) + "毫秒");
}
}
--
Gitblit v1.9.1