Динамические workflows и оркестрация агентов

Один агент с одним контекстным окном работает линейно: читает, думает, пишет, повторяет. Для большинства задач этого достаточно. Но когда нужно одновременно проверить безопасность, производительность и покрытие тестами PR с 2000 строк изменений — или провести рефакторинг пяти независимых модулей за один прогон — линейная работа становится узким местом.

Ключевое наблюдение: большие задачи можно декомпозировать на подзадачи, которые либо независимы (выполняются параллельно), либо имеют зависимости (pipeline). Оркестрация в Claude Code — это инструментарий для управления такими структурами.

Три уровня параллелизма

Claude Code предоставляет три принципиально разных механизма, и выбор между ними определяет и архитектуру, и стоимость:

  • Субагенты — изолированные рабочие внутри одной сессии. Каждый получает подзадачу, выполняет её в своём контекстном окне, возвращает только итог. Общение только через ведущего агента.
  • Команды агентов — полноценные сессии с общим списком задач и почтовым ящиком. Агенты-участники могут переписываться напрямую, не только через ведущего.
  • Agent SDK — программная сборка конвейеров на Python или TypeScript, где разработчик явно управляет параллелизмом через вызовы query().
flowchart TD subgraph SUB["Субагенты (внутри одной сессии)"] direction TB M1[Ведущий агент] -->|делегирует задачу| A1[Субагент: security] M1 -->|делегирует задачу| A2[Субагент: perf] M1 -->|делегирует задачу| A3[Субагент: tests] A1 -->|только результат| M1 A2 -->|только результат| M1 A3 -->|только результат| M1 end subgraph TEAM["Команды агентов (agent teams, эксп.)"] direction TB L[Ведущий] -->|создаёт задачи| TL[(Список задач)] T1[Участник 1] <-->|сообщения| T2[Участник 2] T1 <-->|сообщения| T3[Участник 3] T1 & T2 & T3 -->|захватывают| TL T1 & T2 & T3 -->|сообщения| L end subgraph SDK["Agent SDK (программный)"] direction TB ORCH[Ваш код] -->|asyncio.gather| Q1["query() #1"] ORCH -->|asyncio.gather| Q2["query() #2"] ORCH -->|asyncio.gather| Q3["query() #3"] Q1 & Q2 & Q3 -->|результаты| ORCH end
flowchart TD
    subgraph SUB["Субагенты (внутри одной сессии)"]
        direction TB
        M1[Ведущий агент] -->|делегирует задачу| A1[Субагент: security]
        M1 -->|делегирует задачу| A2[Субагент: perf]
        M1 -->|делегирует задачу| A3[Субагент: tests]
        A1 -->|только результат| M1
        A2 -->|только результат| M1
        A3 -->|только результат| M1
    end

    subgraph TEAM["Команды агентов (agent teams, эксп.)"]
        direction TB
        L[Ведущий] -->|создаёт задачи| TL[(Список задач)]
        T1[Участник 1] <-->|сообщения| T2[Участник 2]
        T1 <-->|сообщения| T3[Участник 3]
        T1 & T2 & T3 -->|захватывают| TL
        T1 & T2 & T3 -->|сообщения| L
    end

    subgraph SDK["Agent SDK (программный)"]
        direction TB
        ORCH[Ваш код] -->|asyncio.gather| Q1["query() #1"]
        ORCH -->|asyncio.gather| Q2["query() #2"]
        ORCH -->|asyncio.gather| Q3["query() #3"]
        Q1 & Q2 & Q3 -->|результаты| ORCH
    end
Три модели оркестрации: субагенты (изоляция контекста), команды агентов (прямая коммуникация), Agent SDK (программный параллелизм)

Субагенты: делегирование с изоляцией контекста

Субагент — это не просто «ещё один вызов Claude». Это отдельный экземпляр с собственным контекстным окном, собственным системным промптом и независимым набором инструментов. Когда субагент завершает работу, в основной диалог возвращается только финальный результат — не поисковые запросы, не прочитанные файлы, не промежуточные шаги.

Это важно: изоляция контекста — главная причина использовать субагенты. Если агент-исследователь читает 50 файлов и выдаёт сводку в 200 строк, основной контекст получает 200 строк, а не 50 файлов.

Определение субагента

Пользовательские субагенты хранятся в .claude/agents/ (уровень проекта) или ~/.claude/agents/ (глобально). Каждый — это markdown-файл с frontmatter:

.claude/
└── agents/
    ├── security-reviewer.md   # аудит безопасности
    ├── test-writer.md         # генерация тестов
    └── doc-updater.md         # обновление документации
---
name: security-reviewer
description: Проводит аудит безопасности кода. Используй для проверки auth, валидации входных данных, работы с токенами.
model: claude-sonnet-4-5
tools:
  - Read
  - Glob
  - Grep
