Neuron Machine à états avec mémoire et révision
Ce modèle est utile lorsqu’un agent doit conserver le contexte de la conversation au fil de plusieurs cycles de révision humaine, tandis que Queuety orchestre toujours l’ensemble du cycle de vie.
Queuety gère la machine à états de révision, tandis que Neuron gère la mémoire de l’agent pour le fil de rédaction.
Pourquoi la mémoire Neuron est utile ici
Si un réviseur rejette un brouillon et demande des modifications, le prochain appel de l’agent ne devrait pas avoir à reconstituer tout le contexte depuis le début.
Le Neuron de SQLChatHistory convient bien à cet usage, car l’agent peut continuer à travailler dans le même fil de conversation tandis que Queuety achemine les décisions du réviseur et les cycles de révision.
L’agent Neuron avec un historique géré par SQL
namespace App\Neuron;
use NeuronAI\Agent\Agent;
use NeuronAI\Chat\History\ChatHistoryInterface;
use NeuronAI\Chat\History\SQLChatHistory;
use NeuronAI\Providers\AIProviderInterface;
use NeuronAI\Providers\Anthropic\Anthropic;
use NeuronAI\SystemPrompt;
final class EditorAgent extends Agent
{
public function __construct(
private readonly string $threadId,
private readonly \PDO $pdo,
) {}
protected function provider(): AIProviderInterface
{
return new Anthropic(
key: $_ENV['ANTHROPIC_API_KEY'],
model: $_ENV['ANTHROPIC_MODEL'],
);
}
protected function chatHistory(): ChatHistoryInterface
{
return new SQLChatHistory(
thread_id: $this->threadId,
pdo: $this->pdo,
table: 'neuron_chat_history',
contextWindow: 50000,
);
}
public function instructions(): string
{
return (string) new SystemPrompt(
background: [
'You are an editorial agent for long-form product briefs.',
'Apply reviewer notes without losing the original intent.',
],
);
}
}
Rédiger une fois, puis réviser dans le même fil de mémoire
namespace App\StateMachines\Actions;
use App\Neuron\EditorAgent;
use NeuronAI\Chat\Messages\UserMessage;
use Queuety\Contracts\StateAction;
final class DraftBriefAction implements StateAction
{
public function handle(array $state, ?string $event = null, array $event_payload = [], array $payload = []): array|string
{
// Queuety creates actions with `new DraftBriefAction()`, so build dependencies here.
$pdo = new \PDO($_ENV['NEURON_DB_DSN'], $_ENV['NEURON_DB_USER'], $_ENV['NEURON_DB_PASSWORD']);
$threadId = $state['neuron_thread_id'] ?? sprintf(
'brief:%d:editor',
$state['brief_id'],
);
$prompt = "Write the first draft for brief {$state['brief_id']}.";
if ('reject' === $event) {
$prompt = sprintf(
'Revise the draft using these review notes: %s',
$event_payload['reason'] ?? 'Tighten the draft and address reviewer concerns.',
);
}
$agent = new EditorAgent($threadId, $pdo);
$message = $agent->chat(
new UserMessage($prompt)
)->getMessage();
return [
'neuron_thread_id' => $threadId,
'draft_markdown' => $message->getContent(),
'review_round' => (int) ($state['review_round'] ?? 0) + 1,
'_event' => 'draft_ready',
];
}
}
La machine à états Queuety
use Queuety\Enums\StateMachineStatus;
use Queuety\Queuety;
$machine_id = Queuety::machine('brief_review_session')
->version('brief-review-session.v1')
->state('drafting')
->action(DraftBriefAction::class)
->on('draft_ready', 'awaiting_review')
->state('awaiting_review')
->on('approve', 'completed')
->on('reject', 'drafting')
->state('completed', StateMachineStatus::Completed)
->dispatch([
'brief_id' => 42,
]);
// Later, once the draft action has finished and the machine waits in `awaiting_review`:
Queuety::machine_event(
$machine_id,
'reject',
[ 'reason' => 'Tighten the introduction and add pricing context.' ]
);
Queuety::machine_event(
$machine_id,
'approve',
[ 'reviewer' => 'editor@example.com' ]
);
Le premier passage entre dans drafting, exécute l’action en attente, puis passe à awaiting_review.
Si le réviseur rejette le brouillon, la machine revient à drafting. Comme la charge utile de l’événement reject est transmise à DraftBriefAction, le brouillon suivant peut tenir compte des remarques du réviseur sans perdre l’historique du fil Neuron d’origine.
Pourquoi cette approche est plus claire qu’une répétition de workflow
Ce problème concerne le cycle de vie. Un brouillon attend une révision humaine, le réviseur peut l’approuver ou le rejeter, un rejet renvoie la session à la rédaction et une approbation met fin à la session. Ce fonctionnement correspond davantage à une machine à états qu’à un workflow linéaire doté d’un retour en arrière.
L’état du workflow reste idéal pour les données d’orchestration comme le nombre de cycles de révision, les ID d’artefacts, les ID de workflows et les décisions de routage. La mémoire Neuron convient mieux au contexte conversationnel, comme les invites précédentes, les révisions successives et les réponses du modèle qui doivent rester dans le fil de rédaction.
Utiliser les deux permet à chaque système de remplir le rôle qui lui convient le mieux.
Quand conserver un workflow à la place
Préférez un workflow lorsque le processus de révision est bien délimité et axé sur l’exécution, par exemple :
Générer un brouillon, attendre une approbation, puis publier.
Préférez une machine à états lorsque la session peut repasser plusieurs fois par les mêmes états et que des événements externes déterminent son cycle de vie.