
# 代理编排

当你希望代理系统能够自适应执行任务，同时保留持久化状态、重试、检查能力和明确的等待语义时，Queuety就很适合。

对于大多数智能体流程，你不需要可在运行时编辑的 DAG 引擎。实际上，大多数系统都可以用一小组持久化编排操作来建模。`for_each()` 在单个工作流运行中扩展工作。`start_agents()` 和 `wait_for_agents()` 将运行时发现的工作交给独立的顶层工作流。`wait_for_decision()` 和 `wait_for_input()` 暂停以等待人工控制。`wait_for_workflow()`、`wait_for_workflows()` 和 `wait_for_agent_group()` 让一个顶层运行等待另一个运行，或等待已启动的一组工作流达到指定数量。

## 一个实用的思维模型

分层思考智能体编排最容易理解。规划器决定有哪些工作。Queuety 将这些工作转化为持久化任务或工作流。专业执行运行产出结果并存储制品。监督阶段汇总这些结果，并决定下一步做什么。在任何时候，人工都可以在运行继续之前批准、拒绝或纠正它。

重要区别在于，智能体决定要做什么工作，而 Queuety 仍决定如何持久化、重试、恢复和汇总这些工作。当编排对象是长期会话而不是有界运行时，可搭配[状态机](https://queuety.inline0.dev/zh/docs/state-machines)使用。

## 模式 1：规划器/执行器与独立的智能体运行

当规划步骤在运行时发现任务，且每个任务都应成为独立的顶层工作流时，使用 `start_agents()`。

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

$agent_run = Queuety::workflow( 'agent_run' )
    ->then( FetchSourcesStep::class )
    ->then( DraftFindingStep::class );

Queuety::workflow( 'brief_research' )
    ->version( 'brief-research.v1' )
    ->idempotency_key( 'brief:42:research' )
    ->max_transitions( 20 )
    ->then( PlanResearchTasksStep::class )
    ->start_agents( 'agent_tasks', $agent_run, group_key: 'researchers' )
    ->wait_for_agent_group( 'researchers', WaitMode::Quorum, 2, 'agent_results' )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

在此模式中，`PlanResearchTasksStep` 将运行时发现的项目写入 `$state['agent_tasks']`，`start_agents()` 为每项任务分派一个持久化工作流，而 `wait_for_agent_group()` 会暂停父工作流，直到满足所选完成条件。随后，`SynthesizeBriefStep` 会在 `$state['agent_results']` 下接收已完成的子工作流状态。

这是规划器/执行器系统最简洁的起点，因为每次智能体运行都可以单独检查和重试。

## 模式 2：运行继续前进行人工审核

当工作流应停下来等待明确的“是”或“否”决定时，使用 `wait_for_decision()`；当操作员需要将结构化反馈发送回运行中时，使用 `wait_for_input()`。

```php
use Queuety\Queuety;

$workflow_id = Queuety::workflow( 'brief_review' )
    ->then( DraftBriefStep::class )
    ->wait_for_decision( result_key: 'review' )
    ->then( HandleReviewDecisionStep::class )
    ->wait_for_input( result_key: 'revision_notes' )
    ->then( ApplyRevisionNotesStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

如果审核者批准：

```php
Queuety::approve_workflow(
    $workflow_id,
    [
        'reviewer' => 'editor@example.com',
        'notes' => 'Looks good, publish after legal check',
    ],
    'approved'
);
```

如果审核者拒绝：

```php
Queuety::reject_workflow(
    $workflow_id,
    [
        'reviewer' => 'editor@example.com',
        'reason' => 'Needs citations and a stronger conclusion',
    ],
    'rejected'
);
```

在 `wait_for_decision()` 之后，工作流会收到一个结构化负载，其中包含最终结果、恢复运行的信号，以及随附的任何审核者数据。这样就能实现真正的人工参与检查点，同时无需在运行等待期间保持 PHP 进程处于打开状态。

## 模式 3：一个顶层工作流等待另一个工作流

有时，最简洁的建模方式不是创建一个庞大的工作流。一个工作流可以完成研究，另一个工作流则可以等待它完成后再开始下一阶段。

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

$research_id = Queuety::workflow( 'research_brief' )
    ->then( PlanResearchTasksStep::class )
    ->start_agents( 'agent_tasks', $agent_run )
    ->wait_for_agents( mode: WaitMode::All, result_key: 'agent_results' )
    ->then( SynthesizeBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );

Queuety::workflow( 'publish_brief' )
    ->wait_for_workflow( $research_id, 'research' )
    ->wait_for_decision( result_key: 'editor_review' )
    ->then( PublishBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

这样就在研究阶段和发布阶段之间建立了持久化边界。每次运行都有自己的运维生命周期，但第二个工作流仍会在依赖项完成后自动恢复。

## 选择合适的基本操作

当工作属于单个工作流运行，并且分支实际上只是在共享状态集合上并行执行步骤时，`for_each()` 是合适的工具。

当每个发现的任务都应成为独立的持久化顶层运行，并且你希望分别检查、重试或导出这些运行时，`start_agents()` 更合适。

当一个顶层工作流依赖另一个顶层工作流，并且你希望在这些编排层之间实现持久化异步协调时，`wait_for_workflow()` 和 `wait_for_workflows()` 最合适。

当工作流暂停期间需要人工批准、拒绝或引导运行，且不占用工作进程容量时，应在相应位置使用 `wait_for_decision()` 和 `wait_for_input()`。

## 建议的状态约定

状态键保持可预测，智能体系统就更容易调试。实际上，`agent_tasks` 适合存放规划器输出，`agent_workflow_ids` 适合存放已启动的顶层运行，`agent_results` 适合存放已完成的子工作流状态，而 `review` 或 `decision` 适合存放批准或拒绝负载。草稿、引文或研究清单等较大的输出通常应存放在制品存储中，而不是主工作流的状态集合里。

## 智能体运行的建议护栏

智能体工作流本质上具有动态性，因此比几乎任何其他用例都更需要护栏。

```php
Queuety::workflow( 'agent_research' )
    ->version( 'agent-research.v2' )
    ->idempotency_key( 'brief:42:agent-research' )
    ->max_transitions( 20 )
    ->max_for_each_items( 12 )
    ->max_state_bytes( 32768 );
```

这些控制措施回答了生产环境中最实际的问题：哪个定义版本生成了某次运行、重复的外部请求是否会导致重复分派、规划器是否会创建过多分支，以及工作流状态是否会无限增长。对于大多数智能体系统，应先设置这些护栏，再调整工作进程数量或队列拓扑。

## 另请参阅

- [示例](https://queuety.inline0.dev/zh/docs/examples)
- [操作触发器](https://queuety.inline0.dev/zh/docs/features/action-triggers)
- [动态逐项处理](https://queuety.inline0.dev/zh/docs/workflows/for-each)
- [异步交接](https://queuety.inline0.dev/zh/docs/workflows/async-handoffs)
- [工作流依赖项](https://queuety.inline0.dev/zh/docs/workflows/dependencies)
- [制品](https://queuety.inline0.dev/zh/docs/features/artifacts)
- [工作流信号](https://queuety.inline0.dev/zh/docs/workflows/signals)
- [工作流护栏](https://queuety.inline0.dev/zh/docs/workflows/guardrails)
