Przejdź do treści

Neuron Maszyna stanów sesji

Ten wzorzec sprawdza się, gdy tworzony system bardziej przypomina sesję niż jednorazowe uruchomienie.

Maszyny stanów Queuety zarządzają cyklem życia sesji, przepływy pracy Queuety wykonują zadania w tle, które korzystają z ponawiania prób, trybów zakończenia for-each i agregacji, a Neuron obsługuje właściwe wywołania agenta i pamięć konwersacji.

Ten podział sprawdza się w przypadku asystentów wsparcia, wewnętrznych asystentów badawczych, agentów redakcyjnych, którzy mogą czekać na weryfikację przez człowieka, oraz wątków czatu przechodzących między etapami planowania, oczekiwania i odpowiedzi.

Przebieg cyklu życia

W tym przykładzie sesja wsparcia przechodzi przez stany awaiting_user, planning, waiting_on_research i responding. Maszyna jawnie zarządza tym cyklem życia, a przepływ pracy w tle gromadzi materiały badawcze i sygnalizuje sesji zakończenie zadania.

Maszyna stanów

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

Gdy użytkownik wyśle wiadomość:

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

Maszyna przechodzi do planning, kolejkuje swoją akcję i uruchamia przepływ badawczy.

Akcja planowania: uruchom przepływ pracy

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',
        ];
    }
}

Ta akcja stanu nie próbuje samodzielnie przeprowadzać badań. Uruchamia przepływ pracy i zapisuje jego ID w stanie maszyny.

Krok przepływu pracy: powiadom maszynę, gdy badania będą gotowe

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 [];
    }
}

Po zakończeniu przepływ pracy wysyła research_completed z powrotem do maszyny i dołącza przygotowany pakiet jako dane zdarzenia.

Akcja odpowiadania: wywołaj Neuron z pamięcią sesji

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',
        ];
    }
}

Po wygenerowaniu odpowiedzi maszyna przechodzi z powrotem do awaiting_user, zapisując zaktualizowaną odpowiedź w stanie publicznym. Ta sama sesja może wtedy przyjąć kolejne zdarzenie user_message i kontynuować.

Dlaczego to rozwiązanie jest lepsze niż jeden duży przepływ pracy

Złożone zadania nadal realizuje przepływ pracy, ponieważ część badawcza wymaga planowania, uruchamiania wyspecjalizowanych agentów, agregacji i syntezy. Długotrwałą rozmowę wsparcia lepiej obsługuje maszyna stanów, ponieważ sesja musi śledzić, czy czeka na wiadomość użytkownika, wyniki badań, czy może już wysłać odpowiedź.

Ten podział ułatwia zrozumienie działania środowiska uruchomieniowego i jawnie określa model sesji.