Neuron-Sitzungs-Zustandsmaschine
Dieses Muster eignet sich, wenn sich das, was du entwickelst, eher wie eine Sitzung als wie ein einmaliger Lauf verhält.
Queuety-Zustandsmaschinen verwalten den Sitzungslebenszyklus, Queuety-Workflows erledigen Hintergrundaufgaben, die von Wiederholungen, Modi zur Verarbeitung jedes Elements und zum Abschluss sowie von Zusammenfassungen profitieren, und Neuron übernimmt die eigentlichen Agentenaufrufe und den Gesprächsspeicher.
Diese Aufteilung eignet sich gut für Support-Copiloten, interne Rechercheassistenten, redaktionelle Agenten, die für eine menschliche Prüfung pausieren können, und Chat-Threads, die zwischen Planungs-, Warte- und Antwortphasen wechseln.
Ablauf des Lebenszyklus
In diesem Beispiel durchläuft eine Support-Sitzung awaiting_user, planning, waiting_on_research und responding. Die Maschine bildet diesen Lebenszyklus explizit ab, während ein Hintergrund-Workflow Recherchen sammelt und die Sitzung benachrichtigt, sobald alles bereit ist.
Die Zustandsmaschine
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',
] );
Wenn ein Benutzer eine Nachricht sendet:
Queuety::machine_event(
$session_id,
'user_message',
[
'session_machine_id' => $session_id,
'message' => 'Can you compare our pricing to the main competitors?',
]
);
Die Maschine wechselt zu planning, reiht ihre Aktion ein und startet den Recherche-Workflow.
Planungsaktion: Workflow starten
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',
];
}
}
Diese Zustandsaktion führt die Recherche nicht selbst durch. Sie startet einen Workflow und speichert die Workflow-ID im Maschinenzustand.
Workflow-Schritt: Die Maschine benachrichtigen, wenn die Recherche bereit ist
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 [];
}
}
Nach Abschluss des Workflows sendet er research_completed zurück an die Maschine und fügt das zusammengestellte Paket als Ereignisdaten hinzu.
Antwortaktion: Neuron mit Sitzungsspeicher aufrufen
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',
];
}
}
Nach dem Erstellen der Antwort wechselt die Maschine mit der aktualisierten Antwort im öffentlichen Zustand zurück zu awaiting_user. Dieselbe Sitzung kann dann ein weiteres Ereignis user_message annehmen und fortfahren.
Warum das sauberer ist als ein großer Workflow
Die aufwendige Arbeit folgt weiterhin dem Workflow-Muster, weil die Recherche Planung, spezialisierte Läufe, Zusammenführung und Synthese erfordert. Das langlebige Support-Gespräch folgt dem Zustandsmaschinen-Muster, weil die Sitzung im Blick behalten muss, ob sie auf eine Benutzernachricht, auf Recherche oder auf die Freigabe zum Antworten wartet.
Diese Aufteilung sorgt für eine gut lesbare Laufzeit und ein klar definiertes Sitzungsmodell.