AI 联动-工作流

什么是工作流函数

工作流函数(FlowFunctionCalling)是基于 Flowable 工作流引擎的智能函数,允许用户通过自然语言描述自动发起工作流程。系统会自动提取流程参数、匹配流程定义并完成流程启动,实现办公流程的智能化操作。

应用场景

支持请假申请、报销审批、采购申请等各类工作流场景。用户只需用自然语言描述需求,如"明天帮我请一天假,好兄弟结婚",系统会自动提取参数并发起流程。

演示视频

业务场景说明

通过自然语言告诉大模型"帮我明天请一天假,明天家里有事",大模型会自动识别意图、提取流程参数(请假天数、开始日期、请假事由),并调用 Flowable 引擎发起请假流程。

自然语言交互示例

执行流程

工作流函数的执行流程包括用户输入、流程匹配、参数提取、权限校验和流程启动五个阶段:

graph LR
    A[用户输入自然语言] --> B[大模型提取参数]
    B --> C[获取用户可启动流程列表]
    C --> D{流程是否存在?}
    D -->|否| E[返回流程不存在错误]
    D -->|是| F{用户是否有权限?}
    F -->|否| G[返回权限不足错误]
    F -->|是| H[构建流程参数]
    H --> I[调用Flowable引擎启动]
    I --> J[返回流程启动结果]

配置工作流程

创建请假流程

进入【协同办公】>【流程管理】>【创建流程】,创建名称为"请假"的流程。

创建流程步骤1 创建流程步骤2

流程表单参数配置

参数ID必须匹配

表单参数的ID必须与代码中定义的参数名称完全一致,否则参数提取会失败。

参数名称参数ID说明
天数days请假天数,整数类型
开始日期startDate请假开始日期,格式 yyyy-MM-dd
事由reason请假原因,文本类型

使用AI助手发起流程

在AI对话界面中,使用自然语言描述请假需求,例如"明天帮我请一天假,好兄弟结婚"。系统会自动提取参数并发起流程。

AI助手发起流程示例

实现原理

FunctionCalling 接口说明

所有函数必须实现 FunctionCalling<T> 接口,该接口定义了函数调用的核心能力:

  • showFunction(): 是否在前端展示此函数
  • showInfo(): 前端显示的函数名称和描述
  • functionName(): 函数的唯一标识符
  • routePath(): 点击函数卡片后的跳转路径

接口定义与执行流程

public interface FunctionCalling<T extends BaseAiRequest> extends ToolExecutor {
    // 参数校验,子类必须实现
    R checkParams(T t, PigxUser userDetails, ChatMessageDTO.ExtDetails extDetails);

    // 业务处理,子类必须实现
    R<String> handle(T t, PigxUser userDetails, ChatMessageDTO.ExtDetails extDetails);

    // 构建工具规格,供大模型理解函数能力
    default ToolSpecification buildToolSpecification() {
        ToolSpecification.Builder builder = ToolSpecification.builder();
        builder.name(this.functionName());
        builder.description(this.functionDesc());
        Field[] fields = ReflectUtil.getFields(getGenericType());
        builder.parameters(ToolSpecificationsUtils.parametersFrom(fields));
        return builder.build();
    }

    // 预处理逻辑,自动完成上下文设置和参数校验
    default R preHandle(T t) {
        // 1. 验证messageKey
        // 2. 查询聊天记录
        // 3. 设置上下文(租户、用户、token)
        // 4. 调用checkParams校验参数
        // 5. 调用handle执行业务逻辑
    }
}
预处理机制

preHandle() 方法会自动处理上下文设置、用户认证、租户隔离等通用逻辑,子类只需实现 checkParams()handle() 两个业务方法。

工作流函数实现

@Component
@RequiredArgsConstructor
public class FlowFunctionCalling implements FunctionCalling<FlowRequest> {

    private final RemoteFlowApiFlowService flowService;

