| 2025-10-30 | Administrator | ![]() |
| 2025-10-30 | Administrator | ![]() |
| 2025-10-30 | Administrator | ![]() |
| 2025-10-30 | Administrator | ![]() |
| 2025-10-30 | Administrator | ![]() |
| 2025-10-30 | Administrator | ![]() |
| 2025-10-30 | Administrator | ![]() |
src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java
@@ -7,6 +7,7 @@ import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkListDto; import cc.mrbird.febs.ai.res.memberTalk.*; import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkReloadStreamVo; import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkStreamV2Vo; import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkStreamVo; import cc.mrbird.febs.ai.service.AiMemberTalkStreamService; import cc.mrbird.febs.common.entity.FebsResponse; @@ -40,44 +41,13 @@ return aiMemberTalkStreamService.start(dto); } @ApiOperation(value = "陪练记录", notes = "陪练记录") @ApiOperation("继续回答") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiMemberTalkListVo.class) @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamV2Vo.class), }) @PostMapping(value = "/talkList") public FebsResponse talkList(@RequestBody @Validated ApiMemberTalkListDto dto) { return aiMemberTalkStreamService.talkList(dto); } @ApiOperation(value = "陪练记录-答题记录", notes = "陪练记录-答题记录") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiTalkMemberListVo.class) }) @PostMapping(value = "/talkMemberList") public FebsResponse talkMemberList(@RequestBody @Validated ApiTalkMemberListDto dto) { return aiMemberTalkStreamService.talkMemberList(dto); } @ApiOperation(value = "陪练记录-查看报告", notes = "陪练记录-查看报告") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiTalkReportListVo.class) }) @PostMapping(value = "/talkReportList") public Flux<FebsResponse> talkReportList(@RequestBody @Validated ApiTalkReportListDto dto) { return aiMemberTalkStreamService.talkReportList(dto); } @ApiOperation(value = "陪练记录-查看报告", notes = "陪练记录-查看报告") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiTalkReportListVo.class) }) @PostMapping(value = "/talkReportListV2") public FebsResponse talkReportListV2(@RequestBody @Validated ApiTalkReportListDto dto) { return aiMemberTalkStreamService.talkReportListV2(dto); @PostMapping("/startV2") public Flux<FebsResponse> startV2(@RequestBody @Validated ApiMemberTalkStreamV2Dto dto) { return aiMemberTalkStreamService.startV2(dto); } @ApiOperation(value = "再练一次(重新回答)", notes = "再练一次(重新回答)") @@ -146,7 +116,7 @@ return aiMemberTalkStreamService.answerV3(dto); } @ApiOperation(value = "保存AI回答", notes = "保存AI回答") @ApiOperation(value = "保存AI回答(继续回答)", notes = "保存AI回答(继续回答)") @PostMapping(value = "/saveAnswer") public FebsResponse saveAnswer(@RequestBody @Validated ApiMemberTalkAnswerSavaDto dto) { @@ -160,4 +130,45 @@ return aiMemberTalkStreamService.saveReport(dto); } @ApiOperation(value = "陪练记录", notes = "陪练记录") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiMemberTalkListVo.class) }) @PostMapping(value = "/talkList") public FebsResponse talkList(@RequestBody @Validated ApiMemberTalkListDto dto) { return aiMemberTalkStreamService.talkList(dto); } @ApiOperation(value = "陪练记录-答题记录", notes = "陪练记录-答题记录") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiTalkMemberListVo.class) }) @PostMapping(value = "/talkMemberList") public FebsResponse talkMemberList(@RequestBody @Validated ApiTalkMemberListDto dto) { return aiMemberTalkStreamService.talkMemberList(dto); } @ApiOperation(value = "陪练记录-查看报告", notes = "陪练记录-查看报告") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiTalkReportListVo.class) }) @PostMapping(value = "/talkReportList") public Flux<FebsResponse> talkReportList(@RequestBody @Validated ApiTalkReportListDto dto) { return aiMemberTalkStreamService.talkReportList(dto); } @ApiOperation(value = "陪练记录-查看报告", notes = "陪练记录-查看报告") @ApiResponses({ @ApiResponse(code = 200, message = "success", response = ApiTalkReportListVo.class) }) @PostMapping(value = "/talkReportListV2") public FebsResponse talkReportListV2(@RequestBody @Validated ApiTalkReportListDto dto) { return aiMemberTalkStreamService.talkReportListV2(dto); } } src/main/java/cc/mrbird/febs/ai/entity/AiProductRole.java
@@ -49,7 +49,7 @@ private String iconImg; /** * 模型ID * 角色的定义内容 */ private String modelId; src/main/java/cc/mrbird/febs/ai/enumerates/AiTalkOutputEnum.java
@@ -9,6 +9,8 @@ @Getter public enum AiTalkOutputEnum { CONTEXT_TALK(5,"AI陪练连续对话","CONTEXT_TALK"), KEY_KNOWLEDGE(4,"权威参考依据","KEY_KNOWLEDGE"), REFERENCE_ANSWER(3,"情境化示例答案","REFERENCE_ANSWER"), src/main/java/cc/mrbird/febs/ai/req/memberTalkStream/ApiMemberTalkAnswerSavaDto.java
@@ -22,14 +22,8 @@ @ApiModelProperty(value = "会话ID", example = "10") private String memberTalkId; @ApiModelProperty(value = "会话ItemID", example = "10") private String memberTalkItemId; @NotBlank(message = "回复内容不能为空") @ApiModelProperty(value = "回复内容", example = "10") private String content; @NotNull(message = "类型ID不能为空") @ApiModelProperty(value = "类型 1:亮点 2:建议 3:参考答案 4:知识点总结", example = "10") private Integer type; } src/main/java/cc/mrbird/febs/ai/req/memberTalkStream/ApiMemberTalkStreamV2Dto.java
New file @@ -0,0 +1,20 @@ package cc.mrbird.febs.ai.req.memberTalkStream; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotBlank; @Data @ApiModel(value = "ApiMemberTalkStreamV2Dto", description = "参数") public class ApiMemberTalkStreamV2Dto { @NotBlank(message = "ID不能为空") @ApiModelProperty(value = "产品ID", example = "10") private String id; @NotBlank(message = "ID不能为空") @ApiModelProperty(value = "对话ID", example = "10") private String memberTalkId; } src/main/java/cc/mrbird/febs/ai/res/memberTalkStream/ApiMemberTalkStreamV2Vo.java
New file @@ -0,0 +1,42 @@ package cc.mrbird.febs.ai.res.memberTalkStream; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data @ApiModel(value = "ApiMemberTalkStreamV2Vo", description = "参数") public class ApiMemberTalkStreamV2Vo { /** * 用户对话ID (UUID) */ @ApiModelProperty(value = "会话ID") private String memberTalkItemId; @ApiModelProperty(value = "会话ID") private String memberTalkId; /** * 类型 1-AI提问 2-用户回答 3-AI分析结果 */ @ApiModelProperty(value = "类型 1-AI提问 2-用户回答 3-AI分析结果") private Integer type; /** * 内容 */ @ApiModelProperty(value = "内容(文本格式)") private String context; @ApiModelProperty(value = "内容亮点、建议、参考答案、核心知识点雷达图表数据(数据对象)") private String report; @ApiModelProperty(value = "题目数量") private Integer questionCnt; @ApiModelProperty(value = "作答数量") private Integer doneCnt; } src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkStreamService.java
@@ -20,6 +20,8 @@ */ FebsResponse start(ApiMemberTalkStreamDto dto); Flux<FebsResponse> startV2(ApiMemberTalkStreamV2Dto dto); FebsResponse talkList(ApiMemberTalkListDto dto); FebsResponse talkMemberList(ApiTalkMemberListDto dto); src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkStreamServiceImpl.java
@@ -10,10 +10,8 @@ import cc.mrbird.febs.ai.req.memberTalkStream.*; import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkListDto; import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkMemberAnswerSavaVo; import cc.mrbird.febs.ai.res.memberTalk.ApiTalkReportListVo; import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkReloadStreamVo; import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkStreamVo; import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkListVo; import cc.mrbird.febs.ai.service.*; import cc.mrbird.febs.ai.strategy.LlmStrategyFactory; import cc.mrbird.febs.ai.strategy.enumerates.LlmApplicationAppIdEnum; @@ -87,19 +85,32 @@ query.eq(AiMemberTalk::getState,AiTypeEnum.AI_MEMBER_TALK_STATE_ING.getCode()); query.orderByDesc(AiMemberTalk::getCreatedTime); query.last("limit 1"); AiMemberTalk aiMemberTalk = aiMemberTalkService.getByQuery(query); if (ObjectUtil.isNull(aiMemberTalk)){ aiMemberTalk = aiMemberTalkService.add(memberUuid,companyId,productId,nowTime,questionCount); } ApiMemberTalkStreamVo apiMemberTalkVo = new ApiMemberTalkStreamVo(); AiMemberTalk aiMemberTalk = aiMemberTalkService.getByQuery(query); String title = "加载时间过程,请重试!"; AiProductQuestion question = aiProductQuestionService.createQuestion(productId); if (ObjectUtil.isNotNull(question)){ title = question.getTitle(); AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.add(memberUuid, aiMemberTalk.getId(),companyId, 1, title, nowTime); apiMemberTalkVo.setMemberTalkItemId(aiMemberTalkItem.getId()); aiMemberTalkService.updateTimeUpdate(nowTime,aiMemberTalk.getId()); if (ObjectUtil.isNull(aiMemberTalk)){ aiMemberTalk = aiMemberTalkService.add(memberUuid,companyId,productId,nowTime,questionCount); AiProductQuestion question = aiProductQuestionService.createQuestion(productId); if (ObjectUtil.isNotNull(question)){ title = question.getTitle(); AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.add(memberUuid, aiMemberTalk.getId(),companyId, 1, title, nowTime); apiMemberTalkVo.setMemberTalkItemId(aiMemberTalkItem.getId()); aiMemberTalkService.updateTimeUpdate(nowTime,aiMemberTalk.getId()); } }else{ AiMemberTalkItem oneByQuery = aiMemberTalkItemService.getOneByQuery( Wrappers.lambdaQuery(AiMemberTalkItem.class) .eq(AiMemberTalkItem::getMemberTalkId, aiMemberTalk.getId()) .eq(AiMemberTalkItem::getType, 1) .orderByDesc(AiMemberTalkItem::getCreatedTime) .last("limit 1") ); if (ObjectUtil.isNotNull(oneByQuery)){ title = oneByQuery.getContext(); apiMemberTalkVo.setMemberTalkItemId(oneByQuery.getId()); } } apiMemberTalkVo.setMemberTalkId(aiMemberTalk.getId()); apiMemberTalkVo.setType(1); @@ -107,6 +118,52 @@ apiMemberTalkVo.setQuestionCnt(aiMemberTalk.getQuestionCnt()); apiMemberTalkVo.setDoneCnt(aiMemberTalk.getDoneCnt() + 1); return new FebsResponse().success().data(apiMemberTalkVo); } @Override public Flux<FebsResponse> startV2(ApiMemberTalkStreamV2Dto dto) { String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid(); String memberTalkId = dto.getMemberTalkId(); AiMemberTalk aiMemberTalk = aiMemberTalkMapper.selectById(memberTalkId); if (ObjectUtil.isNull(aiMemberTalk)){ throw new FebsException("对话不存在"); } LambdaQueryWrapper<AiProductRoleLink> productLinkQuery = Wrappers.lambdaQuery(AiProductRoleLink.class); productLinkQuery.eq(AiProductRoleLink::getProductId,aiMemberTalk.getProductId()); productLinkQuery.last("limit 1"); AiProductRoleLink aiProductRoleLink = aiProductRoleLinkService.getByQuery(productLinkQuery); if(ObjectUtil.isNull(aiProductRoleLink)){ throw new FebsException("产品没有关联AI陪练"); } String productRoleId = aiProductRoleLink.getProductRoleId(); AiProductRole aiProductRole = aiProductRoleService.getById(productRoleId); if (ObjectUtil.isNull(aiProductRole)){ throw new FebsException("产品AI陪练不存在"); } String promptHead = aiProductRole.getModelId(); List<LlmStrategyDto> llmStrategyDtoList = new ArrayList<>(); LlmStrategyDto llmStrategyDto = this.buildLlmStrategyDtoList(promptHead, 1); llmStrategyDtoList.add(llmStrategyDto); llmStrategyDto = this.buildLlmStrategyDtoList("请按照上下文内容,进行对话的延续,例如提出一个相关的问题或者对内容进行一个延续", 2); llmStrategyDtoList.add(llmStrategyDto); AiCompanyWorkflow aiCompanyWorkflow = aiCompanyWorkflowService.getByTypeAndCompanyId(LlmApplicationAppIdEnum.CONTEXT_TALK.getCode(),aiMemberTalk.getCompanyId()); if (ObjectUtil.isNull(aiCompanyWorkflow)){ throw new FebsException("工作流配置异常,请联系管理员"); } llmStrategyDto = this.buildLlmStrategyDtoList(aiCompanyWorkflow.getCode(), 4); llmStrategyDtoList.add(llmStrategyDto); LlmStrategyDto llmStrategyDtoMessage = buildMessages(memberTalkId); llmStrategyDtoList.add(llmStrategyDtoMessage); String modelName = LlmStrategyEnum.getName(aiService.getSystemSetAiType()); return llmStrategyFactory.getCalculationStrategyMap().get(modelName).llmInvokeStreamingNoThink(llmStrategyDtoList); } @Override @@ -402,28 +459,8 @@ this.updateMemberTalkUpdateTime(aiMemberTalk.getState(),aiMemberTalk.getDoneCnt(),aiMemberTalk.getId(), new Date()); String companyId = aiMemberTalk.getCompanyId(); Integer type = dto.getType(); String memberTalkItemId; String contentByCode = AiTalkOutputEnum.HIGH_LIGHT.getCodeByType(type); if(StrUtil.isEmpty(dto.getMemberTalkItemId())){ HashMap<String, String> stringStringHashMap = new HashMap<>(); stringStringHashMap.put(contentByCode,content); AiMemberTalkItem add = aiMemberTalkItemService.add(memberUuid, memberTalkId,companyId, 3, JSONUtil.toJsonStr(stringStringHashMap), new Date()); memberTalkItemId = add.getId(); }else{ memberTalkItemId = dto.getMemberTalkItemId(); AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getById(memberTalkItemId); String context = aiMemberTalkItem.getContext(); HashMap<String, String> stringStringHashMap = JSONUtil.toBean(context, HashMap.class); stringStringHashMap.put(contentByCode,content); aiMemberTalkItemService.update(null, Wrappers.lambdaUpdate(AiMemberTalkItem.class) .set(AiMemberTalkItem::getContext,JSONUtil.toJsonStr(stringStringHashMap)) .set(AiMemberTalkItem::getUpdatedTime,new Date()) .set(AiMemberTalkItem::getRevision,aiMemberTalkItem.getRevision()+1) .eq(AiMemberTalkItem::getId,memberTalkItemId) ); } AiMemberTalkItem add = aiMemberTalkItemService.add(memberUuid, memberTalkId,companyId, 1, content, new Date()); String memberTalkItemId = add.getId(); HashMap<String, String> stringStringHashMap = new HashMap<>(); stringStringHashMap.put("memberTalkItemId",memberTalkItemId); src/main/java/cc/mrbird/febs/ai/strategy/Impl/AliApplicationLlmStrategyServiceImpl.java
@@ -60,6 +60,36 @@ return bizParamsMap; } private List<Message> getMessages(List<LlmStrategyDto> dto) { List<Message> messages = new ArrayList<>(); for (LlmStrategyDto item : dto){ if (StrUtil.equals(item.getRole(),AiTypeEnum.MESSAGES.getName())){ List<LlmStrategyDto> messagesList = item.getMessages(); for (LlmStrategyDto dtoItem : messagesList){ if (StrUtil.equals(dtoItem.getRole(),Role.SYSTEM.getValue())){ messages.add(Message.builder() .role(Role.SYSTEM.getValue()) .content(dtoItem.getContent()) .build()); } if (StrUtil.equals(dtoItem.getRole(),Role.USER.getValue())){ messages.add(Message.builder() .role(Role.USER.getValue()) .content(dtoItem.getContent()) .build()); } if (StrUtil.equals(dtoItem.getRole(),Role.ASSISTANT.getValue())){ messages.add(Message.builder() .role(Role.ASSISTANT.getValue()) .content(dtoItem.getContent()) .build()); } } } } return messages; } private String getQuery(List<LlmStrategyDto> dto) { String query = null; for (LlmStrategyDto dtoItem : dto){ @@ -188,6 +218,8 @@ if (appId == null){ throw new FebsException("百炼工作流初始化异常"); } List<Message> messages = getMessages(dto); long startTime = System.currentTimeMillis(); ApplicationParam param = ApplicationParam.builder() // 若没有配置环境变量,可用百炼API Key将下行替换为:.apiKey("sk-xxx")。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。 @@ -195,6 +227,7 @@ .appId(appId) //替换为实际的应用 ID .flowStreamMode(FlowStreamMode.MESSAGE_FORMAT) .prompt(query) .messages( messages) .bizParams(JsonUtils.toJsonObject( prompt)) .build(); src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmApplicationAppIdEnum.java
@@ -5,6 +5,8 @@ @Getter public enum LlmApplicationAppIdEnum { CONTEXT_TALK(8,"48468fdcdfc340f2950e49559a4dbcd3","AI陪练连续对话"), REPORT(6,"48468fdcdfc340f2950e49559a4dbcd3","生成报告"), NORMAL(5,"8ea7ab1665de4a88b868849318d72f45","通用"),