From 46a2e8027a053d4ba7a4dfe357e562c9b41a1047 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Thu, 04 Sep 2025 17:29:19 +0800
Subject: [PATCH] feat(ai): 添加阿里百炼工作流策略实现
---
src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmApplicationAppIdEnum.java | 37 ++++++
src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmStrategyEnum.java | 1
src/main/java/cc/mrbird/febs/ai/service/ApiMemberTalkStreamService.java | 2
src/main/java/cc/mrbird/febs/ai/service/impl/ApiMemberTalkStreamServiceImpl.java | 54 +++++++++
src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java | 12 ++
src/main/java/cc/mrbird/febs/ai/strategy/Impl/AliApplicationLlmStrategyServiceImpl.java | 208 ++++++++++++++++++++++++++++++++++
6 files changed, 314 insertions(+), 0 deletions(-)
diff --git a/src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java b/src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java
index b13f133..1dea19b 100644
--- a/src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java
+++ b/src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java
@@ -83,6 +83,18 @@
return apiMemberTalkStreamService.answer(dto);
}
+ @ApiOperation("回答(流式)")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "流式响应", response = cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo.class),
+ })
+ @PostMapping("/answerV2")
+ public Flux<FebsResponse> answerV2(@RequestBody @Validated AiTalkAnswerStreamDto dto) {
+ if (StrUtil.isEmpty(dto.getId()) || StrUtil.isEmpty(dto.getReqContext())|| StrUtil.isEmpty(dto.getReqContext())){
+ return Flux.just(new FebsResponse().fail().message("参数异常"));
+ }
+ return apiMemberTalkStreamService.answerV2(dto);
+ }
+
@ApiOperation(value = "保存AI回答", notes = "保存AI回答")
@PostMapping(value = "/saveAnswer")
public FebsResponse saveAnswer(@RequestBody @Validated ApiMemberTalkAnswerSavaDto dto) {
diff --git a/src/main/java/cc/mrbird/febs/ai/service/ApiMemberTalkStreamService.java b/src/main/java/cc/mrbird/febs/ai/service/ApiMemberTalkStreamService.java
index 93da81c..e2d593f 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/ApiMemberTalkStreamService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/ApiMemberTalkStreamService.java
@@ -23,5 +23,7 @@
Flux<FebsResponse> answer(AiTalkAnswerStreamDto dto);
+ Flux<FebsResponse> answerV2(AiTalkAnswerStreamDto dto);
+
FebsResponse saveAnswer(ApiMemberTalkAnswerSavaDto dto);
}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/ApiMemberTalkStreamServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/ApiMemberTalkStreamServiceImpl.java
index 41c55c7..ff0df55 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/ApiMemberTalkStreamServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/ApiMemberTalkStreamServiceImpl.java
@@ -182,6 +182,57 @@
return llmStrategyFactory.getCalculationStrategyMap().get(modelName).llmInvokeStreamingNoThink(llmStrategyDtoList);
}
+ @Override
+ public Flux<FebsResponse> answerV2(AiTalkAnswerStreamDto dto) {
+
+
+ String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
+ Integer type = dto.getType();
+ String memberTalkId = dto.getId();
+ AiMemberTalk aiMemberTalk = this.getById(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陪练不存在");
+ }
+ LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery = Wrappers.lambdaQuery(AiMemberTalkItem.class);
+ memberTalkItemQuery.eq(AiMemberTalkItem::getMemberId,memberUuid);
+ memberTalkItemQuery.eq(AiMemberTalkItem::getMemberTalkId,memberTalkId);
+ memberTalkItemQuery.eq(AiMemberTalkItem::getType,1);
+ memberTalkItemQuery.orderByDesc(AiMemberTalkItem::getCreatedTime);
+ memberTalkItemQuery.last("limit 1");
+ AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getByQuery(memberTalkItemQuery);
+
+ String question = aiMemberTalkItem.getContext();
+ String promptHead = aiProductRole.getPromptHead();
+ String answer = dto.getReqContext();
+
+ List<LlmStrategyDto> llmStrategyDtoList = new ArrayList<>();
+ LlmStrategyDto llmStrategyDto = this.buildLlmStrategyDtoList(promptHead, 1);
+ llmStrategyDtoList.add(llmStrategyDto);
+ llmStrategyDto = this.buildLlmStrategyDtoList(question, 3);
+ llmStrategyDtoList.add(llmStrategyDto);
+ llmStrategyDto = this.buildLlmStrategyDtoList(answer, 2);
+ llmStrategyDtoList.add(llmStrategyDto);
+ llmStrategyDto = this.buildLlmStrategyDtoList(String.valueOf(type), 4);
+ llmStrategyDtoList.add(llmStrategyDto);
+ String modelName = LlmStrategyEnum.getName(aiService.getSystemSetAiType());
+
+ return llmStrategyFactory.getCalculationStrategyMap().get(modelName).llmInvokeStreamingNoThink(llmStrategyDtoList);
+ }
+
private String buildPrompt(String question,String answer,String promptHead, String promptTemplate,Integer type){
AiPromptJsonReq aiPromptJsonReq = new AiPromptJsonReq();
// aiPromptJsonReq.setQuestion( question);
@@ -204,6 +255,9 @@
if (type == 3){
llmStrategyDto.setRole(Role.ASSISTANT.getValue());
}
+ if (type == 4){
+ llmStrategyDto.setRole(Role.TOOL.getValue());
+ }
llmStrategyDto.setContent(Str);
return llmStrategyDto;
diff --git a/src/main/java/cc/mrbird/febs/ai/strategy/Impl/AliApplicationLlmStrategyServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/strategy/Impl/AliApplicationLlmStrategyServiceImpl.java
new file mode 100644
index 0000000..bd1e116
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/strategy/Impl/AliApplicationLlmStrategyServiceImpl.java
@@ -0,0 +1,208 @@
+package cc.mrbird.febs.ai.strategy.Impl;
+
+import cc.mrbird.febs.ai.strategy.LlmStrategyService;
+import cc.mrbird.febs.ai.strategy.enumerates.LlmApplicationAppIdEnum;
+import cc.mrbird.febs.ai.strategy.enumerates.LlmStrategyContextEnum;
+import cc.mrbird.febs.ai.strategy.param.LlmStrategyDto;
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.exception.FebsException;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.dashscope.aigc.generation.Generation;
+import com.alibaba.dashscope.aigc.generation.GenerationParam;
+import com.alibaba.dashscope.aigc.generation.GenerationResult;
+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.common.Message;
+import com.alibaba.dashscope.common.Role;
+import com.alibaba.dashscope.exception.InputRequiredException;
+import com.alibaba.dashscope.exception.NoApiKeyException;
+import com.alibaba.dashscope.utils.JsonUtils;
+import io.reactivex.Flowable;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Flux;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@Component("AliApplicationLlmStrategyServiceImpl")
+public class AliApplicationLlmStrategyServiceImpl implements LlmStrategyService {
+
+ private static final String apiKey = "sk-babdcf8799144134915cee2683794b2f";
+
+ private final String bizParam_1 = "prompt_ai_system";
+ private final String bizParam_2 = "question";
+ private final String bizParam_3 = "query";
+
+ private HashMap getPrompt(List<LlmStrategyDto> dto) {
+ HashMap<String, String> bizParamsMap = new HashMap<>();
+ for (LlmStrategyDto dtoItem : dto){
+ if (StrUtil.equals(dtoItem.getRole(), Role.SYSTEM.getValue())){
+ bizParamsMap.put(bizParam_1, dtoItem.getContent());
+ }
+ if (StrUtil.equals(dtoItem.getRole(),Role.ASSISTANT.getValue())){
+ bizParamsMap.put(bizParam_2, dtoItem.getContent());
+ }
+ if (StrUtil.equals(dtoItem.getRole(),Role.USER.getValue())){
+ bizParamsMap.put(bizParam_3, dtoItem.getContent());
+ }
+ }
+ return bizParamsMap;
+ }
+
+ private String getQuery(List<LlmStrategyDto> dto) {
+ String query = null;
+ for (LlmStrategyDto dtoItem : dto){
+ if (StrUtil.equals(dtoItem.getRole(),Role.USER.getValue())){
+ query = dtoItem.getContent();
+ break;
+ }
+ }
+ return query;
+ }
+
+ private String getAppId(List<LlmStrategyDto> dto) {
+ String appId = null;
+ for (LlmStrategyDto dtoItem : dto){
+ if (StrUtil.equals(dtoItem.getRole(),Role.TOOL.getValue())){
+ int code = Integer.parseInt(dtoItem.getContent());
+ appId = LlmApplicationAppIdEnum.HIGH_LIGHT.getAppIdByCode(code);
+ break;
+ }
+ }
+ return appId;
+ }
+
+ @Override
+ public FebsResponse llmInvokeNonStreaming(List<LlmStrategyDto> dto) {
+ return null;
+ }
+
+ @Override
+ public Flux<FebsResponse> llmInvokeStreamingWithThink(List<LlmStrategyDto> dto) {
+
+ if (CollUtil.isEmpty(dto)){
+ throw new FebsException("百炼大模型初始化异常");
+ }
+ List<Message> messages = new ArrayList<>();
+ for (LlmStrategyDto dtoItem : dto){
+ 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());
+ }
+ }
+
+ GenerationParam generationParam = GenerationParam.builder()
+ // 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:.apiKey("sk-xxx")
+ .apiKey(apiKey)
+ // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
+ .model("qwen-plus")
+ .messages(messages)
+ .resultFormat(GenerationParam.ResultFormat.MESSAGE)
+ .enableThinking(true)
+ .incrementalOutput(true)
+ .build();
+
+ long startTime = System.currentTimeMillis();
+ Generation gen = new Generation();
+ Flowable<GenerationResult> result;
+ try {
+ result = gen.streamCall(generationParam);
+ } catch (NoApiKeyException | InputRequiredException e) {
+ throw new FebsException(StrUtil.format("百炼大模型输出失败:{}",e.getMessage()));
+ }
+
+ return Flux.from(result)
+ .map(message -> {
+ HashMap<String, String> stringStringHashMap = new HashMap<>();
+ if (StrUtil.isNotEmpty(message.getOutput().getChoices().get(0).getMessage().getReasoningContent())){
+ stringStringHashMap.put(LlmStrategyContextEnum.THINK.name(),message.getOutput().getChoices().get(0).getMessage().getReasoningContent());
+
+ System.out.print(message.getOutput().getChoices().get(0).getMessage().getReasoningContent());
+ }
+ if (StrUtil.isNotEmpty(message.getOutput().getChoices().get(0).getMessage().getContent())){
+ stringStringHashMap.put(LlmStrategyContextEnum.CONTENT.name(),message.getOutput().getChoices().get(0).getMessage().getContent());
+ System.out.print(message.getOutput().getChoices().get(0).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));
+ });
+ }
+
+ @Override
+ public Flux<FebsResponse> llmInvokeStreamingNoThink(List<LlmStrategyDto> dto) {
+ if (CollUtil.isEmpty(dto)){
+ throw new FebsException("百炼工作流初始化异常");
+ }
+ HashMap prompt = getPrompt(dto);
+ String query = getQuery(dto);
+ String appId = getAppId(dto);
+ if (prompt == null || prompt.size() == 0){
+ throw new FebsException("百炼工作流初始化异常");
+ }
+ if (query == null){
+ throw new FebsException("百炼工作流初始化异常");
+ }
+ if (appId == null){
+ throw new FebsException("百炼工作流初始化异常");
+ }
+ long startTime = System.currentTimeMillis();
+ ApplicationParam param = ApplicationParam.builder()
+ // 若没有配置环境变量,可用百炼API Key将下行替换为:.apiKey("sk-xxx")。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。
+ .apiKey(apiKey)
+ .appId(appId) //替换为实际的应用 ID
+ .flowStreamMode(FlowStreamMode.MESSAGE_FORMAT)
+ .prompt(query)
+ .bizParams(JsonUtils.toJsonObject( prompt))
+ .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<>();
+ 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));
+ });
+ }
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmApplicationAppIdEnum.java b/src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmApplicationAppIdEnum.java
new file mode 100644
index 0000000..12eef5b
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmApplicationAppIdEnum.java
@@ -0,0 +1,37 @@
+package cc.mrbird.febs.ai.strategy.enumerates;
+
+import lombok.Getter;
+
+@Getter
+public enum LlmApplicationAppIdEnum {
+
+ NORMAL(5,"8ea7ab1665de4a88b868849318d72f45","通用"),
+
+ KEY_KNOWLEDGE(4,"8ea7ab1665de4a88b868849318d72f45","知识点总结"),
+
+ REFERENCE_ANSWER(3,"5caf9f87434b4c97becd00024dc6a2b3","标准答案"),
+
+ SUGGESTION(2,"8abbd36ccdd64cd99198546d76a8253b","建议"),
+
+ HIGH_LIGHT(1,"44d818ab83a54131a59b1d2c2fa4a14b","亮点");
+
+ private final int code;
+ private final String appId;
+ private final String value;
+
+ LlmApplicationAppIdEnum(int code, String appId, String value) {
+
+ this.code = code;
+ this.appId = appId;
+ this.value = value;
+ }
+
+ public static String getAppIdByCode(int code) {
+ for (LlmApplicationAppIdEnum c : LlmApplicationAppIdEnum.values()) {
+ if (c.code == code) {
+ return c.appId;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmStrategyEnum.java b/src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmStrategyEnum.java
index fe13c1d..b06d73c 100644
--- a/src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmStrategyEnum.java
+++ b/src/main/java/cc/mrbird/febs/ai/strategy/enumerates/LlmStrategyEnum.java
@@ -10,6 +10,7 @@
public enum LlmStrategyEnum {
+ ALI_APPLICATION(3,"AliApplicationLlmStrategyServiceImpl","阿里百炼工作流"),
ALI(2,"AliLlmStrategyService","阿里百炼大模型"),
HS(1,"HsLlmStrategyService","火山大模型");
--
Gitblit v1.9.1