Aller au contenu

Neuron Machine à états de session

Ce modèle est utile lorsque ce que vous construisez ressemble davantage à une session qu’à une exécution ponctuelle.

Les machines à états Queuety gèrent le cycle de vie de la session, les workflows Queuety effectuent les tâches de fond qui bénéficient des nouvelles tentatives, des modes d’achèvement for-each et de l’agrégation, tandis que Neuron gère les appels de l’agent et la mémoire des conversations.

Cette répartition convient bien aux assistants de support, aux assistants de recherche internes, aux agents éditoriaux pouvant être mis en pause pour une révision humaine et aux fils de discussion qui passent par des phases de planification, d’attente et de réponse.

Structure du cycle de vie

Dans cet exemple, une session de support passe par awaiting_user, planning, waiting_on_research et responding. La machine rend ce cycle de vie explicite, tandis qu’un workflow en arrière-plan rassemble des informations et signale à la session quand il est terminé.

La machine à états

PHP
use Queuety\Queuety;

$session_id = Queuety::machine( 'support_session' )
    ->version( 'support-session.v1' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( DispatchResearchWorkflowAction::class )
    ->on( 'research_dispatched', 'waiting_on_research' )
    ->state( 'waiting_on_research' )
    ->on( 'research_completed', 'responding' )
    ->state( 'responding' )
    ->action( RespondWithNeuronAction::class )
    ->on( 'response_ready', 'awaiting_user' )
    ->dispatch( [
        'thread_id' => 'support:customer-42',
    ] );

Lorsqu’un utilisateur envoie un message :

PHP
Queuety::machine_event(
    $session_id,
    'user_message',
    [
        'session_machine_id' => $session_id,
        'message' => 'Can you compare our pricing to the main competitors?',
    ]
);

La machine passe à planning, met son action en file d’attente et lance le workflow de recherche.

Action de planification : lancer le workflow

PHP
namespace App\StateMachines\Actions;

use Queuety\Contracts\StateAction;
use Queuety\Enums\WaitMode;
use Queuety\Queuety;

final class DispatchResearchWorkflowAction implements StateAction
{
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string
    {
        $message = trim( (string) ( $event_payload['message'] ?? '' ) );

        $agent_workflow = Queuety::workflow( 'support_research_agent' )
            ->then( ResearchTopicStep::class );

        $research_workflow = Queuety::workflow( 'support_research_packet' )
            ->then( PlanResearchTasksStep::class )
            ->start_agents( 'agent_tasks', $agent_workflow )
            ->wait_for_agents( mode: WaitMode::All, result_key: 'agent_results' )
            ->then( SynthesizeResearchPacketStep::class )
            ->then( NotifySessionResearchReadyStep::class );

        $workflow_id = $research_workflow->dispatch( [
            'machine_id'      => $state['session_machine_id'],
            'thread_id'       => $state['thread_id'],
            'customer_prompt' => $message,
        ] );

        return [
            'latest_user_message'  => $message,
            'research_workflow_id' => $workflow_id,
            '_event'               => 'research_dispatched',
        ];
    }
}

Cette action d’état ne tente pas d’effectuer elle-même la recherche. Elle lance un workflow et enregistre son ID dans l’état de la machine.

Étape du workflow : signaler à la machine que la recherche est prête

PHP
namespace App\Workflow\Steps;

use Queuety\Queuety;
use Queuety\Step;

final class NotifySessionResearchReadyStep implements Step
{
    public function handle( array $state ): array
    {
        Queuety::machine_event(
            $state['machine_id'],
            'research_completed',
            [
                'research_packet' => $state['research_packet'],
            ]
        );

        return [];
    }

    public function config(): array
    {
        return [];
    }
}

Lorsque le workflow se termine, il renvoie research_completed à la machine et inclut le dossier de synthèse dans la charge utile de l’événement.

Action de réponse : appeler Neuron avec la mémoire de session

PHP
namespace App\StateMachines\Actions;

use App\Neuron\SupportAgent;
use NeuronAI\Chat\Messages\UserMessage;
use Queuety\Contracts\StateAction;

final class RespondWithNeuronAction implements StateAction
{
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string
    {
        // Queuety creates actions with `new RespondWithNeuronAction()`, so build dependencies here.
        $pdo = new \PDO( $_ENV['NEURON_DB_DSN'], $_ENV['NEURON_DB_USER'], $_ENV['NEURON_DB_PASSWORD'] );

        $agent = new SupportAgent(
            threadId: $state['thread_id'],
            pdo: $pdo,
        );

        $message = $agent->chat(
            new UserMessage(
                sprintf(
                    "Answer the user message '%s' using this research packet:\n\n%s",
                    $state['latest_user_message'],
                    json_encode( $state['research_packet'] ?? [], JSON_PRETTY_PRINT )
                )
            )
        )->getMessage();

        return [
            'assistant_reply' => $message->getContent(),
            '_event'          => 'response_ready',
        ];
    }
}

Une fois la réponse générée, la machine revient à awaiting_user avec la réponse mise à jour dans l’état public. La même session peut alors accepter un autre événement user_message et continuer.

Pourquoi cette approche est plus claire qu’un grand workflow

Le travail principal relève toujours d’un workflow, car la recherche nécessite de la planification, des exécutions spécialisées, de l’agrégation et de la synthèse. La longue conversation de support relève d’une machine à états, car la session doit savoir si elle attend un message de l’utilisateur, les résultats de la recherche ou l’autorisation d’envoyer une réponse.

Cette répartition rend le code d’exécution plus lisible et le modèle de session plus explicite.