![]()
更新日志:
-
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;
}
}