# pi-dynamic-workflows

> 来源：[GitHub](https://github.com/Michaelliv/pi-dynamic-workflows) · ⭐ 573 stars

```markdown
# pi-dynamic-workflows：用 TypeScript 重新定义动态工作流编排

## 项目简介

在现代后端系统中，工作流编排早已不是新鲜概念。从 Airflow 的 DAG 到 Temporal 的状态机，开发者们习惯了先定义流程、再执行代码的开发模式。但这种方式存在一个根本性矛盾：**业务需求的变动速度远超代码部署的频率**。当市场团队需要一个紧急的审批流程变更时，开发团队却要走完代码修改、测试、发布的完整周期，这种滞后性在快节奏的业务场景中愈发刺眼。

[pi-dynamic-workflows](https://github.com/Michaelliv/pi-dynamic-workflows) 正是瞄准这一痛点而生。这个基于 TypeScript 的轻量级框架（573 Stars）提出了一种"运行时动态编排"的范式——工作流的节点、边、条件分支乃至执行逻辑，都可以在运行时通过配置或 API 动态注入，无需重启服务即可生效。它的核心价值在于将"流程定义"从编译期解耦到运行期，让业务人员能够像搭积木一样实时调整工作流，而开发者只需专注于原子化任务节点的实现。

## 核心特性

- **完全动态的工作流拓扑**：节点与边的关系不在代码中硬编码，而是通过运行时配置构建，支持实时增删改查工作流结构，变更秒级生效。

- **类型安全的节点契约**：尽管强调动态性，框架仍借助 TypeScript 的类型系统约束节点输入输出接口，在编译期捕获类型错误，避免运行时因数据格式不匹配导致的流程崩溃。

- **内置状态持久化与恢复**：工作流执行状态自动持久化到存储后端（默认支持 Redis/PostgreSQL），节点故障后支持断点续传，满足长流程业务的可靠性要求。

- **嵌套子工作流与条件网关**：支持将复杂流程拆分为可复用的子工作流模块，同时提供基于表达式的条件网关（如 `$.amount > 1000`），实现真正的动态分支决策。

- **轻量级嵌入设计**：核心无外部强依赖，可作为库嵌入现有 Express/Fastify/NestJS 应用，而非强制引入一套独立的运行时平台。

## 技术实现

pi-dynamic-workflows 的架构设计体现了"动态性"与"工程严谨"的巧妙平衡。其底层采用**有向无环图（DAG）的惰性求值模型**：工作流在启动时仅做拓扑合法性校验（检测环路与孤立节点），真正的节点调度发生在运行时。这种设计避免了传统工作流引擎"预编译全图"的开销，也为动态修改留下空间。

框架的核心抽象是 `NodeExecutor` 接口与 `WorkflowEngine` 调度器。每个任务节点需实现统一的执行契约：

```typescript
interface NodeExecutor<TInput, TOutput> {
  execute(ctx: ExecutionContext<TInput>): Promise<TOutput>;
  // 声明输入输出 Schema，用于运行时类型校验
  schema: {
    input: ZodSchema<TInput>;
    output: ZodSchema<TOutput>;
  };
}
```

值得注意的是，框架选择 **Zod** 而非 JSON Schema 作为运行时校验工具，这一决策极具 TypeScript 生态特色。Zod 的 Schema 定义本身就是类型安全的，编译期类型与运行期校验天然同构，避免了"类型定义写一套、校验规则再写一套"的重复劳动。

调度层采用**事件驱动的状态机模型**。`WorkflowEngine` 维护每个工作流实例的 `ExecutionState`，节点完成后触发下游节点的调度事件。状态持久化通过可插拔的 `StateStore` 接口实现，默认的 Redis 实现利用 Lua 脚本保证状态变更的原子性，防止分布式场景下的竞态条件。对于需要人工审批的暂停节点，框架实现了基于 **Promise 的异步唤醒机制**——工作流实例在内存中挂起等待外部信号，超时后自动回滚或告警，而非占用线程轮询。

动态修改的安全性通过**版本隔离**保障：工作流定义变更时生成新版本号，正在运行的实例仍按旧版本执行，新实例采用最新定义，避免"中途换引擎"导致的状态不一致。

## 快速上手

以下示例展示如何动态注册一个简单的工作流并触发执行：

```typescript
import { WorkflowEngine, InMemoryStateStore } from 'pi-dynamic-workflows';
import { z } from 'zod';

// 1. 初始化引擎
const engine = new WorkflowEngine({
  stateStore: new InMemoryStateStore(), // 生产环境换为 RedisStore
});

// 2. 定义原子任务节点
const validateOrder = {
  id: 'validate',
  execute: async (ctx) => {
    const { userId, amount } = ctx.input;
    if (amount <= 0) throw new Error('Invalid amount');
    return { valid: true, riskScore: amount > 10000 ? 'high' : 'low' };
  },
  schema: {
    input: z.object({ userId: z.string(), amount: z.number() }),
    output: z.object({ valid: z.boolean(), riskScore: z.enum(['low', 'high']) }),
  },
};

const processPayment = {
  id: 'payment',
  execute: async (ctx) => {
    console.log(`Processing payment for ${ctx.input.userId}`);
    return { transactionId: crypto.randomUUID() };
  },
  schema: {
    input: z.object({ userId: z.string(), riskScore: z.string() }),
    output: z.object({ transactionId: z.string() }),
  },
};

// 3. 动态注册工作流（可在运行时通过 API 调用）
await engine.registerWorkflow({
  id: 'order-flow',
  nodes: [validateOrder, processPayment],
  edges: [
    { from: 'start', to: 'validate' },
    { from: 'validate', to: 'payment', condition: '$.riskScore !== "high"' },
    { from: 'validate', to: 'manual-review', condition: '$.riskScore === "high"' },
  ],
});

// 4. 触发执行
const result = await engine.start('order-flow', {
  userId: 'user_123',
  amount: 500,
});
```

## 应用场景

**电商平台的促销规则引擎**：大促期间，满减规则、叠加策略、库存预占逻辑可能每小时调整。运营团队通过后台直接修改工作流配置，无需发布代码即可上线新玩法，活动结束后再回滚至常规流程。

**SaaS 产品的客户定制化审批**：不同企业客户的审批层级、抄送规则、会签/或签策略各异。传统做法是为每个客户维护分支代码，而 pi-dynamic-workflows 允许将审批模板存储为租户级别的配置，同一套服务支撑千企千面。

**AI Agent 的链式调用编排**：在 LLM 应用中，ReAct、CoT 等推理模式的步骤组合经常需要实验调优。开发者可动态调整工具调用顺序、反思循环次数，甚至 A/B 测试不同的链式策略，快速验证效果。

## 总结

pi-dynamic-workflows 的价值不在于替代 Airflow、Temporal 等重型编排系统，而是填补了一个长期被忽视的细分市场：**需要高度动态性、又不愿牺牲类型安全与工程规范的中小型工作流场景**。它特别适合以下人群：正在构建多租户 SaaS 需要租户级流程定制的全栈开发者、探索 LLM Agent 编排模式的 AI 工程师、以及希望将业务规则配置化以减少发布频率的技术团队。573 Stars 的成绩说明其定位已获社区认可，若能在可视化设计器、执行历史追踪等方向持续迭代，有望成为 TypeScript 生态中动态工作流的首选方案。
```