Przejdź do treści

Neuron Pamięć + maszyna stanów przeglądu

Ten wzorzec sprawdza się, gdy agent ma zachowywać kontekst rozmowy między kolejnymi rundami weryfikacji przez człowieka, a cały cykl życia nadal ma być koordynowany przez Queuety.

Queuety odpowiada za maszynę stanów przeglądu, a Neuron za pamięć agenta w wątku tworzenia tekstu.

Dlaczego pamięć Neuron jest tu przydatna

Jeśli osoba weryfikująca odrzuci wersję roboczą i poprosi o poprawki, kolejne wywołanie agenta nie powinno wymagać odtwarzania całego kontekstu od początku.

Neuron's SQLChatHistory dobrze się do tego nadaje, ponieważ agent może kontynuować pracę w tym samym wątku rozmowy, podczas gdy Queuety zarządza decyzjami osoby weryfikującej i kolejnymi rundami poprawek.

Agent Neuron z historią opartą na 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.',
            ],
        );
    }
}

Najpierw utwórz wersję roboczą, potem poprawiaj ją z użyciem tej samej pamięci

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',
        ];
    }
}

Maszyna stanów 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' ]
);

Pierwsze podejście przechodzi do drafting, wykonuje zakolejkowaną akcję, a następnie przechodzi do awaiting_review.

Jeśli osoba weryfikująca odrzuci wersję roboczą, maszyna wraca do drafting. Ponieważ dane zdarzenia reject są przekazywane do DraftBriefAction, podczas tworzenia kolejnej wersji agent może skorzystać z uwag osoby weryfikującej, nie tracąc historii pierwotnego wątku Neuron.

Dlaczego to rozwiązanie jest lepsze niż powtarzanie przepływu pracy

To problem dotyczący cyklu życia. Wersja robocza czeka na weryfikację człowieka, osoba weryfikująca może ją zatwierdzić lub odrzucić, odrzucenie kieruje sesję z powrotem do tworzenia tekstu, a zatwierdzenie kończy sesję. Ten scenariusz lepiej odwzorowuje maszyna stanów niż liniowy przepływ pracy z pętlą wsteczną.

Stan przepływu pracy nadal świetnie nadaje się do przechowywania informacji o orkiestracji, takich jak liczba rund weryfikacji, ID artefaktów, ID przepływów pracy i decyzje dotyczące routingu. Pamięć Neuron lepiej przechowuje kontekst rozmowy, taki jak wcześniejsze prompty, kolejne poprawki i odpowiedzi modelu, które powinny pozostać częścią wątku tworzenia tekstu.

Połączenie obu rozwiązań pozwala każdemu systemowi wykonywać zadania, do których najlepiej się nadaje.

Kiedy zamiast tego wybrać przepływ pracy

Wybierz przepływ pracy, gdy ścieżka weryfikacji jest ściśle określona i nastawiona na wykonanie, na przykład:

Utwórz jedną wersję roboczą, poczekaj na jedno zatwierdzenie i opublikuj.

Wybierz maszynę stanów, gdy sesja może wielokrotnie wracać do tych samych stanów, a cyklem życia sterują zdarzenia zewnętrzne.