Глава 11

Распараллеливайте динамическое число независимых workers

Orchestrator-workers подходит для breadth-first поиска и map/reduce задач, где число ветвей определяется содержанием input, а outputs можно объединить по явному правилу. Lead agent или planner сначала строит список независимых work items; control plane проверяет размер fan-out, дубли, write sets и budget, затем запускает ограниченное число workers; reducer объединяет structured outputs, а не длинные разговоры.

  1. Plan: набор work items
  2. Validate: limit, dedupe, scope
  3. Fan out: bounded concurrency
  4. Fan in: schema и provenance
  5. Gap check: stop или второй раунд

Anthropic описывает production research-систему именно как orchestrator-worker: lead agent создаёт специализированные subagents, которые исследуют направления параллельно, после чего отдельный процесс собирает результат и citations. Тот же материал показывает failure mode ранних версий: простой запрос мог породить десятки agents без разумного budget.

Ограничители fan-out поэтому обязательны: максимум workers на run и на один planning step; deduplication key для одинаковых work items; concurrency limit отдельно от total worker limit; локальный token-, tool-call- и time-budget; запрет рекурсивного spawn или явный depth limit; gap check, который может завершить run без нового раунда. В коде основа этого - ready set над DAG.

TypeScript
import type { TaskStatus, WorkItem } from "./types.js";

export function readyTasks(
  tasks: readonly WorkItem[],
  status: Readonly<Record<string, TaskStatus>>
): readonly WorkItem[] {
  return tasks.filter((task) =>
    status[task.id] === "pending" &&
    task.dependsOn.every((dependency) => status[dependency] === "completed")
  );
}

export function createInitialStatus(tasks: readonly WorkItem[]): Record<string, TaskStatus> {
  return Object.fromEntries(tasks.map((task) => [task.id, "pending" as const]));
}

export function hasUnfinishedTasks(status: Readonly<Record<string, TaskStatus>>): boolean {
  return Object.values(status).some((value) => value === "pending" || value === "running");
}
Параллелизм экономит только wall-clock. Он сокращает время лишь для независимых ветвей, а суммарные tokens, calls и cost обычно растут. Поэтому в отчёте храните и critical path, и полное потребление.

Ветви запущены. Когда же среди них появляется критик - он должен давать review по evidence, а не бесконечный спор.

Проверка знаний

Что экономит параллельный запуск независимых workers?

Ссылки