Neuron 记忆 + 审核状态机
当代理需要在多轮人工审核中保留对话上下文,同时整体生命周期仍由 Queuety 编排时,这种模式很有用。
Queuety 管理审核状态机,而 Neuron 管理起草对话线程中的代理记忆。
Neuron 记忆为何在此有用
如果审核者拒绝草稿并要求修改,下次调用代理时就不必从头重建完整上下文。
Neuron 的 SQLChatHistory 很适合这种情况,因为代理可以在同一对话线程中继续工作,同时由 Queuety 路由审核者的决定和各轮修改。
带有 SQL 历史记录的 Neuron 代理
PHP
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.',
],
);
}
}
先起草,再用同一记忆线程修改
PHP
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',
];
}
}
Queuety 状态机
PHP
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' ]
);
第一轮进入 drafting,执行排队的操作,然后转换到 awaiting_review。
如果审核者拒绝草稿,状态机就会回到 drafting。由于 reject 事件载荷会传入 DraftBriefAction,下一版草稿可以使用审核意见,同时保留原有 Neuron 线程历史记录。
为什么这比在工作流中重复执行更清晰
这个问题围绕生命周期展开。草稿等待人工审核,审核者可以批准或拒绝,拒绝会让会话返回起草阶段,批准则会结束会话。与带回边的线性工作流相比,这种流程更适合用状态机表示。
工作流状态仍非常适合保存审核轮数、制品 ID、工作流 ID 和路由决策等编排信息。Neuron 记忆更适合保存对话上下文,例如先前的提示、迭代修改内容,以及应保留在起草线程中的模型响应。
同时使用两者,可以让每个系统各司其职,发挥所长。
何时改用工作流
如果审核路径范围明确且以执行为主,例如以下情况,更适合使用工作流:
生成一份草稿,等待一次批准,然后发布。
如果会话可能多次回到相同状态,且生命周期由外部事件驱动,则更适合使用状态机。