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 [返回流程启动结果]
配置工作流程
创建请假流程
进入【协同办公】>【流程管理】>【创建流程】,创建名称为"请假"的流程。
流程表单参数配置
⚠ 参数ID必须匹配
表单参数的ID必须与代码中定义的参数名称完全一致,否则参数提取会失败。
参数名称 参数ID 说明 天数 days 请假天数,整数类型 开始日期 startDate 请假开始日期,格式 yyyy-MM-dd 事由 reason 请假原因,文本类型
使用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 引擎。参数名称必须与流程表单定义保持一致。