Saltar al contenido

Máquinas de estados

Las máquinas de estados y los flujos de trabajo resuelven problemas distintos.

Un flujo de trabajo responde a «¿qué tarea se ejecuta a continuación?». Una máquina de estados responde a «¿en qué estado se encuentra este elemento y qué eventos son válidos en este punto?». Esta diferencia importa cuando lo que está creando no es solo una secuencia de pasos, sino una entidad de larga duración que reacciona a entradas externas a lo largo del tiempo, como una sesión de agente, un hilo de chat, un ciclo de revisión, un ticket o un flujo de aprobación.

Ejemplo rápido

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' ]
);

Esta máquina comienza en awaiting_user, pasa a planning cuando llega un mensaje del usuario, permite que la acción en cola emita planned y luego espera en awaiting_review hasta que una aprobación externa complete la sesión. Lo importante no es solo la sintaxis del código, sino que el ciclo de vida sea explícito, inspeccionable y orientado a eventos.

Cómo se ejecuta una máquina

Cuando se envía una instancia de máquina, Queuety conserva el nombre del estado actual, la carga útil pública del estado, la versión y el hash de la definición, los eventos entrantes válidos para el estado actual y una cronología duradera de eventos.

Si el estado al que se entra tiene un action(), Queuety pone en cola un trabajo de entrada al estado. Esa acción puede limitarse a actualizar el estado público y dejar la máquina a la espera del siguiente evento, o puede emitir _event y, opcionalmente, _event_payload para activar de inmediato la siguiente transición. Si el estado no tiene ninguna acción ni un estado terminal, la máquina simplemente espera eventos externos enviados con Queuety::machine_event().

Cómo elegir entre flujos de trabajo y máquinas de estados

Use un flujo de trabajo cuando la ejecución se base fundamentalmente en pasos: obtener datos, esperar por cada tarea, sintetizar y terminar. Este modelo encaja bien cuando hay un inicio y un final claros, y la pregunta principal es qué tarea debe ejecutarse a continuación.

Use una máquina de estados cuando el comportamiento válido dependa de la fase actual del ciclo de vida. Es la opción más adecuada cuando los eventos pueden llegar en distinto orden, la entidad puede existir durante mucho tiempo o necesita inspeccionar explícitamente el ciclo de vida mediante estados como awaiting_user, planning, running_agents, awaiting_review y completed.

Cómo encajan ambas opciones

Las dos primitivas funcionan mejor juntas que por separado. Una acción de estado puede enviar un flujo de trabajo cuando una transición requiere una ejecución más intensa, y un flujo de trabajo puede emitir un evento a una máquina cuando termina esa tarea. En la práctica, esto permite que la máquina de estados controle el ciclo de vida mientras los flujos de trabajo realizan las ejecuciones costosas o muy estructuradas.

Estructura del constructor

StateMachineBuilder se mantiene deliberadamente pequeño. initial() y state() definen el grafo del ciclo de vida. action() vincula el trabajo en cola a un estado. on() declara qué eventos pueden llevar la máquina al siguiente estado. on_queue(), with_priority() y max_attempts() controlan cómo se ejecutan las acciones de estado, mientras que version() y idempotency_key() añaden metadatos de envío duraderos. dispatch() crea la instancia de la máquina.

Las acciones de estado también pueden exponer un método opcional config(), que usa las mismas claves de política de recursos que los pasos y controladores de flujos de trabajo. Esto permite mantener una transición de estado costosa dentro de un grupo de concurrencia compartido o asignarle un peso de coste mayor cuando una acción consume más presupuesto que un paso normal.

El modelo general de recursos se describe en Gestión de recursos.

Contratos

Las acciones de entrada a un estado implementan 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 es la carga útil estructurada de la definición de la acción; está vacío cuando la acción se indica como un nombre de clase simple. Queuety crea acciones con new $class(), por lo que una clase de acción necesita un constructor sin argumentos obligatorios.

Las acciones de estado también pueden definir un método opcional config() con concurrency_group, concurrency_limit y cost_units cuando la acción de entrada en cola deba participar en la admisión de trabajadores, los presupuestos ponderados de colas o proveedores y otras políticas de recursos.

Las condiciones de transición implementan Queuety\Contracts\StateGuard:

PHP
namespace Queuety\Contracts;

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

API de inspección

Use la fachada para inspeccionar las máquinas en ejecución:

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 );

Las máquinas de estados no tienen comandos WP-CLI: wp queuety workflow ... solo cubre los flujos de trabajo. Use estas API PHP, por ejemplo mediante wp eval, para inspeccionar máquinas desde la línea de comandos.

Estas API exponen el nombre del estado actual, el estado del ciclo de vida, el estado público, los eventos disponibles, los metadatos de la definición, la cronología completa de transiciones y un paquete de trazas normalizado para las interfaces de depuración. Las filas de la lista de máquinas incluyen la versión y el hash de la definición, además de la clave de idempotencia, para que las pantallas de lista muestren la versión de origen y de ejecución sin cargar cada máquina por separado. El paquete de trazas incluye estados agrupados, eventos sin procesar, trabajos de acción correlacionados, registros, referencias a artefactos, referencias a fragmentos, entrada y salida del controlador, estado anterior y posterior, contexto, tiempos y errores estructurados.

Sesiones de agente

Las máquinas de estados son especialmente útiles para sistemas de agentes que se comportan más como sesiones que como flujos de procesamiento. Una sesión que pasa por awaiting_user, planning, running_agents, awaiting_review y completed suele ser más fácil de entender como un ciclo de vida explícito que como una definición de flujo de trabajo muy larga con muchas esperas y retornos a pasos anteriores.

Véase también