Pular para o conteúdo

Neuron Máquina de estados da sessão

Esse padrão é útil quando o que você está criando se comporta mais como uma sessão do que como uma execução isolada.

Queuety máquinas de estados cuidam do ciclo de vida da sessão, Queuety fluxos de trabalho executam as tarefas em segundo plano que se beneficiam de novas tentativas, modos de conclusão para cada elemento e agregação, e Neuron cuida das chamadas reais ao agente e da memória da conversa.

Essa divisão funciona bem para copilotos de suporte, assistentes internos de pesquisa, agentes editoriais que podem pausar para revisão humana e conversas que passam pelas etapas de planejamento, espera e resposta.

Estrutura do ciclo de vida

Neste exemplo, uma sessão de suporte passa por awaiting_user, planning, waiting_on_research e responding. A máquina mantém esse ciclo de vida explícito, enquanto um fluxo de trabalho em segundo plano reúne informações de pesquisa e avisa a sessão quando termina.

A máquina de estados

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

Quando um usuário envia uma mensagem:

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

A máquina passa para planning, enfileira sua ação e inicia o fluxo de trabalho de pesquisa.

Ação de planejamento: iniciar o fluxo de trabalho

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

Essa ação de estado não tenta fazer a pesquisa por conta própria. Ela inicia um fluxo de trabalho e registra o ID do fluxo no estado da máquina.

Etapa do fluxo de trabalho: avisar a máquina quando a pesquisa estiver pronta

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

Quando o fluxo de trabalho termina, ele envia research_completed de volta à máquina e inclui o pacote sintetizado como payload do evento.

Ação de resposta: chamar Neuron com a memória da sessão

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

Depois que a resposta é gerada, a máquina volta para awaiting_user com a resposta atualizada no estado público. A mesma sessão pode então aceitar outro evento user_message e continuar.

Por que isso é mais simples do que um único fluxo de trabalho grande

As tarefas mais pesadas ainda seguem o formato de fluxo de trabalho, pois a pesquisa exige planejamento, execuções especializadas, agregação e síntese. A conversa de suporte duradoura segue o formato de máquina de estados, pois a sessão precisa acompanhar se está aguardando uma mensagem do usuário, a pesquisa ou a possibilidade de enviar uma resposta.

Essa divisão mantém o ambiente de execução legível e o modelo da sessão explícito.