Мы уже подробно разбирали, почему код стал дешёвым расходником, как нейросети ломают архитектуру ради коротких путей и почему роль инженера сместилась в сторону Design Review.

Но если вы сегодня используете автономных терминальных агентов — будь то Claude Code, OpenAI Codex, OpenCode или Antigravity CLI, — вы наверняка сталкивались с одной и той же раздражающей картиной.

Вы ставите агенту нетривиальную задачу на рефакторинг или реализацию фичи из пяти файлов. Отходите заварить кофе. Возвращаетесь через 20 минут в предвкушении готового результата, а в терминале красуется бодрое: «All tasks completed, tests passed!».

Вы с облегчением открываете git diff — и по спине пробегает холодок:

  • Половина краевых условий в бизнес-логике просто удалена;
  • Строгая типизация заменена заглушками any;
  • Упавшие тесты не исправлены, а заботливо закомментированы;
  • Заодно переписан файл конфигурации, который никто не просил трогать.

Одиночный агент свалился в контекстный дрейф (Context Drift) и начал подыгрывать сам себе.

Чтобы разорвать этот порочный круг, я разработал и выложил в открытый доступ pair-mailbox — открытый детерминированный протокол парного программирования для независимых AI-CLI.

Ниже — детальный разбор того, почему пара независимых агентов с правильной архитектурой на порядок надежнее одного «всемогущего» ИИ, и как я устроил эту систему под капотом.


В этой статье:


1. Синдром вежливого согласия: почему один агент слеп к своим багам

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

Когда вы оставляете один на один с кодовой базой одиночную нейросеть, происходит катастрофа:

  1. Вероятностная предвзятость: Модель склонна соглашаться с собственными предыдущими рассуждениями в контексте.
  2. Срезание углов (Shortcutting): Если юнит-тест падает, простейший путь к зеленому статусу для LLM — не переписать сложную логику, а подогнать тест под новое некорректное поведение.
  3. Засорение контекстного окна: К 15–20-му шагу диалога модель забывает глобальные архитектурные инварианты, зацикливаясь на локальной правке синтаксиса.

Главный закон автономного кодинга:
«Вежливое согласие двух агентов не стоит ничего. Ценность имеет только найденный дефект или доказанный факт. Молчаливый консенсус в коде — это не победа, это мина замедленного действия.»

Для надежной работы нужны два изолированных контекста, а в идеале — две разные модели (например, Claude Code на базе Claude 3.7 Sonnet и OpenCode / Codex на базе o3/GPT-4o), которые смотрят на одну кодовую базу под противоположными углами.


2. Проблема «человека-курьера» и хаос обычных мульти-агентных систем

Казалось бы, решение на поверхности: открыть два окна терминала. В одном запустить модель-генератор, во втором — модель-ревьюера.

Но здесь разработчик моментально наступает на грабли:

  • Антипаттерн «Человек-курьер» (RFC 1149): Вы сидите и вручную копируете диффы из одного окна терминала в другое, потом копируете замечания ревьюера обратно, превращаясь в живой сетевой кабель. Какая уж тут автономность?
  • Гонки состояний (State Races): Если дать обоим агентам свободный доступ на запись в репозиторий, через пять минут файлы проекта превращаются в кашу: агенты правят одни и те же строчки, перезаписывают чужие сохранения и ломают git-индекс.
  • Облачный оверинжиниринг: Популярные фреймворки предлагают ставить тяжелые оркестраторы, докер-контейнеры, базы данных и подписки на платные SaaS. Если у вас отваливается интернет или прокси — разработка парализована.

Я пошел от обратного: автономное взаимодействие должно быть локальным, легковесным и строго детерминированным.


3. Архитектура pair-mailbox: разделение властей и Single-Writer

Ядро pair-mailbox построено по принципу классического разделения властей и не требует сторонних pip-зависимостей (Zero External Dependencies — чистый стандартный Python 3, tmux и git).

 ┌──────────────────────────────────────┐        ┌──────────────────────────────────────┐
 │             AGENT: WRITER            │        │             AGENT: CRITIC            │
 │     (OpenCode / Antigravity CLI)     │        │       (Claude Code / Codex CLI)      │
 │  • Единственный с правом на запись   │        │  • Строгий Read-Only режим           │
 │  • Владеет Single-Writer Lock        │        │  • Ищет дефекты и граничные случаи   │
 └──────────────────┬───────────────────┘        └──────────────────┬───────────────────┘
                    │                                               │
                    │ 1. Реализует модуль                           │
                    ├──────────────────────────────────────────────►│ 2. Независимый запуск тестов,
                    │    Передает дифф через Mailbox                │    аудит контрактов и архитектуры
                    │                                               │
                    │ 3. Возвращает SHA-256 опечатанный патч        │
                    │◄──────────────────────────────────────────────┤ (Server: mode 0600, Hash Seal)
                    │                                               │
                    │ 4. Верифицирует хеш, применяет,               │
                    │    устраняет замечания                        │
                    ├──────────────────────────────────────────────►│ 5. Апрув: «Инварианты соблюдены»
                    │                                               │
                    ▼                                               ▼
            [ Автономный проверенный Pull Request без участия человека ]

Принцип «Единственного писателя» (Single-Writer Invariant)

В любой момент времени в проекте может быть только один агент с правом записи.

  • Чтобы начать писать код, агент обязан явно заявить свои намерения через pair_claim, предоставив осмысленное техническое обоснование.
  • Попытка второго агента заявить права отклоняется сервером (CLAIM_REJECTED).
  • Второй агент автоматически принимает на себя роль Критика (Critic / Auditor).

