Neuron 세션 상태 머신
만드는 대상이 일회성 실행보다 세션에 가까운 방식으로 동작할 때 유용한 패턴입니다.
Queuety 상태 머신은 세션 수명 주기를 담당하고, Queuety 워크플로는 재시도, for-each 완료 모드 및 집계가 필요한 백그라운드 작업을 수행하며, Neuron는 실제 에이전트 호출과 대화 메모리를 처리합니다.
이 구조는 고객 지원 코파일럿, 사내 조사 보조 도구, 사람의 검토를 위해 일시 중지될 수 있는 편집 에이전트, 그리고 계획·대기·응답 단계 사이를 오가는 채팅 스레드에 적합합니다.
수명 주기 구조
이 예제에서는 하나의 지원 세션이 awaiting_user, planning, waiting_on_research 및 responding을 거칩니다. 상태 머신은 이 수명 주기를 명시적으로 관리하고, 백그라운드 워크플로는 조사 자료를 모은 뒤 완료되면 세션에 알립니다.
상태 머신
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',
] );
사용자가 메시지를 보내면:
Queuety::machine_event(
$session_id,
'user_message',
[
'session_machine_id' => $session_id,
'message' => 'Can you compare our pricing to the main competitors?',
]
);
머신은 planning으로 전환하고 작업을 대기열에 넣은 다음 조사 워크플로를 시작합니다.
계획 작업: 워크플로 디스패치
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',
];
}
}
이 상태 작업은 직접 조사하지 않습니다. 워크플로를 디스패치하고 워크플로 ID를 머신 상태에 기록합니다.
워크플로 단계: 조사가 준비되면 머신에 알리기
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 [];
}
}
워크플로가 완료되면 research_completed을 머신에 보내고, 종합한 자료를 이벤트 페이로드로 포함합니다.
응답 작업: 세션 메모리와 함께 Neuron 호출
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',
];
}
}
응답이 생성되면 머신은 공개 상태에 업데이트된 답변을 담아 awaiting_user으로 돌아갑니다. 그러면 같은 세션에서 user_message 이벤트를 다시 받아 계속 진행할 수 있습니다.
큰 워크플로 하나보다 이 방식이 깔끔한 이유
조사에는 계획, 전문 작업 실행, 집계 및 종합이 필요하므로 핵심 작업은 여전히 워크플로 형태가 적합합니다. 장기 실행 지원 대화는 사용자의 다음 입력을 기다리는 중인지, 조사 결과를 기다리는 중인지, 응답을 내보내도 되는지 추적해야 하므로 상태 머신 형태가 적합합니다.
이 분리로 런타임을 이해하기 쉽고 세션 모델도 명확하게 유지할 수 있습니다.