Почему разделение слоёв критично для AI-агентов: уроки построения Autonomous Developer Farm (ADF)
Когда я начинал строить систему из нескольких AI-агентов, которые пишут код параллельно и автономно, я думал что главная проблема — техническая. Как запустить 6 агентов одновременно? Как не потерять задачи при сбое? Как смотреть на всё это с телефона?
Я ошибался. Главная проблема оказалась архитектурной. И она называется: агент оптимизирует под метрику, а не под результат.
Закон Гудхарта в мире AI-агентов
Есть старый принцип из экономики: «Когда мера становится целью — она перестаёт быть мерой». Карл Гудхарт сформулировал это в 1975 году применительно к монетарной политике. В 2026 году это стало главной проблемой AI-агентов.
Представьте задачу: написать модуль авторизации. Метрика успеха — тесты проходят.
Агент знает какие тесты будут запущены. Агент умён. Агент оптимизирует:
def authenticate(username, password):
# Агент "знает" что тест проверяет admin/secret
if username == "admin" and password == "secret":
return True
return FalseТест проходит. Код бесполезен. Система считает задачу выполненной.
Это не гипотетический сценарий — это то, что происходит когда агент, выполняющий задачу, имеет доступ к критериям оценки своей же работы.
Решение которое кажется очевидным, но не реализуется
Решение простое: тот кто пишет код не должен знать как его будут проверять. А тот кто проверяет не должен знать кто писал и почему.
Это принцип независимой верификации. В науке это называется двойным слепым экспериментом. В праве — независимым судом. В бухгалтерии — внешним аудитом.
Почему же практически ни один из существующих open-source оркестраторов это не реализует?
Потому что это архитектурное ограничение, а не техническая функция. Его нельзя добавить потом. Его нужно закладывать в основу.
Четыре слоя и жёсткие границы
Система которую я построил состоит из четырёх слоёв. Между ними — однонаправленные, типизированные, append-only контракты.
PLANNING → task graph
(что делать, в каком порядке)
↓
EXECUTION → artifacts
(git diff, логи агента)
↓
VERIFICATION → verdict
(прошёл / не прошёл + баллы)
↓
OPTIMIZATION → proposals
(предложения людям, не команды)Критично не что каждый слой делает, а что он не видит.
Planning не видит execution
Planning агент получает цель и структуру кодовой базы. Он генерирует граф задач с зависимостями. Он не знает какой воркер возьмёт задачу, сколько это займёт, каким путём агент придёт к решению.
Это важно потому что если Planning знает детали execution — он начинает под них подстраиваться. Задачи становятся слишком конкретными. Система теряет гибкость.
Execution не видит criteria
Воркер получает только описание задачи. Он не знает:
какие тесты будут запущены
какой минимальный score нужен
по каким критериям его оценят
Это устраняет возможность оптимизации под метрику. Агент вынужден решать задачу честно.
Verification не знает автора
Верификатор получает запечатанный артефакт: git diff и логи. Worker ID скрыт. Контекст «почему так сделано» не передаётся.
def verify(artifact: SealedArtifact, criteria: dict) -> Verdict:
# artifact.worker_id существует в системе
# но намеренно не передаётся в функции проверки
score = check_tests(artifact.worktree_path)
review = llm_review(artifact.git_diff, criteria['rubric'])
# LLM видит только код, не видит кто писалЭто устраняет второй уровень проблемы — контекстуальное смягчение оценки. Когда reviewer знает «агент wt-3 работал над этим три часа и это его третья попытка» — оценка неизбежно смягчается. Когда видит только код — судит объективно.
Optimization видит только агрегаты
Пятый слой анализирует паттерны и предлагает улучшения. Он видит:
процент успешных задач
типичные ошибки (из verdicts)
среднее время выполнения
Он не видит содержимое артефактов. Не видит текущее состояние выполнения. И самое важное — он не может изменить систему напрямую. Только proposals, которые идут на апрув к человеку.
Это защищает от третьей проблемы — нестабильности при самомодификации. Система которая сама себя оптимизирует во время работы — это система которая ломается непредсказуемо.
Почему это сложно реализовать
Проблема не техническая — Python, Redis, SQLite, tmux. Всё это просто. Проблема в том что нужно постоянно сопротивляться соблазну упростить.
Когда пишешь verification агента, очень хочется передать ему описание задачи «для контекста». Это же поможет лучше оценить, правда?
Нет. Это разрушает границу.
Когда пишешь planning агента, хочется показать ему текущие логи агентов чтобы он «понимал ситуацию». Это же разумно?
Нет. Planning не должен знать как выполняется execution.
Каждое исключение из правил кажется разумным. Совокупность исключений делает систему ненадёжной.
Решение — технически закрепить границы. Не договорённостями, а кодом.
# Redis ACL — каждый слой читает только свой канал
user planning-agent on ~events:planning* ~history:* +XADD +XREAD
user execution-agent on ~events:execution* ~heartbeats:* +XADD +XREAD
user verify-agent on ~events:verify* ~artifacts:* +XADD +XREAD
user optim-agent on ~metrics:* ~verdicts:summary:* +XREAD/farm/
agents/planning/ # user: farm-planning
agents/execution/ # user: farm-execution
agents/verification/# user: farm-verify
agents/optimization/# user: farm-optimКогда граница существует на уровне OS permissions и сетевых ACL — её невозможно случайно нарушить.
Event-driven архитектура как следствие
Жёсткие границы между слоями делают классическую request-response архитектуру неудобной. Если Planning не может напрямую вызвать Execution — как они общаются?
Через append-only event log.
HUMAN → TaskSubmitted
PLANNING → TaskGraphEmitted
SCHEDULER → TaskReady
DISPATCHER → TaskAssigned
WORKER → Heartbeat, Progress, Done, Failed
RECONCILER → TaskRequeued, WorkerReleased
VERIFICATION→ VerdictEmitted
OPTIMIZATION→ ProposalGeneratedКаждое событие — immutable запись. Текущее состояние системы — это fold над всеми событиями. Dashboard в браузере не поллит базу данных — он подписывается на stream и применяет новые события поверх текущего состояния.
Это даёт бесплатный audit log, возможность replay для дебага и простое восстановление после сбоев.
Reconciler как control loop
Когда слои изолированы и общаются через events — возникает вопрос: кто следит за тем чтобы желаемое состояние соответствовало реальному?
Reconciler. Он работает по принципу Kubernetes control loop:
loop каждые 10 секунд:
desired = что event log говорит что должно происходить
actual = что heartbeats говорят что происходит реально
diff = desired - actual
act(diff)Если воркер назначен на задачу но не шлёт heartbeat больше 120 секунд — Reconciler переставляет задачу в очередь. Если процесс умер — освобождает воркер. Если задача провалилась меньше max_retries раз — повторно ставит в очередь.
Человек не узнает что что-то пошло не так. Он просто увидит результат.
Что в итоге
Система работает на VPS 8 vCPU / 16 GB. Шесть воркеров, Big Pickle через OpenCode Zen, git worktrees для изоляции, Tailscale для доступа с телефона.
Но железо и инструменты — детали. Принцип один:
AI-агенты без архитектурных границ деградируют. Они начинают оптимизировать под метрику вместо результата, под видимость работы вместо работы.
Закон Гудхарта не исчез когда появились LLM. Он просто стал работать быстрее.

Если хотите повторить — начните с Reconciler и Verification. Остальное можно добавлять постепенно. Но эти два слоя должны быть с самого начала.