
# 异步交接

当工作流需要将发现的工作交给独立的顶层工作流，而不是将所有内容保留在单次运行中时，使用 `start_workflows()`。

如果你明确在建模智能体运行，`start_agents()` 和 `wait_for_agents()` 是语义别名，并采用面向智能体的默认值。

如需更完整的端到端示例，请参阅[智能体编排](https://queuety.inline0.dev/zh/docs/workflows/agent-orchestration)。

这适用于智能体式编排：

- 规划器工作流在运行时发现任务
- 每项任务都成为自己的持久化工作流
- 父工作流将这些工作流 ID 存储在状态中
- 父工作流之后可以等待 `all`、`any`、`quorum`，或等待一个已命名的已启动组

## `->start_workflows()` 方法

```php
use Queuety\Enums\WaitMode;
use Queuety\Queuety;

$agent_task = Queuety::workflow( 'agent_task' )
    ->then( ResearchTopicStep::class )
    ->then( SummarizeTopicStep::class );

Queuety::workflow( 'brief_research' )
    ->then( PlanTopicsStep::class )
    ->start_workflows(
        items_key: 'topics',
        workflow_builder: $agent_task,
        result_key: 'child_workflow_ids',
        group_key: 'researchers',
    )
    ->wait_for_workflow_group( 'researchers', WaitMode::All, result_key: 'child_results' )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

在该示例中：

1. `PlanTopicsStep` 将主题负载数组写入 `$state['topics']`
2. `start_workflows()` 为每个主题分派一个 `agent_task` 工作流
3. 将已启动工作流的 ID 写入 `$state['child_workflow_ids']`
4. `wait_for_workflow_group()` 暂停，直到该已启动组满足所选完成条件
5. `SynthesizeBriefStep` 在 `$state['child_results']` 下接收已完成的子工作流状态

## 子负载

`items_key` 中的每个项目都会成为一个已启动工作流的初始负载。

如果项目是数组，其公开键会合并到子负载中：

```php
[
    [ 'topic' => 'pricing', 'source' => 'competitors' ],
    [ 'topic' => 'reviews', 'source' => 'customers' ],
]
```

如果项目是标量，Queuety 会将其存储在 `payload_key` 下：

```php
->start_workflows(
    items_key: 'topic_names',
    workflow_builder: $agent_task,
    payload_key: 'topic',
)
```

## 继承父工作流状态

默认情况下，`start_workflows()` 会将父工作流的公开状态合并到每个子负载中。

这意味着 `brief_id`、`account_id` 或 `campaign` 等共享上下文会自动在每个已启动工作流中可用。

源 `items_key` 和 `result_key` 会从继承内容中排除，以免每个子工作流都收到完整任务列表或最终工作流 ID 数组。

当每个子工作流只应接收自己的项目负载时，设置 `inherit_state: false`。

## 为什么不用 `run_workflow()`？

`run_workflow()` 会建立父子关系，父工作流会停在该步骤，直到子工作流完成。

`start_workflows()` 的耦合较松：

- 已启动的工作流是独立的顶层工作流
- 每个已启动的工作流都可以单独检查、重试或导出
- 父工作流之后可决定是否等待它们，以及如何等待

因此，`start_workflows()` 更适合异步智能体任务交接，而 `run_workflow()` 更适合在单个编排树中进行嵌套组合。

## 之后再等待

`start_workflows()` 专为与[工作流依赖项](https://queuety.inline0.dev/zh/docs/workflows/dependencies)配合使用而设计：

```php
use Queuety\Enums\WaitMode;

Queuety::workflow( 'review_board' )
    ->start_workflows( 'review_tasks', $review_task_workflow, 'review_workflow_ids' )
    ->wait_for_workflows( 'review_workflow_ids', WaitMode::Any, 'winner' )
    ->then( FinalizeDecisionStep::class )
    ->dispatch( $payload );
```

- `WaitMode::All` 收集所有已完成的子工作流状态
- `WaitMode::Any` 在第一个成功的子工作流完成时恢复

这样就能实现规划器/执行器编排，而无需完整的运行时可编辑 DAG 引擎。

## 适合智能体的别名

`start_agents()` 是 `start_workflows()` 的别名，采用适合规划器/执行器流程的默认值：

- `result_key` 默认值为 `agent_workflow_ids`
- `payload_key` 默认值为 `agent_task`
- `name` 默认值为 `start_agents`

`wait_for_agents()` 是 `wait_for_workflows()` 的别名，采用适合收集智能体结果的默认值：

- `workflows` 默认值为 `agent_workflow_ids`
- `result_key` 默认值为 `agent_results`
- `name` 默认值为 `wait_for_agents`

`wait_for_agent_group()` 是 `wait_for_workflow_group()` 的别名，采用适合智能体批次的默认值。

```php
use Queuety\Enums\WaitMode;

$agent_run = Queuety::workflow( 'agent_run' )
    ->then( ResearchTopicStep::class )
    ->then( SummarizeTopicStep::class );

Queuety::workflow( 'brief_research' )
    ->then( PlanTopicsStep::class )
    ->start_agents( 'agent_tasks', $agent_run, group_key: 'researchers' )
    ->wait_for_agent_group( 'researchers', WaitMode::Quorum, 2 )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```
