.NET 开源工作流:Slickflow 流程自动化运行技术指南

概述

Slickflow.NET 作为基于 .NET 的开源工作流引擎,除了支持传统的人工审批流程(用户任务、会签、加签等),还提供了流程自动化运行能力。与需要人工介入的审批流程不同,自动化运行流程在启动后可按预定义顺序自动执行所有节点,直至流程结束,无需人工参与。

本文面向 Slickflow 引擎开发人员,重点讲解:

  1. 流程自动化运行的底层逻辑与架构(相对于人工审批流程的差异)
  2. 代码方式定义流程:使用 Workflow 类在内存中构建 BPMN 流程
  3. 代码方式运行流程:使用 WorkflowExecutorUseProcess(workflow) 执行
  4. 两个完整测试用例:LocalMethod 订单金额计算流程、AI 多轮问答智能客服流程

一、流程自动化运行 vs 人工审批流程

1.1 核心差异

维度 人工审批流程 流程自动化运行
执行模式 每执行一个节点后暂停,等待人工操作 启动后自动顺序执行所有节点,直到结束
节点类型 用户任务(UserTask)为主,需要签收、办理 服务任务(ServiceTask)、AI 任务、脚本任务等自动节点
持久化 依赖数据库存储流程实例、活动实例、任务 可选用纯内存执行,不落库
上下文 ActivityForwardContext、IDbSession AutoExecutionContext、内存变量字典
典型场景 请假审批、报销审批、合同会签 数据处理流水线、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 XML
WorkflowExecutorExtensions.UseProcess(workflow) 接收 Workflow 实例,调用 BuildInMemory() 生成 ProcessEntity,并按 ProcessId:Version 进行内存缓存,无需数据库
ServiceTaskDelegateRegistry LocalMethod 委托注册表,按 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 节点
    .ServiceTask<MyService>("保存", "Save001")                 // LocalService 服务类
    .End("End");
  • new Workflow(string name, string code)name 为流程名称,code 为流程编码;内部会自动生成 ProcessId = "process_xxx" 与默认版本号 Version = "1"
  • Start("Start"):开始事件
  • ServiceTask(name, code, delegateKey):绑定 LocalMethod,delegateKey 对应委托注册表中的 key
  • RagService(name, code):RAG 增强的 AI 服务任务
  • LlmService(name, code):通用大模型服务任务节点(如 DeepSeek、通义千问等)
  • ServiceTask<TService>(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:VersionProcessEntity 缓存到内存中,不产生数据库写入,适合单元测试、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")
    .ServiceTask<ContactExtractService>("提取联系方式", "Extract001")
    .ServiceTask<ContactSaveService>("保存客户", "Save001")
    .ServiceTask<ConversationService>("保存对话记录", "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_response RAG 节点写回 大模型生成的回复
customer ContactExtractService 写回 提取的联系方式 JSON
customer_id, session_id, industry_id 调用方 AddVariable 业务上下文

七、LocalMethod 与 AI 节点技术要点

7.1 LocalMethod 执行链路

  1. WorkflowExecutor.Run 推进到 ServiceTask 节点
  2. WorkflowActivityExecutor 识别 ServiceDetail.Method == LocalMethod
  3. ServiceDetail.Expression 取出 delegateKey(如 "CalcAmount")
  4. ServiceTaskDelegateRegistry 按 key 解析委托
  5. 调用 ExecuteLocalMethodInExecutor 执行委托,传入 IEventService 上下文
  6. 若委托返回 ServiceTaskResult,按其中指定的变量名写回;否则写回默认变量

7.2 ServiceTaskResult 自定义输出变量

// 默认写回 ServiceResult
return total;

// 自定义变量名
return ServiceTaskResult.WithVariable("Var_OrderTotal", total);

7.3 AI/RAG 节点执行链路

  1. 引擎识别活动类型为 AIService / RagService
  2. ai_activity_config 或扩展属性读取 AI 配置(SystemPrompt、Temperature 等)
  3. context.Variables 构建 prompt(如 user_message、历史对话)
  4. 调用大模型 API(OpenAI、DeepSeek、通义千问等)
  5. 将返回内容写入 ai_response 或配置的输出变量
  6. 通过 SetNotifyClient 可实时推送流式输出到前端

八、开发建议

8.1 单元测试

  • 使用 UseProcess(workflow) 避免数据库依赖
  • 使用 ServiceTaskDelegateRegistryWithDelegateRegistry(customRegistry) 注入 Mock 委托
  • 断言 result.Statusresult.Variables 中的关键变量

8.2 多租户与隔离

  • 为不同租户创建独立的 ServiceTaskDelegateRegistry,通过 WithDelegateRegistry 注入
  • 流程变量中传递 tenantId,在 LocalMethod 内按租户路由

8.3 与人工审批流程的混合

同一流程中可混用自动节点与用户任务:自动节点执行完毕后引擎自动推进;遇到用户任务时流程暂停,等待人工办理后再调用 Run 继续。


九、总结

Slickflow 流程自动化运行通过以下机制实现:

  1. 代码定义流程Workflow 类支持 ServiceTask、RagService 等自动节点,BuildInMemory() 生成内存 ProcessEntity
  2. 代码运行流程WorkflowExecutor.UseProcess(workflow) 直接使用内存流程,无需数据库
  3. LocalMethod:通过 ServiceTaskDelegateRegistry 注册本地方法,实现快速原型与嵌入式场景
  4. AI 节点:RagService 结合向量检索与大模型,实现多轮智能客服

本文所述两个测试用例(订单金额计算、AI 智能客服)可直接在 Slickflow.ConsoleTest 或自建控制台项目中运行,供引擎开发人员参考与扩展。


参考资料

posted on 2026-03-03 16:31  slickflowteam  阅读(155)  评论(0)    收藏  举报