
# Neuron Конечный автомат с памятью и проверкой

Этот шаблон полезен, когда агенту нужно сохранять контекст диалога между повторными раундами проверки человеком, а всем жизненным циклом по-прежнему управляет Queuety.

Queuety управляет конечным автоматом проверки, а Neuron отвечает за память агента в ветке подготовки текста.

## Чем здесь полезна память Neuron

Если проверяющий отклонит черновик и попросит внести правки, при следующем вызове агенту не придется восстанавливать весь контекст с нуля.

Neuron `SQLChatHistory` хорошо подходит для этой задачи: агент может продолжить работу в той же ветке диалога, а Queuety направляет решения проверяющего и раунды правок.

## Агент Neuron с историей на основе SQL

```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 артефактов и рабочих процессов, а также решений о маршрутизации. Память Neuron лучше подходит для контекста диалога: предыдущих запросов, итеративных правок и ответов модели, которые должны оставаться в ветке подготовки текста.

Если использовать обе системы, каждая выполняет наиболее подходящую для нее задачу.

## Когда вместо этого оставить рабочий процесс

Выбирайте рабочий процесс, если путь проверки четко ограничен и ориентирован на выполнение задач, например:

Создать один черновик, дождаться одного подтверждения и опубликовать.

Выбирайте конечный автомат, если сессия может несколько раз возвращаться к одним и тем же состояниям, а ее жизненный цикл определяется внешними событиями.

## Связанная документация

- [Конечные автоматы](https://queuety.inline0.dev/ru/docs/state-machines)
- [Сигналы рабочего процесса](https://queuety.inline0.dev/ru/docs/workflows/signals)
- [Оркестрация агентов](https://queuety.inline0.dev/ru/docs/workflows/agent-orchestration)
