From 8b958cdc9b77d7b4f2fc67967329491c006951e1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 May 2025 19:50:29 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E3=80=90AI=20=E5=A4=A7=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E3=80=91=E5=B7=A5=E4=BD=9C=E6=B5=81=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20OLLAMA=20=E7=9A=84=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/workflow/AiWorkflowController.http | 12 ++++ .../workflow/vo/AiWorkflowTestReqVO.java | 14 +++- .../ai/service/model/AiModelService.java | 2 +- .../ai/service/model/AiModelServiceImpl.java | 11 ++++ .../workflow/AiWorkflowServiceImpl.java | 65 +++++++++++-------- 5 files changed, 74 insertions(+), 30 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http new file mode 100644 index 0000000000..8dc1b0c0f7 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http @@ -0,0 +1,12 @@ +### 测试 AI 工作流 +POST {{baseUrl}}/ai/workflow/test +Content-Type: application/json +Authorization: {{token}} +tenant-id: {{adminTenantId}} + +{ + "id": 4, + "params": { + "message": "1 + 1 = ?" + } +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java index 4dc509e89d..37b455cc03 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java @@ -1,7 +1,8 @@ package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; +import cn.hutool.core.util.StrUtil; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.AssertTrue; import lombok.Data; import java.util.Map; @@ -10,11 +11,18 @@ import java.util.Map; @Data public class AiWorkflowTestReqVO { - @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - @NotEmpty(message = "工作流模型不能为空") + @Schema(description = "工作流编号", example = "1024") + private Long id; + + @Schema(description = "工作流模型", example = "{}") private String graph; @Schema(description = "参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") private Map params; + @AssertTrue(message = "工作流或模型,必须传递一个") + public boolean isGraphValid() { + return id != null || StrUtil.isNotEmpty(graph); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java index c0802b0fc3..1b5aabbc51 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java @@ -133,7 +133,7 @@ public interface AiModelService { VectorStore getOrCreateVectorStore(Long id, Map> metadataFields); /** - * 获取 Tinyflow 所需 LLm Provider + * 获取 TinyFlow 所需 LLm Provider * * @param tinyflow tinyflow * @param modelId AI 模型 ID diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java index a3f764c2eb..3c7c3a952d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java @@ -12,6 +12,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReq import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper; +import com.agentsflex.llm.ollama.OllamaLlm; +import com.agentsflex.llm.ollama.OllamaLlmConfig; import com.agentsflex.llm.qwen.QwenLlm; import com.agentsflex.llm.qwen.QwenLlmConfig; import dev.tinyflow.core.Tinyflow; @@ -171,6 +173,7 @@ public class AiModelServiceImpl implements AiModelService { // return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); } + // TODO @lesan:是不是返回 Llm 对象会好点哈? @Override public void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId) { AiModelDO model = validateModel(modelId); @@ -178,12 +181,20 @@ public class AiModelServiceImpl implements AiModelService { AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); switch (platform) { // TODO @lesan 考虑到未来不需要使用agents-flex 现在仅测试通义千问 + // TODO @lesan:【重要】是不是可以实现一个 SpringAiLlm,这样的话,内部全部用它就好了。只实现 chat 部分;这样,就把 flex 作为一个 agent 框架,内部调用,还是 spring ai 相关的。成本可能低一点?! case TONG_YI: QwenLlmConfig qwenLlmConfig = new QwenLlmConfig(); qwenLlmConfig.setApiKey(apiKey.getApiKey()); qwenLlmConfig.setModel(model.getModel()); + // TODO @lesan:这个有点奇怪。。。如果一个链式里,有多个模型,咋整呀。。。 tinyflow.setLlmProvider(id -> new QwenLlm(qwenLlmConfig)); break; + case OLLAMA: + OllamaLlmConfig ollamaLlmConfig = new OllamaLlmConfig(); + ollamaLlmConfig.setEndpoint(apiKey.getUrl()); + ollamaLlmConfig.setModel(model.getModel()); + tinyflow.setLlmProvider(id -> new OllamaLlm(ollamaLlmConfig)); + break; } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java index a7a477b655..ac16e8755e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java @@ -40,7 +40,10 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { @Override public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) { - validateWorkflowForCreateOrUpdate(null, createReqVO.getCode()); + // 1. 参数校验 + validateCodeUnique(null, createReqVO.getCode()); + + // 2. 插入工作流配置 AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class); workflowMapper.insert(workflow); return workflow.getId(); @@ -48,47 +51,33 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { @Override public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) { - validateWorkflowForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getCode()); + // 1. 参数校验 + validateWorkflowExists(updateReqVO.getId()); + validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); + + // 2. 更新工作流配置 AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class); workflowMapper.updateById(workflow); } @Override public void deleteWorkflow(Long id) { + // 1. 校验存在 validateWorkflowExists(id); + + // 2. 删除工作流配置 workflowMapper.deleteById(id); } - @Override - public AiWorkflowDO getWorkflow(Long id) { - return workflowMapper.selectById(id); - } - - @Override - public PageResult getWorkflowPage(AiWorkflowPageReqVO pageReqVO) { - return workflowMapper.selectPage(pageReqVO); - } - - @Override - public Object testWorkflow(AiWorkflowTestReqVO testReqVO) { - Map variables = testReqVO.getParams(); - Tinyflow tinyflow = parseFlowParam(testReqVO.getGraph()); - return tinyflow.toChain().executeForResult(variables); - } - - private void validateWorkflowForCreateOrUpdate(Long id, String code) { - validateWorkflowExists(id); - validateCodeUnique(id, code); - } - - private void validateWorkflowExists(Long id) { + private AiWorkflowDO validateWorkflowExists(Long id) { if (ObjUtil.isNull(id)) { - return; + throw exception(WORKFLOW_NOT_EXISTS); } AiWorkflowDO workflow = workflowMapper.selectById(id); if (ObjUtil.isNull(workflow)) { throw exception(WORKFLOW_NOT_EXISTS); } + return workflow; } private void validateCodeUnique(Long id, String code) { @@ -107,6 +96,30 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { } } + @Override + public AiWorkflowDO getWorkflow(Long id) { + return workflowMapper.selectById(id); + } + + @Override + public PageResult getWorkflowPage(AiWorkflowPageReqVO pageReqVO) { + return workflowMapper.selectPage(pageReqVO); + } + + @Override + public Object testWorkflow(AiWorkflowTestReqVO testReqVO) { + // 加载 graph + String graph = testReqVO.getGraph() != null ? testReqVO.getGraph() + : validateWorkflowExists(testReqVO.getId()).getGraph(); + + // 构建 TinyFlow 执行链 + Tinyflow tinyflow = parseFlowParam(graph); + + // 执行 + Map variables = testReqVO.getParams(); + return tinyflow.toChain().executeForResult(variables); + } + private Tinyflow parseFlowParam(String graph) { // TODO @lesan:可以使用 jackson 哇? JSONObject json = JSONObject.parseObject(graph);