Делегирование подзадач (Subagent Delegation)
Инструмент delegate_task порождает дочерние экземпляры AIAgent с изолированным контекстом, ограниченным набором инструментов (toolsets) и собственными терминальными сессиями. Каждый дочерний агент получает новый диалог и работает независимо — только его итоговая сводка попадает в контекст родительского агента.
Одиночная задача
delegate_task(
goal="Debug why tests fail",
context="Error: assertion in test_foo.py line 42",
toolsets=["terminal", "file"]
)
Пакетный параллельный запуск
До 3 одновременных subagent по умолчанию (настраивается, без жёсткого ограничения):
delegate_task(tasks=[
{"goal": "Research topic A", "toolsets": ["web"]},
{"goal": "Research topic B", "toolsets": ["web"]},
{"goal": "Fix the build", "toolsets": ["terminal", "file"]}
])
Как работает контекст subagent
Важно: Subagent ничего не знают
Subagent начинают с полностью нового диалога. Они не имеют никакого представления об истории разговора родителя, предыдущих вызовах инструментов или чём-либо, обсуждавшемся до делегирования. Единственный контекст subagent — это поля goal и context, которые родительский агент заполняет при вызове delegate_task.
Это означает, что родительский агент должен передать всё, что нужно subagent, в самом вызове:
# ПЛОХО — subagent понятия не имеет, что такое "ошибка"
delegate_task(goal="Fix the error")
# ХОРОШО — subagent имеет весь необходимый контекст
delegate_task(
goal="Fix the TypeError in api/handlers.py",
context="""The file api/handlers.py has a TypeError on line 47:
'NoneType' object has no attribute 'get'.
The function process_request() receives a dict from parse_body(),
but parse_body() returns None when Content-Type is missing.
The project is at /home/user/myproject and uses Python 3.11."""
)
Subagent получает сфокусированный системный промпт, построенный на основе ваших goal и context, с инструкцией выполнить задачу и предоставить структурированную сводку: что было сделано, что найдено, какие файлы изменены и с какими проблемами столкнулись.
Практические примеры
Параллельное исследование
Исследуйте несколько тем одновременно и соберите сводки:
delegate_task(tasks=[
{
"goal": "Research the current state of WebAssembly in 2025",
"context": "Focus on: browser support, non-browser runtimes, language support",
"toolsets": ["web"]
},
{
"goal": "Research the current state of RISC-V adoption in 2025",
"context": "Focus on: server chips, embedded systems, software ecosystem",
"toolsets": ["web"]
},
{
"goal": "Research quantum computing progress in 2025",
"context": "Focus on: error correction breakthroughs, practical applications, key players",
"toolsets": ["web"]
}
])
Ревью кода + исправление
Делегируйте workflow ревью и исправления в новый контекст:
delegate_task(
goal="Review the authentication module for security issues and fix any found",
context="""Project at /home/user/webapp.
Auth module files: src/auth/login.py, src/auth/jwt.py, src/auth/middleware.py.
The project uses Flask, PyJWT, and bcrypt.
Focus on: SQL injection, JWT validation, password handling, session management.
Fix any issues found and run the test suite (pytest tests/auth/).""",
toolsets=["terminal", "file"]
)
Многофайловый рефакторинг
Делегируйте крупную задачу рефакторинга, которая перегрузила бы контекст родителя:
delegate_task(
goal="Refactor all Python files in src/ to replace print() with proper logging",
context="""Project at /home/user/myproject.
Use the 'logging' module with logger = logging.getLogger(__name__).
Replace print() calls with appropriate log levels:
- print(f"Error: ...") -> logger.error(...)
- print(f"Warning: ...") -> logger.warning(...)
- print(f"Debug: ...") -> logger.debug(...)
- Other prints -> logger.info(...)
Don't change print() in test files or CLI output.
Run pytest after to verify nothing broke.""",
toolsets=["terminal", "file"]
)
Детали пакетного режима
Когда вы передаёте массив tasks, subagent запускаются параллельно с использованием пула потоков:
-
Максимальная параллельность: 3 задачи по умолчанию (настраивается через
delegation.max_concurrent_childrenили переменную окруженияDELEGATION_MAX_CONCURRENT_CHILDREN; минимум 1, без жёсткого верхнего предела). Пакеты, превышающие лимит, возвращают ошибку инструмента, а не бесшумно обрезаются. -
Пул потоков: Использует
ThreadPoolExecutorс заданным лимитом параллельности в качестве максимального количества рабочих потоков. -
Отображение прогресса: В режиме CLI древовидное представление показывает вызовы инструментов от каждого subagent в реальном времени со строками завершения по каждой задаче. В режиме gateway прогресс накапливается и передаётся в callback прогресса родителя.
-
Порядок результатов: Результаты сортируются по индексу задачи, чтобы соответствовать порядку ввода, независимо от порядка завершения.
-
Распространение прерывания: Прерывание родителя (например, отправка нового сообщения) прерывает всех активных детей.
Одиночная задача делегируется напрямую, без накладных расходов пула потоков.
Переопределение модели
Вы можете настроить другую модель для subagent через config.yaml — полезно для делегирования простых задач более дешёвым/быстрым моделям:
# In ~/.hermes/config.yaml
delegation:
model: "google/gemini-flash-2.0" # Более дешёвая модель для subagent
provider: "openrouter" # Опционально: направить subagent другому провайдеру
Если не указано, subagent используют ту же модель, что и родитель.
Советы по выбору toolsets
Параметр toolsets определяет, к каким инструментам имеет доступ subagent. Выбирайте в зависимости от задачи:
| Шаблон toolset | Сценарий использования |
|---|---|
["terminal", "file"] |
Работа с кодом, отладка, редактирование файлов, сборка |
["web"] |
Исследования, проверка фактов, поиск документации |
["terminal", "file", "web"] |
Полнофункциональные задачи (по умолчанию) |
["file"] |
Анализ только для чтения, ревью кода без исполнения |
["terminal"] |
Системное администрирование, управление процессами |
Определённые toolsets заблокированы для subagent независимо от того, что вы укажете:
-
delegation— заблокирован для листовых subagent (по умолчанию). Сохраняется для детей сrole="orchestrator", ограничен параметромmax_spawn_depth— см. Ограничение глубины и вложенная оркестрация ниже. -
clarify— subagent не могут взаимодействовать с пользователем. -
memory— запрещена запись в общую персистентную память. -
code_execution— дети должны рассуждать пошагово. -
send_message— никаких кроссплатформенных побочных эффектов (например, отправка сообщений Telegram).
Максимум итераций
Каждый subagent имеет лимит итераций (по умолчанию: 50), который ограничивает количество его шагов с вызовом инструментов:
delegate_task(
goal="Quick file check",
context="Check if /etc/nginx/nginx.conf exists and print its first 10 lines",
max_iterations=10 # Простая задача, не требует много шагов
)
Тайм-аут дочернего агента
Subagent считается зависшим и принудительно завершается, если он молчит дольше delegation.child_timeout_seconds секунд реального времени. Значение по умолчанию — 600 (10 минут) — увеличено с 300 секунд в более ранних версиях, потому что модели с высоким уровнем рассуждения при выполнении нетривиальных исследовательских задач завершались принудительно в процессе размышления. Настройте под свою установку:
delegation:
child_timeout_seconds: 600 # по умолчанию
Уменьшайте для быстрых локальных моделей; увеличивайте для медленных моделей рассуждения над сложными задачами. Таймер сбрасывается каждый раз, когда дочерний агент совершает API-вызов или вызов инструмента — только действительно простаивающие рабочие процессы достигают тайм-аута.
Диагностический дамп при тайм-ауте без вызовов
Если subagent завершился по тайм-ауту, совершив ноль API-вызовов (обычно: провайдер недоступен, ошибка аутентификации или отклонение tool-schema), delegate_task записывает структурированную диагностику в ~/.hermes/logs/subagent-timeout-<session>-<timestamp>.log, содержащую снимок конфигурации subagent, трассировку разрешения учётных данных и любые ранние сообщения об ошибках. Это значительно упрощает поиск причин по сравнению с прежним бесшумным тайм-аутом.
Мониторинг запущенных subagent (/agents)
TUI предоставляет панель /agents (алиас /tasks), которая превращает рекурсивное разветвление delegate_task в полноценную поверхность аудита:
-
Дерево запущенных и недавно завершённых subagent в реальном времени, сгруппированных по родителю.
-
Сводки затрат, токенов и количества затронутых файлов по каждой ветке.
-
Управление остановкой и приостановкой — отмените конкретный subagent на лету, не прерывая его соседей.
-
Пост-анализ: просматривайте пошаговую историю каждого subagent даже после того, как они вернулись к родителю.
Классический CLI просто выводит /agents в виде текстовой сводки; TUI — это то, где панель раскрывается в полной мере. См. TUI — Slash commands.
Ограничение глубины и вложенная оркестрация
По умолчанию делегирование является плоским: родитель (глубина 0) порождает детей (глубина 1), и эти дети не могут делегировать дальше. Это предотвращает неконтролируемое рекурсивное делегирование.
Для многоэтапных workflows (исследование → синтез, или параллельная оркестрация подзадач), родитель может порождать детей-оркестраторов, которые могут делегировать задачи собственным рабочим процессам:
delegate_task(
goal="Survey three code review approaches and recommend one",
role="orchestrator", # Позволяет этому потомку порождать собственных рабочих
context="...",
)
-
role="leaf"(по умолчанию): ребёнок не может делегировать дальше — идентично поведению плоского делегирования. -
role="orchestrator": ребёнок сохраняет toolsetdelegation. Ограничивается параметромdelegation.max_spawn_depth(по умолчанию 1 = плоское, так чтоrole="orchestrator"не имеет эффекта при настройках по умолчанию). Увеличьтеmax_spawn_depthдо 2, чтобы оркестраторы могли порождать листовых внуков; до 3 для трёх уровней (максимум). -
delegation.orchestrator_enabled: false: глобальный выключатель, который принудительно устанавливает каждому потомку рольleafнезависимо от параметраrole.
Предупреждение о затратах: При max_spawn_depth: 3 и max_concurrent_children: 3 дерево может достичь 3×3×3 = 27 одновременных листовых агентов. Каждый дополнительный уровень умножает расходы — увеличивайте max_spawn_depth осознанно.
Время жизни и устойчивость
delegate_task синхронен — не является устойчивым
delegate_task выполняется внутри текущего шага родителя. Он блокирует родителя, пока каждый потомок не завершится (или не будет отменён). Это не фоновая очередь задач:
-
Если родитель прерван (пользователь отправил новое сообщение,
/stop,/new), все активные потомки отменяются и возвращаютstatus="interrupted". Их незавершённая работа отбрасывается. -
Потомки не продолжают работу после завершения шага родителя.
-
Отменённые потомки возвращают структурированный результат (
status="interrupted",exit_reason="interrupted"), но поскольку родитель также был прерван, этот результат часто не попадает в видимый пользователю ответ.
Для устойчивой долгосрочной работы, которая должна пережить прерывания или выйти за пределы текущего шага, используйте:
-
cronjob(action=create) — планирует отдельный запуск агента; устойчив к прерываниям родительского шага. -
terminal(background=True, notify_on_complete=True)— долго выполняющиеся shell-команды, которые продолжают работу, пока агент занимается другими делами.
Ключевые свойства
-
Каждый subagent получает собственную терминальную сессию (отдельную от родительской).
-
Вложенное делегирование — опционально — только дети с
role="orchestrator"могут делегировать дальше, и только когдаmax_spawn_depthувеличен с его значения по умолчанию 1 (плоское). Отключите глобально с помощьюorchestrator_enabled: false. -
Листовые subagent не могут вызывать:
delegate_task,clarify,memory,send_message,execute_code. Subagent-оркестраторы сохраняютdelegate_task, но всё ещё не могут использовать остальные четыре. -
Распространение прерывания — прерывание родителя прерывает всех активных потомков (включая внуков под оркестраторами).
-
Только итоговая сводка попадает в контекст родителя, что обеспечивает эффективное использование токенов.
-
Subagent наследуют API-ключ, конфигурацию провайдера и пул учётных данных родителя (что позволяет осуществлять ротацию ключей при ограничениях скорости).
Делегирование vs execute_code
| Фактор | delegate_task | execute_code |
|---|---|---|
| Рассуждение | Полный цикл рассуждения LLM | Только выполнение Python-кода |
| Контекст | Новый изолированный диалог | Нет диалога, только скрипт |
| Доступ к инструментам | Все незаблокированные инструменты с рассуждением | 7 инструментов через RPC, без рассуждения |
| Параллельность | 3 одновременных subagent по умолчанию (настраивается) | Один скрипт |
| Лучше всего для | Сложных задач, требующих суждения | Механических многошаговых конвейеров |
| Стоимость токенов | Выше (полный цикл LLM) | Ниже (возвращается только stdout) |
| Взаимодействие с пользователем | Нет (subagent не могут задавать уточняющие вопросы) | Нет |
Эмпирическое правило: Используйте delegate_task, когда подзадача требует рассуждения, суждения или многошагового решения проблем. Используйте execute_code, когда нужна механическая обработка данных или скриптовые workflows.
Конфигурация
# In ~/.hermes/config.yaml
delegation:
max_iterations: 50 # Максимум шагов на потомка (по умолчанию: 50)
# max_concurrent_children: 3 # Параллельных потомков на пакет (по умолчанию: 3)
# max_spawn_depth: 1 # Глубина дерева (1-3, по умолчанию 1 = плоское). Увеличьте до 2, чтобы оркестраторы могли порождать листовых потомков; до 3 для трёх уровней.
# orchestrator_enabled: true # Отключите, чтобы принудительно установить всем потомкам роль leaf.
model: "google/gemini-3-flash-preview" # Опциональное переопределение модели/провайдера
provider: "openrouter" # Опциональный встроенный провайдер
# Или используйте прямую пользовательскую конечную точку вместо провайдера:
delegation:
model: "qwen2.5-coder"
base_url: "http://localhost:1234/v1"
api_key: "local-key"
Совет
Агент обрабатывает делегирование автоматически, исходя из сложности задачи. Вам не нужно явно просить его делегировать — он сделает это, когда это будет иметь смысл.