docs(research): зафиксирован эксперимент с координатором субагентов
- Зачем: - выводы агентного эксперимента нужны как durable-основа для будущего скилла, а не как одноразовый handoff. - Что: - добавлена research note с гипотезой, протоколом, наблюдениями и ограничениями эксперимента. - зафиксированы роли координатора, worker-а и reviewer-а, классификация находок и инварианты будущего скилла. - Проверка: - ручная перечитка docs/research/2026-06-11-subagent-coordinator-experiment.md.
This commit is contained in:
@@ -0,0 +1,360 @@
|
|||||||
|
# Эксперимент с координатором субагентов
|
||||||
|
|
||||||
|
Status: Research
|
||||||
|
Дата: 2026-06-11
|
||||||
|
|
||||||
|
## Зачем проводили эксперимент
|
||||||
|
|
||||||
|
Проверяли рабочую схему, где верхнеуровневый агент не реализует задачу сам, а
|
||||||
|
управляет цепочкой субагентов:
|
||||||
|
|
||||||
|
- выдаёт одному worker-субагенту один локальный issue;
|
||||||
|
- требует от worker-а работать через `/goal` и `/tdd`;
|
||||||
|
- получает отчёт;
|
||||||
|
- просит того же worker-а сделать саморевью;
|
||||||
|
- классифицирует находки;
|
||||||
|
- запускает исправления только по важным пунктам;
|
||||||
|
- при необходимости подключает отдельного reviewer-субагента;
|
||||||
|
- сам выполняет финальную проверку, разделяет коммиты и принимает решение о
|
||||||
|
переходе к следующей задаче.
|
||||||
|
|
||||||
|
Цель эксперимента была не только закрыть задачи генератора, но и понять, можно
|
||||||
|
ли из такого процесса сделать повторяемый агентный навык.
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
Эксперимент проводился в ветке `feature/data-generator` на последних трёх
|
||||||
|
срезах переработки steady-stream генератора:
|
||||||
|
|
||||||
|
1. `.scratch/feature-data-generator/issues/05-intensity-and-flow-calibration.md`
|
||||||
|
2. `.scratch/feature-data-generator/issues/06-state-v2-and-restart.md`
|
||||||
|
3. `.scratch/feature-data-generator/issues/07-service-integration-and-docs.md`
|
||||||
|
|
||||||
|
Задачи были зависимыми и меняли близкие места генератора, поэтому параллелить
|
||||||
|
их было нельзя. Каждая следующая задача должна была стартовать только после
|
||||||
|
коммита предыдущей.
|
||||||
|
|
||||||
|
Итоговые рабочие коммиты:
|
||||||
|
|
||||||
|
- `8f1e997 feat(generator): откалиброван поток steady-stream генератора`
|
||||||
|
- `640050e feat(generator): добавлено состояние версии 2 для рестартов`
|
||||||
|
- `6c4e0f4 feat(generator): подключена новая модель к steady-stream сервису`
|
||||||
|
|
||||||
|
Отдельные docs-коммиты фиксировали ход и выводы эксперимента.
|
||||||
|
|
||||||
|
## Начальная гипотеза
|
||||||
|
|
||||||
|
Ожидалось, что полезное разделение ролей такое:
|
||||||
|
|
||||||
|
- **Координатор** держит порядок, границы задач, проверки, коммиты и решение,
|
||||||
|
какие замечания действительно надо чинить.
|
||||||
|
- **Worker-субагент** глубоко погружается в один issue и реализует его через
|
||||||
|
короткие TDD-срезы.
|
||||||
|
- **Саморевью worker-а** полезно, потому что у него уже есть подробный контекст
|
||||||
|
реализации.
|
||||||
|
- **Отдельный reviewer-субагент** нужен не всегда, а на рискованных переходах
|
||||||
|
или после больших исправлений.
|
||||||
|
|
||||||
|
Верхнеуровневому координатору решили не включать `/goal`: это могло бы сместить
|
||||||
|
поведение в сторону «закрыть большую цель любой ценой», тогда как задача
|
||||||
|
координатора — управлять этапами и останавливаться на контрольных точках.
|
||||||
|
|
||||||
|
## Словарь ролей
|
||||||
|
|
||||||
|
- **Координатор** — верхнеуровневый агент в текущей сессии. Он не пишет основную
|
||||||
|
реализацию, а управляет порядком работ, проверяет границы, классифицирует
|
||||||
|
замечания, запускает дополнительные проверки и делает коммиты.
|
||||||
|
- **Worker** — субагент-исполнитель. Он получает один issue, работает в общем
|
||||||
|
рабочем дереве, меняет файлы, запускает тесты, но не коммитит.
|
||||||
|
- **Саморевью worker-а** — отдельная фаза после реализации. Worker временно
|
||||||
|
меняет роль с исполнителя на проверяющего свою работу, но сначала только
|
||||||
|
выдаёт находки, не исправляя их.
|
||||||
|
- **Reviewer** — отдельный субагент для независимой проверки. По умолчанию он
|
||||||
|
работает только на чтение: анализирует diff, issue, спеки и тесты, но не
|
||||||
|
редактирует файлы.
|
||||||
|
- **Находка** — гипотеза о проблеме, а не автоматическое требование правки.
|
||||||
|
Решение принимает координатор.
|
||||||
|
|
||||||
|
Важная техническая деталь: все агенты работают с одним рабочим деревом. Поэтому
|
||||||
|
координатор обязан регулярно проверять `git status --short` и явно отделять
|
||||||
|
рабочие изменения задачи от процессных заметок, handoff-файлов и чужих правок.
|
||||||
|
|
||||||
|
## Протокол, который сложился в эксперименте
|
||||||
|
|
||||||
|
Рабочий цикл на задачу:
|
||||||
|
|
||||||
|
1. Проверить чистоту дерева и, если нужно, отделить процессные правки отдельным
|
||||||
|
docs-коммитом.
|
||||||
|
2. Запустить нового worker-субагента на один issue.
|
||||||
|
3. В задании worker-у указать:
|
||||||
|
- `/goal`-образную цель;
|
||||||
|
- `/tdd` как методику;
|
||||||
|
- границы задачи;
|
||||||
|
- запрет на коммит;
|
||||||
|
- запрет на откат чужих изменений;
|
||||||
|
- требование финального отчёта: файлы, тесты, проверки, критерии, риски,
|
||||||
|
`git status --short`.
|
||||||
|
4. После реализации запросить у того же worker-а саморевью без правок.
|
||||||
|
5. Классифицировать находки.
|
||||||
|
6. Вернуть worker-у только одобренные правки.
|
||||||
|
7. Запустить локальные проверки координатора.
|
||||||
|
8. На сложных местах запустить отдельного reviewer-а.
|
||||||
|
9. Снова классифицировать находки reviewer-а.
|
||||||
|
10. При больших исправлениях выполнить второй reviewer-круг.
|
||||||
|
11. Коммит делает координатор.
|
||||||
|
12. Старый worker закрывается; следующая задача стартует в новом worker-е.
|
||||||
|
|
||||||
|
Это описание фиксирует фактический процесс эксперимента. Будущий скилл не обязан
|
||||||
|
механически повторять каждый шаг, но должен сохранить инварианты ниже.
|
||||||
|
|
||||||
|
## Классификация reviewer-находок
|
||||||
|
|
||||||
|
Reviewer не является источником истины. Его выводы работают как гипотезы,
|
||||||
|
которые координатор обязан проверить и классифицировать:
|
||||||
|
|
||||||
|
- `чинить до коммита` — реальный дефект, нарушение acceptance criteria или
|
||||||
|
слабое доказательство ключевого поведения;
|
||||||
|
- `записать как риск` — важно помнить, но не блокирует текущий коммит;
|
||||||
|
- `ложная тревога` — reviewer неверно понял код, тест или границы задачи;
|
||||||
|
- `вне скоупа` — может быть полезно позже, но не относится к текущему issue.
|
||||||
|
|
||||||
|
Это оказалось критически важным. Без фильтрации reviewer легко превращается в
|
||||||
|
источник расползания задачи.
|
||||||
|
|
||||||
|
## Наблюдения по задаче 05
|
||||||
|
|
||||||
|
Задача 05 была самой дорогой по обратной связи: калибровка статистической
|
||||||
|
модели требует длинных симуляций и не даёт быстрый «один инвариант — один
|
||||||
|
результат».
|
||||||
|
|
||||||
|
Worker долго работал без промежуточного отчёта. Координатор сначала поставил
|
||||||
|
мягкий статус-чек, затем прервал агента ради статуса. Это не сломало работу, но
|
||||||
|
показало: для задач со статистическими тестами стоит заранее закладывать
|
||||||
|
контрольные точки.
|
||||||
|
|
||||||
|
Саморевью worker-а нашло реальные проблемы:
|
||||||
|
|
||||||
|
- `docker-compose.yml` оставлял старую интенсивность `GEN_LAMBDA_BASE_PER_MIN=200`;
|
||||||
|
- тест межсессионной паузы был слишком широким;
|
||||||
|
- в тестовой конфигурации оставались старые числа;
|
||||||
|
- README мог быть двусмысленным про `GEN_MIN/MAX_EVENTS_PER_TICK`.
|
||||||
|
|
||||||
|
Координаторская проверка добавила отдельную ценность: был найден конфликт между
|
||||||
|
новым `GEN_LAMBDA_BASE_PER_MIN=30` и старым нижним пределом
|
||||||
|
`GEN_MIN_EVENTS_PER_TICK=5`. При тике 5 секунд это давало минимум 60 событий в
|
||||||
|
минуту и ломало критерий интенсивности.
|
||||||
|
|
||||||
|
Вывод: саморевью хорошо ловит локальные несостыковки реализации, но координатор
|
||||||
|
нужен для проверки связей между дефолтами, обычным запуском, документацией и
|
||||||
|
acceptance criteria.
|
||||||
|
|
||||||
|
## Наблюдения по задаче 06
|
||||||
|
|
||||||
|
Задача 06 показала, зачем нужен отдельный reviewer-субагент между задачами.
|
||||||
|
Саморевью worker-а и отдельный reviewer независимо нашли две существенные
|
||||||
|
проблемы:
|
||||||
|
|
||||||
|
- битое state v2 с валидным состоянием ГПСЧ могло пройти `from_dict_safe`, а
|
||||||
|
затем уронить сервис уже в `restore_state`;
|
||||||
|
- снимок активных визитов сохранялся полными batch-словарями и на верхних
|
||||||
|
лимитах получался порядка мегабайт, хотя спека говорила о компактном
|
||||||
|
состоянии.
|
||||||
|
|
||||||
|
Координатор проверил обе гипотезы локально:
|
||||||
|
|
||||||
|
- `restore_state` действительно падал на битой вложенной структуре;
|
||||||
|
- оценка JSON-снимка при 200 активных визитах и популяции 300 дала около 4.7 MB.
|
||||||
|
|
||||||
|
Обе находки были классифицированы как `чинить до коммита`.
|
||||||
|
|
||||||
|
После исправления компактного состояния понадобился второй reviewer-круг. Он
|
||||||
|
нашёл новый дефект уже в исправленной версии: формально похожий v2-state с
|
||||||
|
`population=[]` или строковым `pending_visit_births` проходил первичную
|
||||||
|
загрузку, но затем оставлял поток без пользователей или ронял следующий тик.
|
||||||
|
|
||||||
|
Координатор подтвердил это локальной проверкой и вернул worker-у как
|
||||||
|
обязательный пункт второго круга. Затем координатор дополнительно нашёл
|
||||||
|
парный случай: пользователь с `active_click_id`, но без соответствующего
|
||||||
|
`active_visit`.
|
||||||
|
|
||||||
|
Вывод: если исправление по reviewer-находке меняет дизайн, нужен повторный
|
||||||
|
reviewer-круг. Иначе можно закрыть старый дефект и внести новый рядом.
|
||||||
|
|
||||||
|
## Наблюдения по задаче 07
|
||||||
|
|
||||||
|
Задача 07 показала другую пользу reviewer-а: проверку силы доказательства, а не
|
||||||
|
только поиск падений.
|
||||||
|
|
||||||
|
Worker добавил сервисный тест с мок-публикацией. Он доказывал, что сервисный
|
||||||
|
тик публикует связанные сообщения во все четыре топика, но внешний reviewer
|
||||||
|
заметил: один опубликованный event ещё слабо доказывает уход от старой плоской
|
||||||
|
модели. Для acceptance criteria было важнее доказать невырожденную модель:
|
||||||
|
один визит должен дать несколько событий с одним `click_id`.
|
||||||
|
|
||||||
|
Координатор классифицировал это как `чинить до коммита`. Тест был усилен:
|
||||||
|
сервисный контур теперь делает несколько тиков и проверяет несколько событий
|
||||||
|
одного визита с общим `click_id`, разными `event_id` и согласованными
|
||||||
|
location/device/geo.
|
||||||
|
|
||||||
|
Reviewer также поймал документационные неточности:
|
||||||
|
|
||||||
|
- README слишком широко говорил, что переменные из таблицы проброшены через
|
||||||
|
compose, хотя `KAFKA_BOOTSTRAP_SERVERS` и `GEN_DATA_DIR` должны оставаться
|
||||||
|
безопасными внутренними значениями контейнера;
|
||||||
|
- `docs/OPERATIONS.md` отставал от новых параметров и метрик.
|
||||||
|
|
||||||
|
Вывод: финальная интеграционная задача требует reviewer-а не только по коду, но
|
||||||
|
и по доказательности тестов и честности документации.
|
||||||
|
|
||||||
|
## Что сработало
|
||||||
|
|
||||||
|
- Один worker на один issue хорошо удерживает контекст и границы.
|
||||||
|
- `/goal`-образное задание worker-у работает как хороший контракт даже тогда,
|
||||||
|
когда координатор сам не находится в goal-режиме.
|
||||||
|
- Саморевью того же worker-а полезно, если явно запретить правки на первом
|
||||||
|
шаге и попросить сначала выдать находки.
|
||||||
|
- Отдельный reviewer полезен на границах задач и после больших исправлений.
|
||||||
|
- Координаторская классификация находок обязательна.
|
||||||
|
- Коммиты должен делать координатор: это последняя точка контроля состава diff.
|
||||||
|
- Процессные наблюдения лучше фиксировать отдельно от рабочих коммитов.
|
||||||
|
|
||||||
|
## Что не стоит автоматизировать слепо
|
||||||
|
|
||||||
|
- Не стоит всегда запускать reviewer-а после каждой мелкой правки. Это дорого и
|
||||||
|
может раздувать задачу.
|
||||||
|
- Не стоит принимать все reviewer-находки как правду.
|
||||||
|
- Не стоит давать координатору верхнеуровневый `/goal` на всю цепочку, если от
|
||||||
|
него требуется управленческая осторожность, а не слепое достижение цели.
|
||||||
|
- Не стоит смешивать handoff-наблюдения с рабочими коммитами задач.
|
||||||
|
- Не стоит требовать реальный Kafka-стек для каждого шага, если acceptance
|
||||||
|
criteria допускают мок-публикацию. Но надо явно фиксировать, что именно не
|
||||||
|
проверялось.
|
||||||
|
|
||||||
|
## Ограничения эксперимента
|
||||||
|
|
||||||
|
Выводы нельзя считать универсально доказанными. Эксперимент прошёл в одном
|
||||||
|
репозитории, на одной ветке и на трёх зависимых задачах одного домена. Это
|
||||||
|
хороший сигнал для класса задач «последовательные инженерные срезы с тестами,
|
||||||
|
документацией и явными acceptance criteria», но не доказательство, что процесс
|
||||||
|
так же хорошо подойдёт для:
|
||||||
|
|
||||||
|
- независимых задач, которые можно безопасно параллелить;
|
||||||
|
- маленьких однофайловых правок;
|
||||||
|
- задач без тестовой базы;
|
||||||
|
- продуктовых или исследовательских задач, где итог заранее неясен;
|
||||||
|
- задач, где субагентам нельзя писать в общее рабочее дерево.
|
||||||
|
|
||||||
|
Стоимость процесса тоже заметна. Статистические и state-задачи требуют долгих
|
||||||
|
прогонов, ожидания worker-а, reviewer-кругов и ручной классификации находок.
|
||||||
|
Поэтому будущий скилл должен уметь выбирать облегчённый режим, а не всегда
|
||||||
|
запускать полную схему.
|
||||||
|
|
||||||
|
## Инварианты будущего скилла
|
||||||
|
|
||||||
|
Если на базе эксперимента делать скилл, в него стоит перенести не конкретные
|
||||||
|
команды из этого репозитория, а следующие обязательные правила:
|
||||||
|
|
||||||
|
- **Один worker — один bounded issue.** Цепочка задач ведётся координатором, а
|
||||||
|
не одним долгоживущим исполнителем.
|
||||||
|
- **Reviewer по умолчанию read-only.** Он выдаёт находки, но не правит файлы.
|
||||||
|
- **Исправления идут только после классификации.** Даже хорошие reviewer-находки
|
||||||
|
сначала проходят фильтр координатора.
|
||||||
|
- **Коммит делает координатор.** Это защищает границы задачи и состав diff.
|
||||||
|
- **Большое исправление требует повторной проверки.** Если правка меняет дизайн
|
||||||
|
или формат данных, результат самой правки надо ревьюить заново.
|
||||||
|
- **Рабочее дерево — общий ресурс.** Каждый цикл должен начинаться и
|
||||||
|
заканчиваться явной проверкой статуса.
|
||||||
|
- **Документация и тесты являются частью доказательства.** Reviewer должен
|
||||||
|
проверять не только падения, но и силу тестов, честность README/операционных
|
||||||
|
документов и соответствие acceptance criteria.
|
||||||
|
|
||||||
|
## Эвристики для будущего скилла
|
||||||
|
|
||||||
|
Если делать скилл на базе эксперимента, в нём стоит закрепить такие правила:
|
||||||
|
|
||||||
|
1. **Начинать с чистого дерева.** Если есть процессные или чужие правки,
|
||||||
|
отделить их до запуска worker-а.
|
||||||
|
2. **Давать worker-у один issue.** Не просить закрывать цепочку задач в одном
|
||||||
|
агенте.
|
||||||
|
3. **Формулировать task prompt как контракт.** В нём должны быть цель, границы,
|
||||||
|
методика, запрет на коммит, запрет на откат чужого и формат отчёта.
|
||||||
|
4. **Требовать саморевью без правок.** Исправления только после решения
|
||||||
|
координатора.
|
||||||
|
5. **Классифицировать находки.** Минимальный набор: чинить до коммита, риск,
|
||||||
|
ложная тревога, вне скоупа.
|
||||||
|
6. **Запускать отдельного reviewer-а на рискованных точках.** Особенно:
|
||||||
|
state/serialization, сервисная интеграция, документация, изменения формата
|
||||||
|
данных, статистические модели.
|
||||||
|
7. **Повторять reviewer-круг после больших исправлений.** Если исправление
|
||||||
|
изменило дизайн, оно само нуждается в ревью.
|
||||||
|
8. **Координатор делает собственный sanity-check.** Не полный реимплемент, а
|
||||||
|
проверка связей: дефолты, compose, документация, acceptance criteria,
|
||||||
|
`git status`, тесты.
|
||||||
|
9. **Коммитить только координатору.** Worker не должен фиксировать изменения.
|
||||||
|
10. **Закрывать агентов.** Завершённые subagents надо закрывать, чтобы не
|
||||||
|
держать лишний контекст и лимиты.
|
||||||
|
|
||||||
|
## Возможный скелет скилла
|
||||||
|
|
||||||
|
1. Прочитать issue и зависимости.
|
||||||
|
2. Проверить рабочее дерево.
|
||||||
|
3. Запустить worker.
|
||||||
|
4. Дождаться отчёта или запросить статус, если работа слишком долго молчит.
|
||||||
|
5. Запустить self-review того же worker-а.
|
||||||
|
6. Классифицировать находки.
|
||||||
|
7. Вернуть worker-у только выбранные правки.
|
||||||
|
8. Запустить проверки координатора.
|
||||||
|
9. Если задача рискованная, запустить reviewer-а.
|
||||||
|
10. Классифицировать reviewer-находки.
|
||||||
|
11. При больших исправлениях повторить reviewer-круг.
|
||||||
|
12. Сделать коммит.
|
||||||
|
13. Обновить исследовательскую заметку или итоговый лог, если эксперимент ещё
|
||||||
|
идёт.
|
||||||
|
|
||||||
|
Этот скелет должен быть параметризуемым. Например, для простой задачи можно
|
||||||
|
ограничиться worker + саморевью + проверки координатора. Для state/serialization,
|
||||||
|
интеграции сервиса, документации или статистической модели нужен reviewer.
|
||||||
|
|
||||||
|
## Оставшиеся вопросы
|
||||||
|
|
||||||
|
- Как заранее понять, когда нужен отдельный reviewer, а когда достаточно
|
||||||
|
саморевью?
|
||||||
|
- Стоит ли задавать worker-у обязательные промежуточные отчёты для статистики и
|
||||||
|
долгих тестов?
|
||||||
|
- Нужно ли явно ограничивать бюджет worker-а или число reviewer-кругов?
|
||||||
|
- Как лучше формализовать критерий «большое исправление требует второго
|
||||||
|
reviewer-а»?
|
||||||
|
- Должен ли будущий скилл сам создавать research log, или это должен быть
|
||||||
|
отдельный режим?
|
||||||
|
|
||||||
|
## Предварительная рекомендация
|
||||||
|
|
||||||
|
Эксперимент стоит считать успешным. Процесс дал не только рабочий результат, но
|
||||||
|
и поймал дефекты, которые легко могли пройти обычный одиночный поток:
|
||||||
|
|
||||||
|
- конфликт дефолтов интенсивности;
|
||||||
|
- падение на битом state v2;
|
||||||
|
- слишком большой state v2;
|
||||||
|
- неполная валидация компактного state;
|
||||||
|
- слабый интеграционный тест, не доказывавший уход от старой плоской модели.
|
||||||
|
|
||||||
|
Для будущего скилла ядро должно быть не «запусти много агентов», а
|
||||||
|
**координационный цикл с классификацией находок и контролем границ**.
|
||||||
|
|
||||||
|
Главная формула процесса:
|
||||||
|
|
||||||
|
```text
|
||||||
|
worker реализует -> worker сам себя ревьюит -> координатор фильтрует ->
|
||||||
|
worker чинит выбранное -> reviewer проверяет риск -> координатор фильтрует ->
|
||||||
|
при большом исправлении повторить reviewer -> координатор коммитит
|
||||||
|
```
|
||||||
|
|
||||||
|
## Связанные артефакты
|
||||||
|
|
||||||
|
- `.scratch/handoffs/2026-06-11-subagent-coordinator-experiment.md` —
|
||||||
|
исходный одноразовый handoff и рабочий журнал наблюдений.
|
||||||
|
- `.scratch/feature-data-generator/issues/05-intensity-and-flow-calibration.md`
|
||||||
|
- `.scratch/feature-data-generator/issues/06-state-v2-and-restart.md`
|
||||||
|
- `.scratch/feature-data-generator/issues/07-service-integration-and-docs.md`
|
||||||
|
- `docs/adr/0003-handoffs-in-scratch.md`
|
||||||
|
- `docs/adr/0002-specs-as-durable-design-docs.md`
|
||||||
Reference in New Issue
Block a user