From f8f1d6a91b32077c69dd97334559fe3165ff79b6 Mon Sep 17 00:00:00 2001 From: Administrator <15274802129@163.com> Date: Tue, 02 Sep 2025 13:45:20 +0800 Subject: [PATCH] feat(ai): 添加 AI 用户陪练(流式)功能 --- src/main/java/cc/mrbird/febs/ai/controller/TestController.java | 158 +++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 117 insertions(+), 41 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..70d4a61 100644 --- a/src/main/java/cc/mrbird/febs/ai/controller/TestController.java +++ b/src/main/java/cc/mrbird/febs/ai/controller/TestController.java @@ -4,6 +4,10 @@ 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.service.AiService; +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.utils.AppContants; @@ -15,8 +19,6 @@ 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.fastjson.JSONObject; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import io.reactivex.Flowable; @@ -39,9 +41,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 +56,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() { @@ -110,12 +112,14 @@ 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 +129,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,15 +142,74 @@ } - public static Flowable<GenerationResult> callWithMessage() throws NoApiKeyException, InputRequiredException { + + @ApiOperation("提问AI(流式)V3") + @ApiResponses({ + @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class), + }) + @PostMapping("/answerStreamV3") + public Flux<FebsResponse> answerStreamV3(@RequestBody @Validated AiTalkAnswerStream dto) { + if (StrUtil.isEmpty(dto.getQuestion())){ + return Flux.just(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).llmInvokeStreamingWithThink(llmStrategyDtoList); + } + + + + @ApiOperation("提问AI(非流式响应)V4") + @ApiResponses({ + @ApiResponse(code = 200, message = "非流式响应", response = ApiMemberTalkStreamVo.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") @@ -162,50 +225,63 @@ .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("qwen-plus") + .messages(Arrays.asList(systemMsg, userMsg)) + .resultFormat(GenerationParam.ResultFormat.MESSAGE) + .build(); + return gen.call(param); + } public static void main(String[] args) { //定义一个开始时间为启动这个main方法的开始时间,用于计算运行时间 long startTime = System.currentTimeMillis(); - - Flowable<GenerationResult> result = null; + String question = "干的漂亮"; + String prompt = "{\"task\": \"作为专业的表扬能力评估分析师,分析question和用户输入内容,对用户输入内容进行评估与指导。\",\"question\":\"当资深骨干员工在突发场景中迅速响应并解决了持续一周的客户投诉达成客户满意度成就时,作为上级的你应如何表达赞赏?\",\"highlight\": \"识别符合表扬原则的积极策略,关注角色视角适配性、员工类型特点、具体行为描述、情感表达真诚度与个性化认可方式\"}"; + String prompt1 = "{\"task\": \"作为专业的表扬能力评估分析师,分析question和用户输入内容,对用户输入内容进行评估与指导。\",\"question\":\"当资深骨干员工在突发场景中迅速响应并解决了持续一周的客户投诉达成客户满意度成就时,作为上级的你应如何表达赞赏?\",\"suggestion\": \"提供针对性优化方案,涵盖场景要素匹配度提升(如正式场合需增加公开认可环节)、成就类型适配改进(如创新贡献应强调思维突破而非仅结果)、具体维度强化方向(如增加 STAR 法则中的 Result 部分描述)、跨文化 / 远程场景特殊策略(如虚拟团队可采用异步表扬 + 公开认可组合)\"}"; + 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(); + result = callWithMessage(question, prompt); } catch (NoApiKeyException e) { e.printStackTrace(); } catch (InputRequiredException e) { e.printStackTrace(); } - Flux.from(result) + 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 { + resultStream = callWithMessageStream(question, prompt); + } catch (NoApiKeyException e) { + e.printStackTrace(); + } catch (InputRequiredException e) { + e.printStackTrace(); + } + Flux.from(resultStream) .doOnNext(message -> { 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