    // 点击函数卡片后跳转到已发起的流程列表
    @Override
    public String routePath() {
        return "/flow/task/started";
    }

    // 前端展示的函数说明
    @Override
    public String showInfo() {
        return "请假助手,能根据您的描述帮您发起请求流程调用flowable。比如:明天帮我请一天假,好兄弟结婚";
    }

    // 参数校验,当前示例直接返回成功
    @Override
    public R<String> checkParams(FlowRequest request, PigxUser userDetails,
                                  ChatMessageDTO.ExtDetails extDetails) {
        return R.ok();
    }

    // 业务处理:匹配流程并启动
    public R<String> handle(FlowRequest request, PigxUser user,
                            ChatMessageDTO.ExtDetails extDetails) {
        // 获取用户有权限启动的流程列表
        R<List<FormGroupVo>> listR = flowService.listCurrentUserStartGroup();
        if (Objects.isNull(listR.getData())) {
            return R.failed("创建失败,权限不足");
        }

        // 根据流程名称匹配流程ID
        String flowId = "";
        for (FormGroupVo formGroupVo : listR.getData()) {
            for (FormGroupVo.FlowVo item : formGroupVo.getItems()) {
                if (StrUtil.containsAnyIgnoreCase(request.getFlowName(), item.getName())) {
                    flowId = item.getFlowId();
                }
            }
        }

        if (StrUtil.isBlank(flowId)) {
            return R.failed("创建失败,流程不存在");
        }

        // 构建流程启动参数
        ProcessInstanceParamDto processInstanceParamDto = new ProcessInstanceParamDto();
        processInstanceParamDto.setFlowId(flowId);
        processInstanceParamDto.setStartUserId(String.valueOf(user.getId()));

        Map<String, Object> paramMap = new HashMap<>();
        Dict rootUser = Dict.create()
            .set("id", processInstanceParamDto.getStartUserId())
            .set("name", user.getUsername())
            .set("type", NodeUserTypeEnum.USER.getKey());
        paramMap.put("root", CollUtil.newArrayList(rootUser));
        paramMap.put("days", request.getDays());
        paramMap.put("reason", request.getReason());
        paramMap.put("startDate", request.getStartDate());
        processInstanceParamDto.setParamMap(paramMap);

        // 调用Flowable引擎启动流程
        flowService.startProcessInstance(processInstanceParamDto);
        return R.ok(StrUtil.format("流程发起成功,流程名称:{}", request.getFlowName()));
    }
}

请求参数定义

@Data
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonClassDescription("流程信息")
class FlowRequest extends BaseAiRequest {
    @FieldPrompt("流程名称:请假流程")
    private String flowName;

    @FieldPrompt("原因:请假原因")
    private String reason;

    @FieldPrompt("天数:请假天数")
    private Integer days;

    @FieldPrompt("开始日期:请假开始日期,格式为 yyyy-MM-dd")
    private String startDate;
}
注解说明

@FieldPrompt 注解用于提示大模型该字段的含义和格式要求,直接影响参数提取的准确性。描述要清晰明确,必要时提供示例格式。

关键技术点

流程匹配机制

系统通过模糊匹配的方式查找流程:用户输入的流程名称只要包含在实际流程名称中即可匹配成功。例如用户说"请假",可以匹配到"员工请假流程"。

if (StrUtil.containsAnyIgnoreCase(request.getFlowName(), item.getName())) {
    flowId = item.getFlowId();
}
匹配精度

如果存在多个名称相似的流程(如"请假流程"和"事假流程"),可能会匹配到第一个符合条件的流程。建议流程命名具有明显区分度。

权限控制

函数会自动获取当前用户有权限启动的流程列表,确保用户只能发起授权范围内的流程。

参数映射

大模型提取的参数会自动映射到 FlowRequest 对象,并通过 paramMap 传递给 Flowable 引擎。参数名称必须与流程表单定义保持一致。