---

Ты — эксперт по безопасности приложений. Анализируй:
- Обработку аутентификации и сессий
- Валидацию и санитизацию входных данных
- Работу с токенами и секретами
- SQL-инъекции и XSS

Всегда указывай severity (critical/high/medium/low) и предлагай конкретное исправление.

Claude автоматически делегирует задачи субагенту, когда описание совпадает с запросом. Явно вызвать можно через Task (в интерактивном режиме) или инструмент Agent. Поле tools строго ограничивает доступные инструменты — субагент не сможет делать ничего, что не указано явно.

Контроль стоимости через модель

Одно из недооценённых применений субагентов — маршрутизация к более дешёвым моделям. Исследовательский субагент, который только читает и суммирует, вполне справляется с claude-haiku-4-5. Субагент, пишущий сложный код, нуждается в claude-sonnet-4-5 или claude-opus-4-5. Поле model в frontmatter позволяет задать это один раз и не думать об этом при каждом запросе.

Check yourself
Вы хотите запустить агента для анализа логов (он прочитает ~30 файлов и создаст сводку), чтобы не засорять основной контекст. Какое поле в frontmatter субагента сделает его недоступным для редактирования файлов?

Quick recall
What is the primary benefit of using subagents over inline agent calls?

Команды агентов: когда агентам нужно общаться

Субагенты идеальны, когда результат важен, а процесс — нет. Но иногда нужно, чтобы агент по безопасности оспорил вывод агента по производительности, или чтобы агент-тестировщик сообщил агенту-архитектору об обнаруженной проблеме прежде, чем тот закончит реализацию.

Для этого в Claude Code есть команды агентов (agent teams) — экспериментальная функция (Claude Code v2.1.32+). По умолчанию отключена:

// .claude/settings.json
{
  "env": {
    "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
  }
}

Архитектура команды

Команда состоит из ведущего (team lead) и участников (teammates). Ведущий — это текущая сессия, которая создаёт команду. Участники — полноценные независимые сессии Claude Code, каждая со своим контекстным окном.

Координация происходит через два механизма:

  • Общий список задач — ведущий создаёт задачи, участники захватывают и выполняют их. Зависимости разрешаются автоматически.
  • Почтовый ящик (mailbox) — любой участник может отправить сообщение любому другому напрямую, без посредника.

Это принципиальное отличие от субагентов: участники команды разговаривают друг с другом. Типичный сценарий — параллельное расследование с конкурирующими гипотезами:

Создай команду из 4 агентов для расследования деградации производительности
в модуле обработки платежей. Один исследует N+1-запросы, второй — утечки
памяти, третий — блокировки, четвёртый — сетевые задержки. Пусть они
активно оспаривают выводы друг друга.

Хуки контроля качества

Для команд доступны специальные хуки в settings.json:

{
  "hooks": {
    "TeammateIdle": [
      {
        "matcher": ".*",
        "hooks": [{ "type": "command", "command": "./scripts/check-tests-pass.sh" }]
      }
    ],
    "TaskCompleted": [
      {
        "matcher": ".*",
        "hooks": [{ "type": "command", "command": "./scripts/validate-output.sh" }]
      }
    ]
  }
}

TeammateIdle — срабатывает, когда участник собирается уйти в idle. Выход с кодом 2 возвращает участника к работе с обратной связью. TaskCompleted — аналогично для задач. Так можно автоматически блокировать «завершение» задачи, если тесты не проходят.

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

Check yourself
Команда агентов исследует баг: три участника тестируют конкурирующие гипотезы. Агент A обнаруживает, что его гипотеза опровергнута данными агента B. Как агент A сообщит об этом агенту B в команде агентов? Как это отличается от субагентов?

Quick recall
How do agent teams differ from subagents in how they share results?

Agent SDK: программные конвейеры

Командная строка Claude Code отлична для интерактивной работы. Но если оркестрация — часть CI/CD или продуктовой системы, нужен программный контроль. Для этого существует Claude Agent SDK (@anthropic-ai/claude-agent-sdk / claude-agent-sdk).

Центральная функция — query(). Она запускает полный агентный цикл: модель планирует, вызывает инструменты, наблюдает результат, повторяет — до завершения.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    async for message in query(
        prompt="Найди все TODO-комментарии и создай сводку",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Glob", "Grep"]
        ),
    ):
        if hasattr(message, "result"):
            print(message.result)

asyncio.run(main())

Параллельный паттерн

