Slickflow.NET 作为基于 .NET 的开源工作流引擎除了支持传统的人工审批流程用户任务、会签、加签等还提供了流程自动化运行能力。与需要人工介入的审批流程不同自动化运行流程在启动后可按预定义顺序自动执行所有节点直至流程结束无需人工参与。本文面向 Slickflow 引擎开发人员重点讲解流程自动化运行的底层逻辑与架构相对于人工审批流程的差异代码方式定义流程使用Workflow类在内存中构建 BPMN 流程代码方式运行流程使用WorkflowExecutor及UseProcess(workflow)执行两个完整测试用例LocalMethod 订单金额计算流程、AI 多轮问答智能客服流程一、流程自动化运行 vs 人工审批流程1.1 核心差异维度人工审批流程流程自动化运行执行模式每执行一个节点后暂停等待人工操作启动后自动顺序执行所有节点直到结束节点类型用户任务UserTask为主需要签收、办理服务任务ServiceTask、AI 任务、脚本任务等自动节点持久化依赖数据库存储流程实例、活动实例、任务可选用纯内存执行不落库上下文ActivityForwardContext、IDbSessionAutoExecutionContext、内存变量字典典型场景请假审批、报销审批、合同会签数据处理流水线、AI 对话编排、ETL 流程1.2 自动化运行的底层逻辑自动化运行流程的核心是引擎在启动流程后循环执行“获取下一可执行活动 → 执行活动 → 推进流程”直到没有可执行活动流程结束或达到步数上限。简化伪代码启动流程 - 创建流程实例 while (存在可执行活动) { 获取下一可执行活动列表 foreach (活动 in 活动列表) 执行活动调用 Service / AI / Script 执行器 推进流程Run- 更新当前节点、流转到下一节点 } 返回执行结果与人工审批流程的区别在于人工审批执行完一个用户任务后流程停在待办需调用WorkflowService.Run(runner)时传入下一节点办理人或由用户在前端签收后再继续。自动化运行服务任务、AI 任务、脚本任务执行完毕后引擎自动推进到下一节点无需人工介入。二、架构设计2.1 整体架构┌─────────────────────────────────────────────────────────────────┐ │ Workflow流程定义层 │ │ Slickflow.Graph.Model.Workflow │ │ .Start() .ServiceTask() .RagService() .End() │ └───────────────────────────┬─────────────────────────────────────┘ │ BuildInMemory() ▼ ┌─────────────────────────────────────────────────────────────────┐ │ ProcessEntity内存流程实体 │ │ ProcessXmlBuilder 序列化为 BPMN XML不写入数据库 │ └───────────────────────────┬─────────────────────────────────────┘ │ UseProcess(workflow) ▼ ┌─────────────────────────────────────────────────────────────────┐ │ WorkflowExecutor执行器 │ │ UseApp / UseProcess / AddVariable / Run │ └───────────────────────────┬─────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ ServiceTask │ │ RagService │ │ LocalService │ │ (LocalMethod) │ │ (AI/RAG) │ │ (服务类) │ │ 委托注册表解析 │ │ 大模型调用 │ │ 反射实例化 │ └───────────────┘ └───────────────┘ └───────────────┘2.2 关键组件组件职责Workflow代码定义流程结构支持 Start、Task、ServiceTask、RagService、LlmService、Agent、Parallels、Branch、End 等ProcessXmlBuilder将 Workflow 图结构序列化为 BPMN 2.0 XMLWorkflowExecutorExtensions.UseProcess(workflow)接收 Workflow 实例调用 BuildInMemory() 生成 ProcessEntity并按ProcessId:Version进行内存缓存无需数据库ServiceTaskDelegateRegistryLocalMethod 委托注册表按 key 解析并执行本地方法WorkflowActivityExecutor根据 ServiceDetail.Method 分发LocalMethod / LocalService / WebAPI 等AutoExecutionContext内存执行上下文Variables 字典存储输入输出变量三、代码方式定义流程3.1 基本语法using Slickflow.Graph.Model; var wf new Workflow(ProcessName, ProcessCode); wf.Start(Start) .ServiceTask(服务节点名, ActivityCode, DelegateKey) // LocalMethod .RagService(RAG 回答, RAG001) // RAG 节点 .LlmService(LLM 智能节点, LLM001) // 通用 LLM 节点 .ServiceTaskMyService(保存, Save001) // LocalService 服务类 .End(End);new Workflow(string name, string code)name为流程名称code为流程编码内部会自动生成ProcessId process_xxx与默认版本号Version 1。Start(Start)开始事件ServiceTask(name, code, delegateKey)绑定 LocalMethoddelegateKey 对应委托注册表中的 keyRagService(name, code)RAG 增强的 AI 服务任务LlmService(name, code)通用大模型服务任务节点如 DeepSeek、通义千问等ServiceTaskTService(name, code)绑定本地服务类End(End)结束事件3.2 并行与分支Parallels / Branch最新的Workflow支持更简洁的并行与分支语法例如// 简单并行开始 - 并行三个任务 - 汇聚 - 结束 wf.Start(Start) .AndSplit(并行网关) .Parallels( (任务A, TaskA), (任务B, TaskB), (任务C, TaskC)) .AndJoin(并行汇聚) .End(End);复杂分支可以通过Branch Lambda 定义分支内部节点wf.Start(Start) .Split(条件网关) .Branch( () wf.Task(条件1处理, Cond1), () wf.Task(条件2处理, Cond2)) .End(End);3.3 Build() 与 BuildInMemory()方法作用数据库wf.Build()序列化流程并插入wf_process 表写入wf.BuildInMemory()仅生成内存中的 ProcessEntity不涉及使用UseProcess(workflow)时内部调用BuildInMemory()并按ProcessId:Version将ProcessEntity缓存到内存中不产生数据库写入适合单元测试、Demo、嵌入式场景同一进程内多次调用会复用缓存的流程定义。四、代码方式运行流程4.1 执行入口using Slickflow.Engine.Executor; using Slickflow.Engine.Core.Result; var wfe new WorkflowExecutor(); var result await wfe .UseApp(App-001, AppName, AppCode) .UseProcess(wf) // 直接传入 Workflow 实例 .AddVariable(key, value) .Run(); if (result.Status WfExecutedStatus.Success) Console.WriteLine(result.Message);4.2 参数传递输入AddVariable(key, value)传入流程变量输出服务节点 / AI 节点通过context.Variables[VarName] result写回LocalMethod通过IEventService.GetVariable读取、SaveVariable或返回值写入五、测试用例一LocalMethod 订单金额计算流程5.1 流程说明流程开始 → 校验订单 → 计算金额 → 通知结果 → 结束。其中「校验订单」「计算金额」「通知结果」均使用LocalMethod绑定本地方法。5.2 定义本地方法并注册using Slickflow.Engine.Executor; using Slickflow.Engine.Event; using Slickflow.Engine.Common; // 校验订单检查必填变量 void ValidateOrder(IEventService ctx) { var orderId ctx.GetVariable(ProcessVariableScopeEnum.Process, OrderId)?.ToString(); var quantity ctx.GetVariable(ProcessVariableScopeEnum.Process, Quantity)?.ToString(); if (string.IsNullOrEmpty(orderId) || string.IsNullOrEmpty(quantity)) throw new InvalidOperationException(OrderId and Quantity are required.); } // 计算金额单价 * 数量返回 ServiceTaskResult 指定输出变量名 object CalcAmount(IEventService ctx) { var price decimal.Parse(ctx.GetVariable(ProcessVariableScopeEnum.Process, UnitPrice)?.ToString() ?? 0); var qty int.Parse(ctx.GetVariable(ProcessVariableScopeEnum.Process, Quantity)?.ToString() ?? 0); var total price * qty; return ServiceTaskResult.WithVariable(Var_OrderTotal, total); } // 通知结果打印到控制台 void NotifyResult(IEventService ctx) { var total ctx.GetVariable(ProcessVariableScopeEnum.Process, Var_OrderTotal); Console.WriteLine($[NotifyResult] 订单总金额: {total}); } // 注册到全局委托表 ServiceTaskDelegateRegistry.Global.Register(ValidateOrder, ValidateOrder); ServiceTaskDelegateRegistry.Global.Register(CalcAmount, CalcAmount); ServiceTaskDelegateRegistry.Global.Register(NotifyResult, NotifyResult);5.3 定义流程using Slickflow.Graph.Model; var wf new Workflow(OrderCalcProcess, OrderCalcProcess_Code); wf.Start(Start) .ServiceTask(校验订单, Validate001, ValidateOrder) .ServiceTask(计算金额, Calc001, CalcAmount) .ServiceTask(通知结果, Notify001, NotifyResult) .End(End);5.4 执行并打印结果var result await new WorkflowExecutor() .UseApp(OrderApp-001, OrderApp) .UseProcess(wf) .AddVariable(OrderId, ORD-2025-001) .AddVariable(Quantity, 3) .AddVariable(UnitPrice, 99.50) .Run(); Console.WriteLine($Status: {result.Status}); Console.WriteLine($Message: {result.Message}); // 从返回结果中获取输出变量若引擎支持 var vars result.Variables; // 或通过 result 的扩展属性获取 if (vars ! null vars.TryGetValue(Var_OrderTotal, out var total)) Console.WriteLine($OrderTotal: {total});5.5 输出示例[NotifyResult] 订单总金额: 298.50 Status: Success Message: 流程执行成功 OrderTotal: 298.50六、测试用例二AI 多轮问答智能客服流程6.1 流程说明流程开始 → RAG 智能回复 → 提取联系方式 → 保存客户 → 保存对话 → 结束。其中 RAG 节点调用大模型实现多轮问答后续节点处理结构化数据。6.2 定义流程using Slickflow.Graph.Model; using Slickflow.Module.External.Customer; var wf new Workflow(ChatAppProcess, ChatAppProcess_Code); wf.Start(Start) .RagService(RAG 智能回复, RAG001) .ServiceTaskContactExtractService(提取联系方式, Extract001) .ServiceTaskContactSaveService(保存客户, Save001) .ServiceTaskConversationService(保存对话记录, Msg001) .End(End);6.3 执行流程var messageId Guid.NewGuid().ToString(N); var sessionId sess-001; var userMessage 你好我想了解一下你们的产品我叫张三手机 13812345678; var result await new WorkflowExecutor() .UseApp($ChatApp-{messageId}-{sessionId}, ChatAppMessage) .UseProcess(wf) .AddVariable(user_message, userMessage) .AddVariable(customer_id, cust-xxx) .AddVariable(session_id, sessionId) .AddVariable(industry_id, 1) .SetNotifyClient(sessionId, (sid, data) Console.WriteLine($[Notify] {sid}: {data})) .Run(); // RAG 节点会将 AI 回复写入 ai_response var aiResponse result.AiResponse; Console.WriteLine($AI 回复: {aiResponse});6.4 数据流说明变量来源用途user_message调用方 AddVariable用户输入RAG 与 ConversationService 使用ai_responseRAG 节点写回大模型生成的回复customerContactExtractService 写回提取的联系方式 JSONcustomer_id, session_id, industry_id调用方 AddVariable业务上下文七、LocalMethod 与 AI 节点技术要点7.1 LocalMethod 执行链路WorkflowExecutor.Run推进到 ServiceTask 节点WorkflowActivityExecutor识别ServiceDetail.Method LocalMethod从ServiceDetail.Expression取出 delegateKey如 CalcAmountServiceTaskDelegateRegistry按 key 解析委托调用ExecuteLocalMethodInExecutor执行委托传入IEventService上下文若委托返回ServiceTaskResult按其中指定的变量名写回否则写回默认变量7.2 ServiceTaskResult 自定义输出变量// 默认写回 ServiceResult return total; // 自定义变量名 return ServiceTaskResult.WithVariable(Var_OrderTotal, total);7.3 AI/RAG 节点执行链路引擎识别活动类型为 AIService / RagService从ai_activity_config或扩展属性读取 AI 配置SystemPrompt、Temperature 等用context.Variables构建 prompt如 user_message、历史对话调用大模型 APIOpenAI、DeepSeek、通义千问等将返回内容写入ai_response或配置的输出变量通过SetNotifyClient可实时推送流式输出到前端八、开发建议8.1 单元测试使用UseProcess(workflow)避免数据库依赖使用ServiceTaskDelegateRegistry或WithDelegateRegistry(customRegistry)注入 Mock 委托断言result.Status与result.Variables中的关键变量8.2 多租户与隔离为不同租户创建独立的ServiceTaskDelegateRegistry通过WithDelegateRegistry注入流程变量中传递 tenantId在 LocalMethod 内按租户路由8.3 与人工审批流程的混合同一流程中可混用自动节点与用户任务自动节点执行完毕后引擎自动推进遇到用户任务时流程暂停等待人工办理后再调用Run继续。九、总结Slickflow 流程自动化运行通过以下机制实现代码定义流程Workflow类支持 ServiceTask、RagService 等自动节点BuildInMemory()生成内存 ProcessEntity代码运行流程WorkflowExecutor.UseProcess(workflow)直接使用内存流程无需数据库LocalMethod通过ServiceTaskDelegateRegistry注册本地方法实现快速原型与嵌入式场景AI 节点RagService 结合向量检索与大模型实现多轮智能客服