docs(experiment): расширена фактура эксперимента для будущей статьи

- Зачем:
  - подробно задокументировать ход и детали эксперимента, чтобы
    использовать как фактуру для статьи на Habr.
- Что:
  - добавлена предыстория (как эксперимент возник из тестирования скилла).
  - описана техническая механика обоих подходов (Agent Teams vs Codex CLI).
  - добавлена сводная таблица последовательного ревью (13 находок рядом).
  - расширена таблица характера мышления (фокус, ценные/слабые находки,
    характер фиксов, пересечения).
  - добавлены детали фиксов для обоих ревьюеров.
  - добавлены дословные цитаты из момента осознания методологической ошибки.
  - добавлены наблюдения по ходу работы и раздел "что бы сделал иначе".
- Проверка:
  - просмотр EXPERIMENT.md в репозитории.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 22:07:15 +03:00
co-authored by Claude Opus 4.6
parent 47b9fb72a2
commit e8767515d6
+292 -33
View File
@@ -1,8 +1,25 @@
# Эксперимент: как две модели ревьюят один и тот же план # Эксперимент: как две модели ревьюят один и тот же план
Opus и GPT-5.4 получили одинаковый план. Нашли разные проблемы. Opus и GPT-5.4 получили одинаковый план и одинаковую adversarial
Ноль полных совпадений из 9 находок. Этот документ — полный ход установку. Нашли разные проблемы. Этот документ — полный ход
эксперимента, от замысла до выводов. эксперимента: от наивного запуска до контролируемого сравнения.
## Предыстория: как мы вообще сюда попали
Эксперимент не планировался. Изначальная цель — протестировать
скилл `/claude-team-review` на реальной задаче. Я выбрал фичу
из roadmap'а (persistent reviewer memory) и начал планировать.
По ходу работы стало интересно: а что если прогнать тот же план
через `/adversarial-review` (Codex)? Просто посмотреть, что найдёт
другая модель. Одно потянуло за другое — последовательное ревью
показало нулевой overlap, я усомнился в методологии, пересобрал
эксперимент с контролем.
Важный контекст: у меня были сомнения в полезности самой фичи
persistent memory. Поэтому работа шла в отдельном бранче
`feat/reviewer-memory` — мержить только если фича докажет
свою ценность.
## Контекст ## Контекст
@@ -14,6 +31,11 @@ Opus и GPT-5.4 получили одинаковый план. Нашли ра
- **claude-team-review** — Claude пишет, другой Claude ревьюит - **claude-team-review** — Claude пишет, другой Claude ревьюит
(Agent Teams). Одна модельная семья, но изолированные контексты. (Agent Teams). Одна модельная семья, но изолированные контексты.
Оба скилла используют одинаковую adversarial stance: ревьюер
по умолчанию скептичен, каждая находка обязана ответить на 4
вопроса (что сломается, почему уязвим, импакт, рекомендация),
нельзя комментировать стиль и спекулятивные улучшения.
Вопрос: насколько отличаются находки двух ревьюеров? Находят ли Вопрос: насколько отличаются находки двух ревьюеров? Находят ли
они одно и то же? Или каждый видит своё? они одно и то же? Или каждый видит своё?
@@ -31,7 +53,70 @@ Opus и GPT-5.4 получили одинаковый план. Нашли ра
- Флаг `nomemory` для отключения - Флаг `nomemory` для отключения
- Edge cases и верификацию - Edge cases и верификацию
## Фаза 1: последовательное ревью (ошибка методологии) ## Как создавался план
План писался в Claude Code Plan Mode. Сначала Explore-агент изучил
структуру репо (SKILL.md, adversarial-reviewer.md, README.md), потом
Plan-агент спроектировал изменения. Я дополнил и скорректировал.
Первая попытка сохранить план в файл — пользователь отклонил:
"Для чего ты сохраняешь в файл?" Вопрос был не праздный — для
тестирования скилла файл не обязателен, план может быть в контексте
разговора. Скилл поддерживает оба варианта. Это уже первый
полезный инсайт: при тестировании всплывают UX-вопросы, которые
не видны при написании.
В итоге план ушёл в Plan Mode file
(`~/.claude/plans/dapper-herding-whisper.md`) — стандартный механизм
Claude Code.
## Как работает Team Review технически
Для контекста будущей статьи — как устроен процесс под капотом:
1. **TeamCreate** — создаёт команду (`~/.claude/teams/review-memory-plan/`)
и связанный task list
2. **Agent spawn** — создаёт teammate типа `adversarial-reviewer`
(определение в `adversarial-reviewer.md`: model=opus, effort=high,
tools=Read/Grep/Glob/Bash/WebSearch/Context7, disallowedTools=Write/Edit)
3. **Briefing** — lead отправляет reviewer'у промпт с описанием
задачи и путём к плану. Reviewer сам читает файлы, изучает репо
4. **Findings** — reviewer отвечает в формате Summary/Findings/Verdict
5. **Fixes** — lead правит план и отправляет **тому же** reviewer'у
через SendMessage. Контекст сохраняется — reviewer помнит прошлый
раунд, не перечитывает проект
6. **Cleanup** — SendMessage с shutdown_request, затем TeamDelete
Ключевая деталь: teammate **не перезапускается** между раундами.
Это отличает Agent Teams от простого Agent spawn — teammate живёт
между сообщениями. Мне пришлось объяснять это в разговоре, когда
возник вопрос "твой teammate после каждого ответа перезапускает
сессию?" Нет — внутри сессии контекст нативный. Persistent memory
решает другую проблему: контекст между сессиями (закрыл терминал,
открыл завтра).
## Как работает Adversarial Review технически
Для сравнения — как устроен процесс с Codex:
1. **Prompt file** — lead пишет промпт в `/tmp/codex-prompt-{id}.md`
через Write tool (не bash, чтобы избежать quoting issues)
2. **codex exec** — `timeout 600 codex exec -m gpt-5.4
-c model_reasoning_effort=high -s read-only
-o /tmp/codex-review-{id}.md - < /tmp/codex-prompt-{id}.md`
Синхронный вызов, 10 минут timeout
3. **Session ID** — извлекается из stderr (`session id: uuid`)
4. **Resume** — `codex exec resume {session_id} - < prompt`
Продолжает сессию, экономит токены. Но есть нюансы (см. статью)
5. **Cleanup** — `rm -f /tmp/codex-*-{id}.*`
Разница в capabilities: Codex работает в read-only sandbox (может
только читать файлы и запускать git). Opus-teammate может запускать
тесты, лinter'ы, обращаться к Context7 (MCP), искать в интернете.
В этом эксперименте разница не существенна (ревьюим план, не код),
но для code review может влиять.
## Фаза 1: последовательное ревью
Сначала я запустил ревью последовательно, как обычно делаю в работе: Сначала я запустил ревью последовательно, как обычно делаю в работе:
@@ -39,9 +124,9 @@ Opus и GPT-5.4 получили одинаковый план. Нашли ра
План v1 → Opus (2 раунда) → План v2 → Codex (5 раундов) → План v3 План v1 → Opus (2 раунда) → План v2 → Codex (5 раундов) → План v3
``` ```
### Opus (claude-team-review): 6 находок за 1 раунд ### Opus (claude-team-review): Round 1 — 6 находок, Round 2 — approve
Opus получил оригинальный план v1 и выдал 6 находок сразу: Opus получил оригинальный план v1 и выдал 6 находок в первом раунде:
**1. [high] Merge-логика слишком сложна для prompt-based системы** **1. [high] Merge-логика слишком сложна для prompt-based системы**
@@ -87,12 +172,36 @@ Lead должен парсить свободный текст reviewer'а и з
Правило "Avoid creating auxiliary files (memory files...)" запрещает Правило "Avoid creating auxiliary files (memory files...)" запрещает
memory files, но план создаёт именно memory file. memory files, но план создаёт именно memory file.
**После фиксов — Opus одобрил план на 2-м раунде.** **Как фиксил:**
Все 6 находок принял. Самый значимый фикс — #1 (merge-логика):
полностью убрал partial merge, заменил на full rewrite. Это не
"подправить формулировку" — это изменение архитектурного решения.
Lead больше не парсит секции и не управляет cap'ами — он просто
пишет файл целиком из ответа reviewer'а.
Для #2 (шаблон) — добавил точный markdown-template в оба файла.
Для #3 (парсинг) — описал порядок: сначала извлекать флаги,
потом определять mode. Для #4 (gitignore) — добавил рекомендацию.
Для #5 (cap) — убрал отдельный read cap, заменил на детектор
повреждения (>500 строк = caps не сработали, регенерировать).
Для #6 — убрал "memory files" из списка запретов.
Во втором раунде Opus проверил фиксы и одобрил план — новых
проблем не нашёл. Проверка заняла заметно меньше времени: reviewer
уже знал проект и план, просто верифицировал изменения.
### Codex (adversarial-review): 7 находок за 5 раундов ### Codex (adversarial-review): 7 находок за 5 раундов
Codex получил уже улучшенный план v2 (после фиксов Opus'а) и нашёл Codex получил уже улучшенный план v2 (после фиксов Opus'а). Это
ещё 7 проблем. По одной-две за раунд, каждый раз копая глубже: важная деталь: Codex ревьюил не тот же вход, что Opus. Грубые
проблемы (merge-логика, отсутствие шаблона) уже были исправлены.
Тем не менее, Codex нашёл ещё 7 проблем — по одной-две за раунд,
каждый раз копая глубже. Все 7 — severity high. Это не случайность:
Opus убрал проблемы уровня "спецификация" и "архитектура", остались
проблемы уровня "безопасность" и "корректность" — те, что Opus
не тронул.
**Раунд 1 — две находки:** **Раунд 1 — две находки:**
@@ -163,28 +272,104 @@ README line 109: `cp .claude/agents/adversarial-reviewer.md ~/.claude/agents/`.
Рекомендация: убрать безусловное утверждение, рекомендовать gitignore Рекомендация: убрать безусловное утверждение, рекомендовать gitignore
по умолчанию, коммитить только после проверки содержимого. по умолчанию, коммитить только после проверки содержимого.
**5 раундов, максимум достигнут. Последний фикс не был verify.** 5 раундов, максимум достигнут. Последний фикс не был verify.
### Что бросилось в глаза **Как фиксил находки Codex:**
Раунд 1: для #1 (injection) — обернул memory в `<untrusted-data>`
с явным ограждением, добавил валидацию структуры при загрузке.
Для #2 (late persistence) — добавил промежуточные checkpoints.
Раунд 2: #3 (section name) — унифицировал `## Review log` везде.
#4 (nomemory) — добавил gate на все memory-операции, включая
checkpoints.
Раунд 3: #5 (ложные resolved) — промежуточные checkpoints теперь
не трогают Resolved findings, только финальный Step 6b.
Раунд 4: #6 (путь в README) — исправил путь установки в плане.
Это оказался реальный баг в текущей документации проекта.
Раунд 5: #7 (no secrets) — убрал безусловное утверждение, сменил
рекомендацию на gitignore по умолчанию.
Характерная разница в фиксах: для Opus я менял **архитектуру**
(переписал merge-логику). Для Codex я добавлял **защиты**
(валидации, gates, ограждения). Разный характер находок →
разный характер фиксов.
### Стоп. Мы сравниваем не то, что думаем
13 находок суммарно. Ноль пересечений. Ни одна находка Codex 13 находок суммарно. Ноль пересечений. Ни одна находка Codex
не дублировала находку Opus. не дублировала находку Opus. Казалось бы — идеальная комплементарность.
Но это нечестное сравнение: Codex ревьюил улучшенный план. На этом этапе я (Дмитрий) сформулировал наблюдение:
Opus мог бы найти те же проблемы в оригинале. Или Codex мог бы
найти другие проблемы, если бы видел merge-логику до упрощения.
## Фаза 2: параллельное ревью (контролируемый эксперимент) > Опус, как ревьюер, рассуждает с позиции архитектора. Видя проблему
> больше сверху. Кодекс — больше как тщательный исполнитель, копает
> нюансы конкретного плана, в сторону уязвимостей/косяков.
Осознав проблему, я сохранил оригинальный план v1 и отправил его Красивый вывод. И вот сводная таблица, которую мы составили
Codex отдельно — на том же входе, что получил Opus. на этом этапе — ещё до осознания методологической проблемы:
### Сводка последовательного ревью (Opus на v1, Codex на v2)
| # | Opus (на v1) | Codex (на v2) |
|---|-------------|---------------|
| 1 | **Merge-логика слишком сложна** — LLM не потянет partial merge в prompt-based системе [high] | **Prompt injection** — memory verbatim в промпте, можно отравить [high] |
| 2 | **Нет шаблона обмена** — lead парсит свободный текст, хрупко [high] | **Memory пишется слишком поздно** — прерванные сессии теряют контекст [high] |
| 3 | **`nomemory` парсинг** — не описаны комбинации аргументов [medium] | **Section name mismatch** — шаблон и валидатор не согласованы [high] |
| 4 | **gitignore** — нет рекомендации коммитить или игнорить [medium] | **`nomemory` не гейтит checkpoints** — opt-out неполный [high] |
| 5 | **500-line cap** — поведение при превышении не определено [medium] | **Checkpoint пишет ложные resolved** — lead предполагает вместо reviewer'а [high] |
| 6 | **Противоречие в правилах** — "memory files" в списке запретов [low] | **README path mismatch** — установка по документации не работает [high] |
| 7 | — | **"Contains no secrets"** — unsafe claim, findings могут содержать sensitive data [high] |
**13 находок. 0 пересечений. 6 vs 7.**
Наглядно видно: Opus нашёл **проблемы дизайна** (сложность,
контракт, спецификация). Codex нашёл **проблемы корректности
и безопасности** (injection, data leak, broken contracts,
ложное состояние). Разные этажи одного здания.
Но тут же возник вопрос: **а честное ли сравнение?**
Codex ревьюил другой вход. Opus получил план v1. Codex получил
план v2 — уже без merge-логики, с точным шаблоном, с описанным
парсингом флагов. Конечно находки не пересекаются: половину
проблем уже исправили.
Наблюдение дословно:
> У нас эксперимент интересный, но не полный. Мы ревьюили
> последовательно. Сначала улучшили план одним агентом, затем,
> после полировки — другим.
Может быть, Codex нашёл бы merge-логику на v1? Может быть, Opus
нашёл бы prompt injection, если бы мы не убрали другие проблемы?
Мы не знаем. Последовательное ревью хорошо для качества плана,
но бесполезно для сравнения ревьюеров.
Нужен контролируемый эксперимент: оба ревьюера на одном и том же
входе.
## Фаза 2: параллельное ревью (контролируемое сравнение)
Я сохранил оригинальный план v1 и отправил его Codex отдельно.
Тот же вход, та же adversarial установка, тот же промпт-шаблон.
Единственная разница — модель.
``` ```
┌→ Opus → 6 находок ┌→ Opus → 6 находок (полный цикл: 2 раунда)
План v1 ──┤ План v1 ──┤
└→ Codex → 3 находки └→ Codex → 3 находки (один прогон, без итераций)
``` ```
Важная оговорка: Codex на v1 запускался одним прогоном без
итеративного цикла фиксов. Opus прошёл 2 полных раунда. Сравнение
не полностью симметричное — Codex мог бы найти больше за 5 раундов.
Но первый раунд — самый информативный: именно он показывает,
на что модель обращает внимание в первую очередь.
### Codex на плане v1: 3 находки ### Codex на плане v1: 3 находки
**1. [high] Memory может утечь в git** **1. [high] Memory может утечь в git**
@@ -206,7 +391,7 @@ Codex отдельно — на том же входе, что получил Op
> режет файл посреди секции. После нескольких циклов файл > режет файл посреди секции. После нескольких циклов файл
> деградирует. > деградирует.
## Сравнение: Opus vs Codex на одном и том же входе ## Сравнение: Opus vs Codex на одном входе
### Все находки рядом ### Все находки рядом
@@ -235,16 +420,29 @@ Codex отдельно — на том же входе, что получил Op
**Только Codex:** утечка данных, monorepo path resolution. **Только Codex:** утечка данных, monorepo path resolution.
### Характер мышления ### Характер мышления — главная таблица
| | Opus | Codex | Эта таблица — ключевой артефакт эксперимента. Она показывает
|---|------|-------| не просто разницу в находках, а разницу в **способе думать**:
| Аспект | Opus | Codex |
|--------|------|-------|
| **Кто он** | Архитектор | Въедливый, тщательный исполнитель |
| **Главный вопрос** | "Сможет ли система это выполнить?" | "Что сломается в реальном мире?" | | **Главный вопрос** | "Сможет ли система это выполнить?" | "Что сломается в реальном мире?" |
| **Роль** | Архитектор | Security/ops инженер | | **Фокус** | Внутренняя согласованность, feasibility | Failure modes, безопасность, edge cases |
| **Лучшая находка** | Merge-логика → упрощение архитектуры | Project root → monorepo сценарий | | **Лучшая находка** | Merge-логика → упрощение архитектуры | Project root → monorepo сценарий |
| **Слепое пятно** | Security (не нашёл injection, data leak) | Feasibility (не сомневается что LLM справится) | | **Слепое пятно** (в этом эксперименте) | Security (не нашёл injection, data leak) | Feasibility (не сомневается что LLM справится с merge) |
| **Стиль рекомендаций** | "Сделай проще" | "Добавь защиту" | | **Стиль рекомендаций** | "Сделай проще" | "Добавь защиту" |
| **Самые ценные находки** | Упрощение merge-логики (архитектурное решение) | Prompt injection, late persistence (неочевидные failure modes) |
| **Слабые находки** | Rule contradiction (косметика) | — (все medium+) |
| **Темп** | 6 находок сразу, approve на Round 2 | 1-2 за раунд, копает послойно | | **Темп** | 6 находок сразу, approve на Round 2 | 1-2 за раунд, копает послойно |
| **Пересечения** | 3 темы — видит как проблемы дизайна/UX | 3 темы — видит как проблемы безопасности/корректности |
| **Характер фиксов** | Меняешь архитектуру | Добавляешь защиты |
Opus видит здание сверху: "фундамент кривой, перестрой". Codex
ходит по этажам и проверяет каждую дверь: "этот замок можно
открыть отвёрткой, тут нет пожарного выхода, а тут табличка
врёт".
## Выводы ## Выводы
@@ -269,14 +467,14 @@ Codex думает как security/ops инженер: "что если злоу
Одна и та же дыра в плане, но один ревьюер предложит добавить Одна и та же дыра в плане, но один ревьюер предложит добавить
строчку в README, а другой — пересмотреть, где хранить файл. строчку в README, а другой — пересмотреть, где хранить файл.
### 3. Последовательное ревью скрывает реальную картину ### 3. Последовательное ревью полезно, но скрывает реальную картину
В последовательном режиме (Opus → Codex) мы увидели 13 уникальных В последовательном режиме (Opus → Codex) мы увидели 13 уникальных
находок и решили, что overlap нулевой. Но это артефакт: Codex ревьюил находок и решили, что overlap нулевой. Но это артефакт: Codex ревьюил
улучшенный план. При параллельном запуске на одном входе — улучшенный план. При параллельном запуске на одном входе —
9 находок с 30% частичным пересечением. 9 находок с 30% частичным пересечением.
Для оценки моделей — параллельный запуск. Для максимального Для сравнения моделей — параллельный запуск. Для максимального
качества плана — последовательный (второй ревьюер находит то, качества плана — последовательный (второй ревьюер находит то,
что первый пропустил даже после фиксов). что первый пропустил даже после фиксов).
@@ -298,14 +496,17 @@ Codex думает как security/ops инженер: "что если злоу
## Числа ## Числа
| Метрика | Opus | Codex (на v1) | Codex (на v2, sequential) | | Метрика | Opus (на v1) | Codex (на v1) | Codex (на v2, sequential) |
|---------|------|---------------|---------------------------| |---------|--------------|---------------|---------------------------|
| Находок | 6 | 3 | 7 | | Находок | 6 | 3 | 7 |
| Раундов до approve/max | 2 | 1 (single pass) | 5 (max reached) | | Раундов | 2 (approve) | 1 (single pass*) | 5 (max reached) |
| High severity | 2 | 2 | 7 | | High severity | 2 | 2 | 7 |
| Уникальных (не найдено другим) | 3 | 2 | 7 | | Уникальных (не найдено другим) | 3 | 2 | 7 |
| Частичных пересечений | 3 темы | 3 темы | 0 (другой вход) | | Частичных пересечений | 3 темы | 3 темы | 0 (другой вход) |
*Codex на v1 запускался одним прогоном для сравнения первой реакции.
Полный итеративный цикл на v1 не проводился.
## Ограничения эксперимента ## Ограничения эксперимента
- Один эксперимент на одном плане — может не обобщаться. - Один эксперимент на одном плане — может не обобщаться.
@@ -315,8 +516,66 @@ Codex думает как security/ops инженер: "что если злоу
доступом к командам. Разные возможности могут влиять на находки. доступом к командам. Разные возможности могут влиять на находки.
- Один и тот же human (lead) фиксил для обоих — интерпретация - Один и тот же human (lead) фиксил для обоих — интерпретация
lead'а добавляет переменную. lead'а добавляет переменную.
- Параллельное сравнение — только Round 1 Codex (без итераций). - Параллельное сравнение асимметрично: Opus прошёл 2 раунда,
Полный итеративный цикл на v1 не проводился. Codex — 1. Codex мог бы найти больше за полный цикл.
- Характеристики "архитектор" и "ops-инженер" — наблюдение из
одного эксперимента, не универсальное свойство моделей.
## Наблюдения по ходу работы
Мелочи, которые не вошли в основной текст, но могут пригодиться:
**Opus одобряет быстрее.** 6 находок в Round 1, approve в Round 2.
Codex на v2 шёл 5 раундов и не одобрил. Возможная причина: Opus
выдаёт всё сразу (широкий взгляд), Codex копает послойно — каждый
фикс открывает новый слой проблем. Для пользователя: Opus-ревью
быстрее, Codex-ревью глубже.
**Codex находил баги в моих фиксах.** Находки #3 и #4 (section
name mismatch, nomemory не гейтит checkpoints) — это баги,
внесённые фиксами к находкам Opus'а. Второй ревьюер ловит ошибки
первого цикла фиксов. Аргумент в пользу последовательного ревью.
**Codex нашёл баг в существующей документации.** Находка #6
(README path) — не про план, а про текущий README. Побочный
эффект: ревью плана обнаружило проблему в проекте, которая
существовала до начала работы.
**Характер фиксов разный.** Для Opus — меняешь архитектуру
(переписал merge-логику). Для Codex — добавляешь защиты
(валидации, gates, ограждения, untrusted-data обёртки).
Архитектурные фиксы меняют больше строк, но делаются один раз.
Защитные фиксы точечные, но их много.
**Момент с intra-session vs cross-session.** В процессе возник
вопрос: "Ты хочешь сказать, твой teammate reviewer в процессе
работы, после каждого ответа, перезапускает сессию?" Нет —
внутри сессии Agent Teams сохраняют контекст нативно (teammate
живёт, получает SendMessage). Persistent memory решает другую
проблему: контекст между сессиями. Это важное различие, которое
не очевидно из описания фичи.
**Plan Mode и запуск скиллов.** План создавался в Claude Code
Plan Mode. Потом из того же Plan Mode запустил `/claude-team-review`
— скилл корректно определил mode=plan из системного сообщения
"Plan mode is active". Затем `/adversarial-review` — тоже
определил plan mode. Оба скилла умеют работать из Plan Mode,
что удобно: ревьюишь план до выхода из планирования.
## Что бы я сделал иначе
Если повторять эксперимент:
1. **Сразу параллельно.** Запускать оба ревьюера на одном входе
с самого начала, без последовательной фазы.
2. **Полный цикл для обоих.** Codex на v1 прошёл только 1 прогон.
Для честного сравнения — оба по 5 раундов с итерациями.
3. **Больше планов.** Один план — одна точка данных. Нужно 5-10
разных планов разной сложности.
4. **Ревью кода, не только плана.** План — абстрактный артефакт.
Code review с реальным diff может показать другие паттерны.
5. **Фиксировать время.** Не записал сколько минут занял каждый
раунд. Для статьи это было бы полезно.
## Дата эксперимента ## Дата эксперимента