
# 工作流分叉

Queuety 可以将正在运行的工作流分叉为完全独立的副本。分叉后的工作流会从与原工作流相同的步骤开始，并具有相同的状态，但从这时起两个工作流会独立推进。对其中一个工作流所做的更改不会影响另一个。

## 分叉工作流

### PHP API

使用工作流 ID 调用 `Queuety::fork_workflow()`。它会返回新的工作流 ID：

```php
use Queuety\Queuety;

$forked_id = Queuety::fork_workflow( $workflow_id );
```

### CLI

```bash
wp queuety workflow fork <id>
```

| 选项 | 说明 |
|---|---|
| `<id>` | 要分叉的工作流 ID（必填） |

```bash
wp queuety workflow fork 42
# Success: Workflow #42 forked. New workflow ID: 43.
```

## 工作原理

分叉工作流时：

1. 原工作流记录会被锁定并读取
2. 插入一条新的工作流记录，其中包含相同的状态、步骤定义和当前步骤
3. 新工作流的名称为 `{original_name}_fork_{timestamp}`
4. 将当前步骤加入分叉工作流的队列
5. 在原工作流和分叉工作流上都记录一条 `workflow_forked` 日志事件
6. 原工作流照常继续处理

分叉后的工作流完全独立。它有自己的工作流 ID、状态副本和作业队列条目。从分叉点开始，每个工作流都可以自由走向不同的状态。

## 使用场景

### 对步骤处理器进行 A/B 测试

分叉一个工作流，以测试即将执行的步骤的两种不同实现：

```php
// Original workflow will use the existing CallLLMHandler (GPT-4).
// Fork it to test with a different handler.
$forked_id = Queuety::fork_workflow( $workflow_id );

// Modify the forked workflow's state to use a different model.
// (Update the state via a custom step or direct DB update.)
```

### 假设分析

在决策点分叉工作流，以探索不同分支：

```php
// Workflow is at step 3, about to process data.
// Fork it so we can try two different processing strategies.
$fork_a = Queuety::fork_workflow( $workflow_id );
$fork_b = Queuety::fork_workflow( $workflow_id );
```

每个分叉都会使用相同的输入状态继续运行，但可以修改为走不同的路径。

### 并行试验

当工作流到达适合并行尝试多种方案的位置时，可以为每种方案创建一个分叉，并在所有分叉完成后比较结果。

## 跟踪分叉

每个工作流上的日志事件都包含对另一个工作流的引用：

- 在原工作流上：`context.forked_workflow_id` 指向新工作流
- 在分叉工作流上：`context.forked_from_workflow_id` 指向原工作流

查询日志以查找某个工作流的所有分叉：

```php
$logs = Queuety::logger()->for_workflow( $workflow_id );

foreach ( $logs as $entry ) {
    if ( 'workflow_forked' === $entry['event'] ) {
        $context = json_decode( $entry['context'] ?? '{}', true );
        // $context['forked_workflow_id'] is the new workflow
    }
}
```

## 限制

- 分叉后的工作流不会继承原工作流的事件日志历史记录，而是从空白日志开始。
- 不会复制运行工作流之间的关系。如果原工作流有一个正在运行的运行工作流，分叉不会获得它的副本。
- 分叉是当前状态的快照。原工作流的任何正在执行中的作业都不会被复制。
