
# 状态机

状态机和工作流解决的是不同的问题。

工作流回答“接下来运行什么任务？”，状态机回答“这个对象处于什么状态？从当前状态可以接收哪些事件？”。当你构建的对象不只是按顺序执行一系列步骤，而是会随着时间推移响应外部输入、长期存在时，这一区别就很重要，例如代理会话、聊天线程、审核生命周期、工单或审批流程。

## 快速示例

```php
use Queuety\Enums\StateMachineStatus;
use Queuety\Queuety;

$machine_id = Queuety::machine( 'agent_session' )
    ->version( 'agent-session.v1' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'awaiting_review' )
    ->state( 'awaiting_review' )
    ->on( 'approve', 'completed', ReviewApprovedGuard::class )
    ->on( 'reject', 'awaiting_user' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );

Queuety::machine_event(
    $machine_id,
    'user_message',
    [ 'message' => 'Find competitors for this product' ]
);

Queuety::machine_event(
    $machine_id,
    'approve',
    [ 'approved' => true, 'reviewer' => 'editor@example.com' ]
);
```

该状态机从 `awaiting_user` 开始，用户消息到达后转入 `planning`，由排队的操作发出 `planned`，随后停留在 `awaiting_review`，直到外部审批完成会话。关键不在于代码语法本身，而在于生命周期是明确的、可检查的，并由事件驱动。

## 状态机如何运行

派发状态机实例时，Queuety会持久化当前状态名称、公共状态载荷、定义版本和哈希、当前状态允许接收的事件，以及持久化事件时间线。

如果进入的状态定义了 `action()`，Queuety就会将一个状态进入任务加入队列。该操作可以只更新公共状态，让状态机等待下一个事件；也可以发出 `_event`，并可选择发出 `_event_payload`，以立即推进到下一个状态。如果该状态没有操作且不是终止状态，状态机就会等待通过 `Queuety::machine_event()` 发送的外部事件。

## 如何选择工作流或状态机

如果运行过程本质上是以步骤为中心的，就使用工作流：获取数据、逐项处理、等待、综合处理、完成。当流程有明确的开始和结束，主要问题是接下来应执行什么任务时，这种模型很合适。

如果有效行为取决于当前生命周期阶段，就使用状态机。当事件可能以不同顺序到达、对象可能长期存在，或者你需要明确检查生命周期并查看 `awaiting_user`、`planning`、`running_agents`、`awaiting_review` 和 `completed` 等状态时，状态机更合适。

## 两者如何配合

这两种基础机制相辅相成，配合使用效果更好。状态操作可以在转换需要大量执行工作时派发工作流；工作流也可以在任务完成后向状态机发送事件。在实际应用中，状态机负责管理生命周期，而工作流负责执行开销较大或结构复杂的任务。

## 构建器结构

`StateMachineBuilder`刻意保持精简。`initial()`和`state()`定义生命周期图。`action()`将排队任务关联到一个状态。`on()`声明哪些事件可以将状态机转入下一个状态。`on_queue()`、`with_priority()`和`max_attempts()`控制状态操作的执行方式，而`version()`和`idempotency_key()`附加持久化派发元数据。`dispatch()`会创建状态机实例。

状态操作还可以提供可选的 `config()` 方法，并使用与工作流步骤和处理器相同的资源策略键。这样一来，你可以将开销较大的状态转换放在共享并发组中，或者在操作消耗的预算高于普通步骤时，为其设置更高的成本权重。

更完整的资源模型请参见 [资源管理](https://queuety.inline0.dev/zh/docs/features/resource-management)。

## 接口约定

状态进入操作需要实现 `Queuety\Contracts\StateAction`：

```php
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
```

`$payload`是操作定义中的结构化载荷；如果操作以普通类名的形式提供，则其值为空。Queuety会通过 `new $class()` 创建操作，因此操作类的构造函数不能包含必需参数。

状态操作还可以定义可选的 `config()` 方法，并提供 `concurrency_group`、`concurrency_limit` 和 `cost_units`，使排队的状态进入操作能够参与工作进程准入、加权队列或服务商预算，以及其他资源策略。

转换守卫需要实现 `Queuety\Contracts\StateGuard`：

```php
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
```

## 检查 API

使用 facade 检查正在运行的状态机：

```php
$status   = Queuety::machine_status( $machine_id );
$machines = Queuety::list_machines();
$events   = Queuety::machine_timeline( $machine_id );
$trace    = Queuety::machine_trace( $machine_id );
$state    = Queuety::machine_state_at( $machine_id, $event_id );
```

状态机没有 WP-CLI 命令：`wp queuety workflow ...`仅涵盖工作流。请使用这些 PHP API（例如通过 `wp eval`），从命令行检查状态机。

这些 API 会提供当前状态名称、生命周期状态、公共状态、可用事件、定义元数据、完整的转换时间线，以及供调试器 UI 使用的标准化跟踪数据包。状态机列表行包含定义版本、定义哈希和幂等键，因此列表页面无需逐个加载状态机，也能显示源代码/运行时版本。跟踪数据包包含分组后的状态、原始事件、关联的操作任务、日志、构件引用、分块引用、处理器输入/输出、转换前后的状态、上下文、计时信息和结构化错误。

## 代理会话

对于行为更像会话而非流水线的代理系统，状态机尤其有用。依次经过 `awaiting_user`、`planning`、`running_agents`、`awaiting_review` 和 `completed` 的会话，通常用明确的生命周期来描述，更容易理解；若将其写成一个包含大量等待和回跳的超长工作流定义，则不易理解。

## 另请参阅

- [Neuron会话状态机](https://queuety.inline0.dev/zh/docs/examples/neuron-session-state-machine)
- [Neuron记忆与审核状态机](https://queuety.inline0.dev/zh/docs/examples/neuron-memory-review)
- [代理编排](https://queuety.inline0.dev/zh/docs/workflows/agent-orchestration)
- [工作流信号](https://queuety.inline0.dev/zh/docs/workflows/signals)
- [工作流依赖](https://queuety.inline0.dev/zh/docs/workflows/dependencies)
- [重复执行](https://queuety.inline0.dev/zh/docs/workflows/repeats)
- [构件](https://queuety.inline0.dev/zh/docs/features/artifacts)
- [PHP API](https://queuety.inline0.dev/zh/docs/api)
