Neuron Macchina a stati della sessione
Questo schema è utile quando ciò che state sviluppando si comporta più come una sessione che come un’esecuzione singola.
Le macchine a stati Queuety gestiscono il ciclo di vita della sessione, i workflow Queuety svolgono il lavoro in background che beneficia di nuovi tentativi, modalità di completamento per ogni elemento e aggregazione, mentre Neuron gestisce le chiamate effettive agli agenti e la memoria della conversazione.
Questa suddivisione è adatta agli assistenti per l’assistenza clienti, agli assistenti di ricerca interni, agli agenti editoriali che possono fermarsi in attesa di una revisione umana e alle conversazioni in chat che passano tra le fasi di pianificazione, attesa e risposta.
Struttura del ciclo di vita
In questo esempio, una sessione di assistenza attraversa gli stati awaiting_user, planning, waiting_on_research e responding. La macchina rende esplicito questo ciclo di vita, mentre un workflow in background raccoglie le informazioni e segnala alla sessione quando ha terminato.
La macchina a stati
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 un utente invia un messaggio:
Queuety::machine_event(
$session_id,
'user_message',
[
'session_machine_id' => $session_id,
'message' => 'Can you compare our pricing to the main competitors?',
]
);
La macchina passa a planning, mette in coda la sua azione e avvia il workflow di ricerca.
Azione di pianificazione: avviare il workflow
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',
];
}
}
Questa azione di stato non esegue direttamente la ricerca. Avvia un workflow e registra il suo ID nello stato della macchina.
Passaggio del workflow: segnalare alla macchina quando la ricerca è pronta
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 [];
}
}
Al termine, il workflow invia research_completed alla macchina e include il pacchetto sintetizzato nel payload dell’evento.
Azione di risposta: chiamare Neuron con la memoria della sessione
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',
];
}
}
Dopo la generazione della risposta, la macchina torna a awaiting_user con la risposta aggiornata nello stato pubblico. La stessa sessione può quindi accettare un altro evento user_message e proseguire.
Perché è più semplice di un unico workflow di grandi dimensioni
Il lavoro più impegnativo continua a essere strutturato come workflow, perché la ricerca richiede pianificazione, esecuzioni specialistiche, aggregazione e sintesi. La conversazione di assistenza di lunga durata è strutturata come macchina a stati, perché la sessione deve tenere traccia del fatto che sia in attesa dell’intervento dell’utente, della ricerca o dell’autorizzazione a inviare una risposta.
Questa suddivisione mantiene il runtime leggibile e il modello della sessione esplicito.