Перейти до вмісту

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 краще підходить для контексту розмови: попередніх запитів, ітеративних правок і відповідей моделі, які мають залишатися в гілці створення тексту.

Використання обох систем дає змогу доручити кожній те, що вона робить найкраще.

Коли краще залишити це робочим процесом

Віддавайте перевагу робочому процесу, якщо етап перевірки чітко обмежений і орієнтований на виконання, наприклад:

Створити одну чернетку, дочекатися одного схвалення й опублікувати.

Віддавайте перевагу автомату станів, якщо сеанс може кілька разів повертатися до тих самих станів, а його життєвим циклом керують зовнішні події.