Физический Read-Only для Критика

Критик не может случайно или намеренно изменить ни один рабочий файл проекта. Все вызовы серверных инструментов резервирования патчей для писателя блокируются, а попытки обойти правила фиксируются в журнале аудита. Критик исследует код, ищет регрессии, запускает линтеры и тесты со стороны.

Почтовый ящик (Mailbox) вместо сокетов и облака

Общение между агентами происходит через атомарные монотонно нумеруемые JSON-пакеты в директории .pair/mailbox/:

  • Сообщения нумеруются строго хронологически (000001-<timestamp>-<role>-<token>.json).
  • Для исключения конфликтов на диске используются файловые замки ядра ОС (fcntl.flock).
  • Агент завершает свой ход блокирующим чтением pair_read(wait_seconds=N) — он никогда не висит вхолостую с непрочитанной почтой. А активная доставка pair_deliver через терминал tmux моментально передает управление напарнику.

4. Запечатанные патчи: криптографический барьер качества

Что делать, если Критик не просто нашел баг, а знает, как именно его исправить?

Он не имеет права править файл вслепую. Вместо этого в протоколе реализован механизм опечатанных патчей (Sealed Patches):

  1. Резервирование (pair_patch_reserve): Сервер генерирует изолированный файл патча с правами 0600 (чтение и запись только владельцу процесса).
  2. Опечатывание (pair_patch_seal): Критик записывает унифицированный дифф (unified diff). Сервер вычисляет контрольную сумму SHA-256 и намертво запечатывает файл в состоянии ожидания.
  3. Верификация Писателем (pair_patch_verify): Писатель получает уведомление о патче. Прежде чем применить изменения в рабочее дерево, сервер сверяет SHA-256 хеш содержимого. Если байты разошлись хотя бы на один пробел — применение немедленно блокируется.
  4. Фиксация применения (pair_patch_applied): Непримененный запечатанный патч физически блокирует закрытие задачи. Забыть замечание ревьюера невозможно.

5. Аварийные стопоры и фоновый супервизор: как не сломать прод

Главный страх любого разработчика перед автономными агентами — что ИИ сотрет рабочую базу, скомпрометирует API-ключи или запушит сломанный код в main.

В pair-mailbox за безопасность отвечают два независимых уровня: разграничение автономии и фоновый супервизор pairpilot.

Три уровня автономности (bin/pairctl autonomy <level>)

  • auto (по умолчанию): Тандем самостоятельно проводит задачу через все этапы: скоупинг → декомпозиция → код → ревью → тесты → создание PR. Человека зовут только при аварии.
  • semi: Агенты делают всю черновую работу, но перед выпуском PR и закрытием задачи требуют подтверждения человека одной командой (bin/pairctl ok).
  • observe: Режим наблюдателя для ответственных рефакторингов.

Защита от опасных команд: Белый список против Слепого «Yes»

Терминальные CLI часто запрашивают подтверждение: Allow command execution? [y/N]. Обычные скрипты глупо шлют yes на всё подряд. В pair-mailbox супервизор сверяет текст команды со строгим белым списком:

  • Разрешено подтверждать автоматически: git status, git diff, npm test, pytest, запуск компилятора, чтение и редактирование файлов внутри директории проекта.
  • Категорически запрещено подтверждать (мгновенный стоп):
    • Любые действия с секретами, переменными окружения и файлами .env;
    • Сетевые пайпы вроде curl ... | bash;
    • Опасные системные команды (rm -rf, dd, mkfs);
    • Разрушительные git-операции (push --force, reset --hard, прямой пуш в ветки main/master);
    • Накатывание миграций на боевые базы данных.

При попытке выполнить опасную операцию агент блокируется, а супервизор логирует отказ в журнал аудита и ждет явного вмешательства инженера.


6. Два режима: Ремонт (fix) против Ревизии (audit)

Протокол поддерживает переключение фокуса работы на лету:

Режим fix (Разработка и устранение багов)

Классический рабочий цикл: Писатель вносит изменения в кодовую базу, Критик атакует гипотезы, проверяет тесты до и после изменений, закрывает задачу созданием аккуратного Pull Request со всеми метриками.

Режим audit (Инспекция без изменения кода)

Режим, в котором изменение файлов проекта заблокировано сервером на 100%:

  • Оба агента систематически сканируют кодовую базу на уязвимости, утечки памяти, race conditions и архитектурные нарушения.
  • Агенты действуют как оппоненты: если один выдвигает гипотезу о наличии бага, второй обязан попытаться ее опровергнуть.
  • Только подтвержденные обоими агентами дефекты оформляются в виде отчетов и складываются в папку docs/backlog/. Это идеальный способ провести ревизию легаси перед большим спринтом без риска что-либо сломать.

Итог: будущее за детерминированными протоколами

Качество работы нейросетей определяет не «размер контекстного окна» и не количество параметров в бенчмарках. Качество определяет архитектура ограничений, внутри которой эти нейросети функционируют.

  1. Один агент — всегда риск. Он влюблен в свои решения и легко маскирует собственные ошибки.
  2. Пара агентов с равными правами — хаос. Без централизованного арбитра они неизбежно передерутся за файлы и заспамят контекст.
  3. Пара агентов с разделением ролей («Писатель + Критик») под надзором строгого детерминированного сервера — это автономная инженерная единица.

Если вы хотите перестать работать ручным копипастером между вкладками терминала и доверить агентам рутину без страха за кодовую базу — попробуйте запустить тандем через pair-mailbox на GitHub.

Код стал дешевым. Настало время сделать надежным его исполнение.