Сила SDK — в явном управлении параллелизмом. Вот классический fan-out: несколько независимых задач запускаются одновременно, результаты собираются вместе:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def review_dimension(dimension: str, pr_diff: str) -> str:
    """Один агент — одно измерение ревью."""
    result_text = ""
    async for message in query(
        prompt=f"Проверь этот diff с точки зрения {dimension}:\n\n{pr_diff}",
        options=ClaudeAgentOptions(allowed_tools=["Read", "Grep"]),
    ):
        if hasattr(message, "result"):
            result_text = message.result
    return result_text

async def parallel_review(pr_diff: str):
    dimensions = ["безопасности", "производительности", "покрытия тестами"]
    
    # Все три агента стартуют одновременно
    tasks = [review_dimension(dim, pr_diff) for dim in dimensions]
    results = await asyncio.gather(*tasks)
    
    for dim, result in zip(dimensions, results):
        print(f"=== {dim.upper()} ===\n{result}\n")

asyncio.run(parallel_review(open("pr.diff").read()))

Сессии и продолжение работы

Agent SDK поддерживает сохранение и продолжение сессий через resume. Это позволяет строить многошаговые конвейеры, где каждый шаг опирается на контекст предыдущего:

async def staged_pipeline(filepath: str):
    session_id = None
    
    # Шаг 1: разведка
    async for msg in query(
        prompt=f"Проанализируй структуру {filepath} и найди потенциальные проблемы",
        options=ClaudeAgentOptions(allowed_tools=["Read", "Grep", "Glob"]),
    ):
        if msg.type == "system" and msg.subtype == "init":
            session_id = msg.data["session_id"]
    
    # Шаг 2: реализация — Claude помнит всё из первого шага
    async for msg in query(
        prompt="Теперь исправь найденные проблемы, начиная с критических",
        options=ClaudeAgentOptions(
            resume=session_id,
            allowed_tools=["Read", "Edit", "Bash"]
        ),
    ):
        if hasattr(msg, "result"):
            print(msg.result)

Субагенты в SDK

Как и в интерактивном режиме, SDK позволяет определять специализированных субагентов через AgentDefinition. Ведущий агент делегирует им задачи через инструмент Agent — включи его в allowed_tools:

from claude_agent_sdk import AgentDefinition

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Glob", "Grep", "Agent"],
    agents={
        "security-reviewer": AgentDefinition(
            description="Эксперт по безопасности: JWT, валидация, SQL.",
            prompt="Анализируй код на уязвимости. Всегда указывай severity.",
            tools=["Read", "Grep"],
        ),
        "perf-reviewer": AgentDefinition(
            description="Эксперт по производительности: N+1, кэши, индексы.",
            prompt="Ищи проблемы производительности. Оцени влияние.",
            tools=["Read", "Grep", "Bash"],
        ),
    }
)
Check yourself
В Agent SDK вы запустили первый query() для анализа кода и сохранили session_id. Затем запускаете второй query() с `resume=session_id`. Что именно передаётся второму агенту — и чего он НЕ получает?

Quick recall
What does the `query()` function accomplish in the Claude Agent SDK?

Паттерны для реальных задач

Fan-out → synthesis. Одна крупная задача разбивается на N независимых подзадач (по модулю, по типу проверки, по гипотезе). Все запускаются параллельно, результаты собирает синтезирующий агент. Классика — параллельное ревью PR по измерениям: безопасность, производительность, тесты.

Pipeline с контекстом. Последовательные шаги, где каждый опирается на результат предыдущего. Разведка → план → реализация → верификация. Удобно реализовать через session_id + resume в Agent SDK или просто через многошаговый диалог в интерактивном режиме.

Конкурирующие гипотезы. Особенно эффективно для дебаггинга: несколько агентов одновременно исследуют разные возможные причины проблемы. Через команды агентов (agent teams) они могут оспаривать выводы друг друга. Тот, чья гипотеза выживает под давлением остальных, скорее всего, прав.

Специализация по домену. Постоянные субагенты в .claude/agents/: security-reviewer, test-writer, doc-updater, migration-helper. Каждый знает свою область, имеет доступ только к нужным инструментам и при необходимости может использовать более дешёвую модель.


Когда не нужна оркестрация

Многоагентные конвейеры добавляют сложность и стоимость. Прежде чем строить команду из пяти агентов, стоит задать себе вопрос: можно ли задачу решить последовательно за разумное время? Если да — один агент с хорошим промптом надёжнее, дешевле и проще в отладке.

Оркестрация оправдана, когда:

  • Задача естественно разбивается на независимые подзадачи (параллельный выигрыш реален)
  • Результат одного агента не нужен другому до его завершения (иначе это просто pipeline)
  • Стоимость дополнительных токенов перекрывается экономией времени или качеством (конкурирующие гипотезы, мультиаспектное ревью)

See also

Sources

  1. Create custom subagents — Claude Code documentation
  2. Agent SDK overview — Claude Code documentation
  3. Orchestrate teams of Claude Code sessions — Claude Code documentation