Dynamic Workflow
План в коде для fan-out, состязательная проверка, волны.
Метаданные навыка
| Источник | Опциональный (устанавливается по запросу) — hermes skills install official/autonomous-ai-agents/dynamic-workflow |
| Путь | optional-skills/autonomous-ai-agents/dynamic-workflow |
| Версия | 2.0.0 |
| Автор | Teknium + Hermes Agent |
| Лицензия | MIT |
| Платформы | linux, macos, windows |
| Теги | orchestration, fan-out, subagents, delegation, verification, migration, audit, research, campaign |
| Связанные навыки | hermes-agent, simplify-code |
Справочник: полный SKILL.md
Ниже — полное определение навыка, которое Hermes загружает при его срабатывании. Именно это агент видит как инструкции, когда навык активен.
Навык Dynamic Workflow
Запускает крупную fan-out-работу (веерное распараллеливание) как workflow: план, цикл и
каждый промежуточный результат живут в скрипте и на диске, так что контекст родителя
держит только проверенные результаты. Покрывает разовые fan-out, состязательную сходимость
(попытки + опровергатели) и многоволновые кампании, которые интегрируют десятки веток
воркеров. Устойчивости delegate_task к перезапускам он не даёт; это задача kanban swarm.
Когда использовать
Берите, когда единица работы понятна (файл, эндпоинт, запись), а единиц больше, чем
вмещает один контекст. Пропускайте при ~10 единицах и меньше или для последовательных
цепочек. Для кампании рефакторинга или исправлений в самом hermes-agent загрузите рядом
hermes-agent (рабочий процесс разработки); этот навык отвечает за форму fan-out.
Требования
- Доступен
delegate_task, аdelegation.max_concurrent_childrenрассчитан на волну (по умолчанию 10; runtime отклоняетtasks=[]больше этого значения с понятной ошибкой, а не ставит в очередь).delegation.max_spawn_depth >= 2— только если потомки должны сами делать fan-out.
- Каталог запуска с правом записи, вычисленный из временного каталога окружения терминала (
$TMPDIR, иначе временный каталог платформы). Никогда не буквальный/tmp: в Termux такого каталога нет, а нативный Windows на нём ломается. Используйте<tmp>/wf_<name>_<uuid>/, уникальный для каждого запуска, чтобы прерванный ранее запуск не оставил устаревших выходных файлов, которые будут прочитаны по ошибке. execute_codeдля детерминированного слоя (внутри него существуют толькоweb_search,web_extract,read_file,write_file,search_files,terminal,patch).
Как запускать
Два слоя, разделённые реальной границей возможностей:
Слой A — скрипт execute_code | Слой B — пакет delegate_task | |
|---|---|---|
| Для чего | ДЕТЕРМИНИРОВАННАЯ работа: скачать N URL, разобрать N файлов, выполнить N команд, сгенерировать N выходов по шаблону, собрать манифесты, слить выходы | Работа, требующая СУЖДЕНИЯ LLM: классифицировать, отревьюить, решить, написать, опровергнуть, отрефакторить одну единицу |
| Держит | цикл, ветвление, промежуточные переменные | ничего; один вызов с tasks=[...], каждая задача — свой изолированный агент |
| Инструменты | набор песочницы выше; delegate_task вызвать НЕ может | наборы инструментов родителя, унаследованные без изменений (сужения по задачам нет); потомки теряют delegate_task, clarify, memory, send_message, cronjob_manage |
| Параллелизм | ваш (ThreadPoolExecutor, пакеты) | ограничен delegation.max_concurrent_children |
| Стоимость | только вызовы инструментов | полное дерево агента на каждую задачу; растёт линейно |
Сначала сделайте детерминированную часть в слое A, разведите веером в слое B только тот шаг, который без LLM не обойтись, синтез — на родителе.
Сначала фон: результаты возвращаются сообщениями
Верхнеуровневый delegate_task возвращается сразу, с одним дескриптором на задачу; результат
каждого потомка по завершении приходит в разговор новым сообщением. Прочитать out_*.csv
на следующей строке после вызова нельзя. Доделайте всё, что от потомков не зависит,
дайте статус в одну строку и ЗАВЕРШИТЕ ХОД; на каждое сообщение с результатом реагируйте
по мере поступления. Обычное последующее сообщение пользователя потомков не отменяет;
/stop, /new и выход из процесса — отменяют. Синхронно работает только делегирование,
выданное субагентом-оркестратором (глубина > 0).
Краткая справка
- На единицу должен быть ответ без выхода соседей, иначе это последовательная работа.
- Манифест: одна единица на строку в
<run>/manifest.jsonl; выведите число + каталог запуска. - На потомка: ~8–12 механических правок, или ~2–3 тыс. строк чтения, или ~50–70 КБ
корпуса; размер задавайте по САМОЙ КРУПНОЙ единице. Структурированный вывод — в файлы,
никогда не в поле
summary(под нагрузкой оно обрезается); строки с разделителями предпочтительнее JSON. - Родитель перед слиянием проверяет число файлов и свежесть для этого запуска.
- «Зависший» потомок обычно уже дописал файл; сначала смотрите файловую систему.
- Сначала ограниченный срез (один каталог, 20 записей), отчёт о расходе токенов, потом масштаб.
Процедура
Разовый fan-out
- Разложите на независимые единицы.
- Предварительный проход в слое A пишет манифест.
- Подберите размер чанков под лимиты выше; если задач больше
max_concurrent_children, запускайте ограниченные волны сами. - Слой B: один
delegate_task(tasks=[...]); каждая задача читает свой срез, пишет<run>/out_<i>.csv, печатает слово-статус, останавливается. - Завершите ход. По мере прихода сообщений с результатами читайте файлы, проверяйте, сливайте; сквозной синтез остаётся на родителе.
Состязательная сходимость (работа, где важно качество находок)
- Независимые попытки: ОДИН И ТОТ ЖЕ вопрос N потомкам (2–4) с РАЗНЫМИ формулировками в
каждом
context, каждый пишет по одному утверждению на строку в<run>/attempt_<i>.md. Только локализованные, по отдельности фальсифицируемые утверждения («уPOST /api/users/:id/roleвsrc/routes/users.ts:142нет проверки роли»); опровергатель не сможет разбить «в слое авторизации есть проблемы». - Слейте и дедуплицируйте на родителе; для каждого утверждения отметьте, сколько попыток с ним согласны.
- Опровергатели: второй пакет с заданием РАЗБИТЬ каждое утверждение контрдоказательствами,
выдавая
claim_idx|survives|counter_evidence. Дайте им источники, а не рассуждения попыток. - Показывайте только выживших; опровергнутые утверждения отбрасывайте с причиной в одну строку.
- Новые утверждения из второго раунда прогоните через ещё одно опровержение; остановитесь, когда раунд не добавляет выживших, максимум 3 раунда.
Та же механика защищает родителя от его собственных ошибочных предпосылок: выдавая потомкам эвристику («каждая точка патча на фасаде — мёртвый шов»), велите им сначала опровергнуть её доказательствами и только потом действовать. Четыре отряда, сделав так, превратили сплошную переписку 647 мест в 59 настоящих исправлений и сохранили 130+ зелёных тестов.
Форма кампании (десятки воркеров, несколько волн, часы)
Рецепт разового fan-out не масштабируется на проход по всей кодовой базе. Что сработало:
- Сначала измерьте (LOC, горячие точки, мёртвые символы, корпуса-оракулы) и напишите ОДИН
общий
BRIEF.mdплюсtask_<cluster>.mdна кластер. Каждый потомок читает оба. Когда флот уходит в сторону (потомки подрезают докстринги вместо того, чтобы резать код), один раз поправьте бриф и направьте; повторно отправленные потомки наследуют правку. - Исключительное владение: один кластер файлов на воркера, правки вне его при интеграции отбрасываются. Вложенные fan-out внутри одного файла владеют ДИАПАЗОНАМИ строк и определяют хелперы внутри своего диапазона, чтобы диффы сливались чисто.
- Коммит на каждый проверенный шаг, локально, без push, без PR, без rebase от потомков.
Закоммиченное состояние — единственная форма передачи; каждый воркер, умерший посреди
кампании, потерял ровно свой незакоммиченный хвост. Воркер, делящий индекс worktree,
коммитит только через
git commit -- <paths>; голый коммит однажды захватил застейдженные ханки соседа. - Размер флота — 12–16 одновременно. Выше ~40 процессов на одном OAuth-гранте почасовое обновление токена превращается в лавину 401 и убивает волну. Остальных — в очередь.
- Живость с точки зрения родителя: потомок, сообщивший
completedс парой десятков строк лога или с 0 коммитов в своей ветке, не закончил; ищите его подветки или отправьте заново с worktree и диффом предшественника. - Интеграция на раунд: заморозьте базовый SHA, чистые ветки перебазируйте механически, каждой конфликтующей ветке дайте своего воркера для rebase («поведение main побеждает, переприменённое внутри новой структуры»), слейте в одну интеграционную ветку, прогоните ПОЛНЫЙ набор тестов на объединённом дереве. Коллизии, которые каждая ветка по отдельности проходила, проявляются только здесь. Следующий раунд ветвится от интегрированного коммита, чтобы его воркеры не конфликтовали друг с другом.
- Прежде чем объявить раунд интегрированным:
git rev-list --count <integration>..<branch>равен 0 для КАЖДОЙ ветки. Воркеры продолжают коммитить после того, как вы слили их вершину; однажды так остались за бортом 168 коммитов в шести срезах. - Прогоны тестов: ровно один раннер на машине, за lock-файлом, с высоким
-j. Много параллельных раннеров с низким-jоказались И медленнее, И убивали группы процессов друг друга. Красные файлы перегоняются на голом worktreeorigin/mainв том же venv; совпадающие наборы падений по файлам — старые, не ваши. - Forward-port в конце, а не на каждый раунд: заморозьте SHA main, раздайте конфликтующие файлы по каталогам воркерам, которые правят ОДИН worktree слияния без коммитов, затем родитель коммитит слияние один раз. CI никогда не запускается на конфликтующем PR, так что перед каждым push заново вливайте main.
- Живой QA — отдельная волна: один отряд на поверхность, изолированный
HERMES_HOME, ожидание записано до проверки, доказательства на диске, только отчёт, и регрессией считается только разница PR относительно main. Зелёные юнит-тесты пропустили единственный P0 (путь кода для залогиненного пользователя, который ни один тест не задействовал). - Утверждения ревьюеров получают то же обращение, что и утверждения потомков: A/B относительно базы, прежде чем что-то «восстанавливать». Несколько уверенно заявленных дельт из ревью на базе уже вели себя именно так.
- Перезапуску родителя нужен
HANDOFF.md: почему он умер, какие дескрипторы мертвы, карточка по каждой ветке (дельта LOC, smoke-проверка импортов, точечные тесты) и точный текст повторной отправки. Сначала снимите каждый грязный worktree в коммитwip:.
Подводные камни
- Вызов
delegate_taskвнутри скриптаexecute_code: в песочнице его нет. - Синтез на том же ходу, что и вызов fan-out: файлов ещё не существует.
- Обещание фоновой устойчивости на дни от
delegate_task: он привязан к ходу и умирает вместе с процессом. Устойчивый граф = kanban swarm; разовое =cronjob. - Доверие к
summaryради содержимого или кstatus=completedради факта завершения. - Одна и та же формулировка в каждой «независимой» попытке: они схлопываются в один ответ.
git stashгде угодно в кампании с worktree:refs/stashобщий для всех worktree, и другой воркер вытащит ваши правки. Сравнивайте через временный worktree.- Отчёт о достигнутой цели, когда честная цифра ниже. Скажите «пока 16 %, вот путь к 30 %» и запускайте следующий раунд.
Проверка
- Число строк манифеста совпадает с ожидаемым числом единиц.
- Каждый
out_*.csvсуществует и записан в этом запуске. - У каждого отброшенного утверждения зафиксировано контрдоказательство; каждое показанное утверждение прошло опровержение.
- Кампания: в каждой ветке 0 неслитых коммитов, полный набор тестов на интегрированном дереве с красными, разобранными относительно голого main, отчёт живого QA по каждой поверхности, расход токенов на ограниченном срезе отчитан до полного прогона.