🔥Star 6.2k 支持 AI 无代码工作流引擎 FlowLong 1.2.3 发布
开源地址:https://gitee.com/aizuda/flowlong 开源地址:https://github.com/aizuda/flowlong 官网文档:https://flowlong.aizuda.com 更新日志: opt: Spring SPEL 兼容错误配置 opt: 优化支持删除存在的流程定义及历史版本相关数据 opt: 优化认领任务创建人时间修改为认领人认领时间 opt: 优化驳回策略使用枚举消除魔法值 opt: 优化执行完任务数据传输对象线程变量释放 opt: 优化完善的AI审批逻辑 opt: 优化完善获取下一步执行节点逻辑 fixed: 修复 maven 模块名冲突 fixed: 修复用户多角色或签认领其它参与者未处理问题 fixed: 修复撤回到发起人唤醒任务未创建问题 AI审批 AI智能体根据参数配置等信息,智能路由决策,智能辅助审批。 核心思想:从“规则驱动”到“语义理解驱动” 传统工作流引擎:依赖于预定义的、结构化的规则。例如:“如果报销金额 < 1000元,则自动通过;否则,转交部门经理审批。” 它高效、准确,但僵化,无法处理规则之外的例外情况或需要复杂判断的场景。 智能审批(结合大模型):引入大模型的自然语言理解、上下文分析、知识推理和内容生成能力。它可以理解审批内容(如合同条款、报销事由、项目报告)的语义,而不仅仅是结构化的数据字段。 @Slf4j public class TestAiHandler implements FlowAiHandler { // 创建一个 AI 智能体处理用户 public static final FlowCreator aiUser = new FlowCreator("1", "AI 智能体"); @Override public AiResponse execute(FlowLongContext flowLongContext, Execution execution, NodeModel nodeModel) { AiConfig aiConfig = nodeModel.getAiConfig(); // 1. 构建 Prompt 提示词 if (null != aiConfig) { System.out.println("AI Prompt Template: " + aiConfig.getPromptTemplate()); } System.out.println("handle 根据 callAi 识别处理具体逻辑:" + nodeModel.getCallAi()); // 2. 调用 AI 服务(此处为示例实现,实际需对接真实 AI API) try { // TODO: 对接实际的 AI 服务,如 OpenAI、Claude、文心一言等 // AiServiceResponse response = aiService.chat(prompt, aiConfig.getModelParams()); // 返回一个模拟的成功响应,包含决策、建议、置信度和指标 return AiResponse.builder() .status(AiStatus.SUCCESS) .decision("flk17631709068381") .advice("AI 审批建议:经分析,该申请符合相关规定,建议通过。") .confidence(0.95) .metrics(AiResponse.AiMetrics.builder() .modelName("default-model") .promptTokens(100L) .completionTokens(50L) .totalTimeMs(500L) .build()) .build(); } catch (Exception e) { log.error("AI processing failed: {}", e.getMessage(), e); return AiResponse.failure("AI 服务调用失败: " + e.getMessage()); } } /** * 处理 AI 响应结果 */ @Override public boolean processAiResponse(FlowLongContext flowLongContext, Execution execution, NodeModel nodeModel, AiResponse aiResponse) { if (null == aiResponse) { return this.handleAiFallback(execution, nodeModel, "AI 处理器返回空响应"); } AiStatus status = aiResponse.getStatus(); // 1. 处理异步情况 if (aiResponse.isAsync()) { // 异步模式:流程挂起,等待回调 return true; } // 获取 AI 配置 AiConfig aiConfig = nodeModel.getAiConfig(); // 2. 合并 AI 提取的变量到执行参数 this.mergeAiVariables(execution, aiResponse, aiConfig); // 3. 检查置信度 double confidenceThreshold = 0.8; if (null != aiConfig) { confidenceThreshold = aiConfig.getConfidenceThresholdOrDefault(); } if (aiResponse.getConfidenceOrDefault() < confidenceThreshold) { return this.handleAiFallback(execution, nodeModel, "AI 置信度低: " + aiResponse.getConfidenceOrDefault() + ", 建议: " + aiResponse.getAdvice()); } // 4. 根据状态处理 switch (status) { case SUCCESS: return this.handleAiSuccess(execution, nodeModel, aiResponse); case LOW_CONFIDENCE: case FALLBACK: case FAILURE: case TIMEOUT: return this.handleAiFallback(execution, nodeModel, aiResponse.getErrorMessage()); default: return true; } } /** * AI 处理成功 */ protected boolean handleAiSuccess(Execution execution, NodeModel nodeModel, AiResponse aiResponse) { // 根据决策结果进行自动审批 List<FlwTask> flwTasks = execution.getFlwTasks(); for (FlwTask ft : flwTasks) { // 记录 AI 审批意见相关数据到任务变量(用于历史记录) Map<String, Object> args = execution.getArgs(); if (null != args) { // 审批意见 if (null != aiResponse.getAdvice()) { args.put("_ai_advice", aiResponse.getAdvice()); args.put("_ai_decision", aiResponse.getDecision()); args.put("_ai_confidence", aiResponse.getConfidenceOrDefault()); } // 记录指标数据 if (null != aiResponse.getMetrics()) { AiResponse.AiMetrics metrics = aiResponse.getMetrics(); args.put("_ai_model", metrics.getModelName()); args.put("_ai_tokens", metrics.getTotalTokens()); args.put("_ai_time_ms", metrics.getTotalTimeMs()); } } // AI 建议通过,自动完成任务 if (aiResponse.isPass()) { execution.getEngine().autoCompleteTask(ft.getId(), args, aiUser); } // AI 建议拒绝,自动拒绝任务 if (aiResponse.isReject()) { execution.getEngine().autoRejectTask(ft, args, aiUser); } } // 其他决策结果,不自动处理,等待人工介入 return true; } /** * AI 降级处理 */ protected boolean handleAiFallback(Execution execution, NodeModel nodeModel, String reason) { AiConfig aiConfig = nodeModel.getAiConfig(); if (null == aiConfig) { return true; } String fallbackStrategy = aiConfig.getFallbackStrategyOrDefault(); if (AiFallbackStrategy.MANUAL.eq(fallbackStrategy)) { return true; } // 记录降级原因 Map<String, Object> args = execution.getArgs(); if (null != args) { args.put("_ai_fallback", true); args.put("_ai_fallback_reason", reason); } List<FlwTask> flwTasks = execution.getFlwTasks(); for (FlwTask ft : flwTasks) { if (AiFallbackStrategy.DEFAULT_PASS.eq(fallbackStrategy)) { // 默认通过 execution.getEngine().autoCompleteTask(ft.getId(), args, aiUser); } else if (AiFallbackStrategy.DEFAULT_REJECT.eq(fallbackStrategy)) { // 默认拒绝 execution.getEngine().autoRejectTask(ft, args, aiUser); } } return true; } @Override public String decideRoute(FlowLongContext flowLongContext, Execution execution, NodeModel nodeModel, Map<String, Object> args) { // 默认实现:返回 null,表示不由 AI 决定路由 System.out.println("AI Decision: " + args.get("content")); // 这里模拟决策返回 审批 A 所在分支 return "flk17631709068381"; } @Override public List<String> decideInclusiveRoutes(FlowLongContext flowLongContext, Execution execution, NodeModel nodeModel, Map<String, Object> args) { // 默认实现:返回 null,表示不由 AI 决定包容分支 return null; } @Override public boolean onAsyncComplete(FlowLongContext flowLongContext, String asyncToken, AiResponse aiResponse) { // 异步回调处理 log.info("AI async complete, token={}, status={}", asyncToken, aiResponse.getStatus()); // TODO: 根据 asyncToken 找到对应的任务,并恢复流程执行 // 1. 根据 asyncToken 查询挂起的任务 // 2. 根据 aiResponse 结果决定是自动完成还是转人工 // 3. 恢复流程执行 return true; } }