Ранас Мукминов – Оркестрация ИИ-агентов. Claude Opus 4.7 (страница 30)
└──────────────┼──────────────┘
▼
┌──────────────┐
│ результат │
└──────────────┘
Топология звездообразная: все рёбра идут от центра к воркерам и обратно, рёбер «воркер — воркер» нет. Это намеренное ограничение. Как только воркеры начинают общаться напрямую, паттерн перестаёт быть fan-out/fan-in и становится либо доской (см. главу 11), либо peer-to-peer-сетью (см. главу 13) — с другими свойствами согласования и другими отказами. Отсутствие горизонтальных рёбер — то, что делает воркеров взаимно изолированными и потому надёжно параллельными (см. главу 54 о share-nothing).
Воркер здесь — это полноценный агент в смысле первой книги: контур наблюдение—план—действие—проверка, со своим контекстом, своими инструментами и своими правами (см. главу 16 о роли как тройке). Оркестратор тоже может быть агентом — то есть управляться моделью, принимающей решения о разбиении на естественном языке, — а может быть детерминированным кодом, который раздаёт фиксированный набор подзадач. Это различие принципиально и разбирается ниже отдельно.
Стоит сразу отделить логическую топологию от физической. Логически fan-out/fan-in — это звезда из одного оркестратора и N воркеров. Физически всё это может исполняться в одном процессе (оркестратор последовательно вызывает воркеров как функции, эмулируя параллелизм через асинхронность), в нескольких процессах на одной машине или в распределённой системе из многих узлов. Паттерн не предписывает физического размещения; он предписывает структуру ответственности. Это важно, потому что отказы привязаны к логической структуре — к трём швам разбиения, исполнения и сборки, — а не к тому, на скольких машинах всё крутится. Распределённое размещение добавляет свои отказы (сетевые разрывы, частичная доставка), но они накладываются на структурные отказы паттерна, а не заменяют их.
Паттерн легко спутать с двумя соседними конструкциями, и эта путаница приводит к неверным ожиданиям по надёжности.
Первое, чем он не является, — это ансамбль (см. главу 3 и главу 63). В ансамбле несколько агентов решают одну и ту же задачу целиком и независимо, а затем кто-то выбирает лучший ответ или усредняет. Там разбиения нет: каждый воркер делает всю работу. Fan-out/fan-in, в строгом смысле, предполагает, что задача декомпозирована и воркеры делают разные части. Граница размывается в спекулятивном исполнении (см. главу 36), где несколько воркеров намеренно делают одно и то же ради скорости или качества, но это частный режим, а не базовый паттерн.
Второе, чем он не является, — это конвейер. В конвейере результат воркера A становится входом воркера B; работа течёт последовательно через стадии (см. главу 10). В fan-out/fan-in воркеры не зависят друг от друга и работают одновременно. Если между подзадачами есть зависимости, чистый fan-out/fan-in уже не применим: нужен либо граф зависимостей с волнами параллелизма (см. главу 32), либо конвейер. Базовый паттерн в его учебной форме предполагает независимые подзадачи — и именно это предположение чаще всего нарушается на практике, о чём ниже.
Три причины. Первая — он даёт настоящий параллелизм при минимальной координации: воркеры не согласуют ничего между собой, вся координация сведена в одну точку. Это минимально возможный координационный налог для параллельной работы (см. главу 5). Вторая — он рекурсивен: воркер может сам быть оркестратором, и так строятся иерархии произвольной глубины (см. главу 9). Третья — он диагностируем: при звездообразной топологии причинные цепочки коротки, и посмертная реконструкция сессии проще, чем в децентрализованных схемах (см. главу 81). Эти три свойства делают fan-out/fan-in разумным выбором по умолчанию — той топологией, с которой стоит начинать и от которой отступать только при доказанной необходимости.
Оркестратор — не «главный агент», а узел, на который возложен набор обязанностей. Их полезно перечислить явно, потому что каждая обязанность — это отдельный источник отказа и отдельная цель для атаки (см. главу 87 о компрометации оркестратора). В реальной системе одни из этих ролей берёт на себя модель, другие — окружающий её детерминированный код; смешение того и другого — норма.
Первая роль — разбить задачу на подзадачи. Это решение о границах: что считать единицей работы, сколько единиц породить, как распределить между ними входные данные. Когда декомпозиция делается моделью, она недетерминирована — на одном и том же входе оркестратор-агент может в разных запусках разбить задачу по-разному. Это не дефект, а свойство, которое нужно либо принять, либо ограничить схемой (см. главу 31 о декомпозиции).
Ключевой риск роли декомпозитора — порождение перекрывающихся или, наоборот, неполных подзадач. Перекрытие ведёт к тому, что два воркера делают одну работу и их результаты конфликтуют на сборке; неполнота — к дырам, когда часть задачи не покрыта ни одним воркером и тихо выпадает. Оба отказа диагностируются на этапе сборки или, что хуже, не диагностируются вовсе и проявляются в готовом результате (см. главу 20 о зазорах ответственности).
Вторая роль — раздать подзадачи воркерам: создать или выбрать исполнителей, передать каждому его часть, запустить. Здесь оркестратор решает, сколько воркеров запустить одновременно (степень параллелизма) и как сопоставить подзадачи исполнителям (см. главу 33 о диспетчеризации). В простейшем случае соответствие «одна подзадача — один воркер», но при большом числе мелких подзадач разумнее пул воркеров фиксированного размера, разбирающих очередь.
Риск роли диспетчера — нерегулируемый fan-out. Если оркестратор-агент сам решает, сколько воркеров запустить, он может запустить их слишком много: исчерпать лимиты провайдера, насытить квоты, создать стоимостный всплеск. Это требует жёсткого потолка, заданного кодом, а не доверенного решению модели (см. главу 34 о взрыве динамически порождаемой работы и главу 59 о стоимости на масштабе).
Третья роль — снабдить каждого воркера контекстом, достаточным для его части и не больше. Это операция handoff (см. главу 47), и она нетривиальна. Воркер не видит задачу целиком; всё, что он знает, оркестратор обязан вложить ему в стартовый контекст. Недодать — воркер сделает не то, потому что не понял рамок. Передать слишком много — раздуть контекст воркера, повысить стоимость и снизить точность, а заодно расширить поверхность утечки данных между подзадачами (см. главу 88 о least privilege).
Эта роль — место, где теряется больше всего качества. Оркестратор, удерживающий полную картину, склонен считать очевидным то, что воркеру неизвестно. Симметрично, воркер не может запросить недостающее, не нарушив звездообразность топологии. Поэтому контракт handoff — что именно входит в стартовый контекст воркера — должен быть явным и проверяемым (см. главу 18 о контракте агента).
Четвёртая роль — принять результаты воркеров и свести их в один. Это fan-in, и это самая недооценённая роль. Сборка — не конкатенация: результаты могут противоречить друг другу, частично перекрываться, приходить в разном формате, а часть может не прийти вовсе. Сборщик обязан решить, что делать с противоречиями, с дублями, с отсутствующими частями (см. главу 37 об агрегации). От стратегии сборки зависит, выдержит ли система частичный отказ воркеров (см. главу 73).
Пятая роль, которую часто забывают вынести в отдельную, — следить за воркерами во время исполнения: какие завершились, какие зависли, какие превысили бюджет токенов или времени, какие вернули мусор. Без надзора fan-in превращается в неопределённое ожидание: оркестратор ждёт результатов, которые никогда не придут. Надзор — это тайм-ауты, проверки живости и решения о повторе или отбрасывании (см. главу 70 о тайм-аутах и circuit breaker, главу 72 о переподхвате).
Принципиальный выбор архитектуры — какие из пяти ролей возложить на модель, а какие на детерминированный код. Чем больше ролей у модели, тем гибче система и тем менее она предсказуема и тем дороже. Грубое правило: решения, требующие понимания содержания задачи (декомпозиция, сборка с разрешением смысловых противоречий), естественно ложатся на модель; решения, требующие гарантий (потолок параллелизма, тайм-ауты, бюджеты, идемпотентность повторов), должны быть в коде.
Роль | Модель-оркестратор | Код-оркестратор
Декомпозитор | Гибко, недетерминированно, дорого; подходит для открытых задач | Жёсткий набор подзадач; подходит, когда разбиение известно заранее
Диспетчер | Может выбрать число воркеров по содержанию; риск нерегулируемого fan-out | Фиксированный пул; предсказуемая стоимость
Передатчик контекста | Сам решает, что вложить воркеру; риск недо- и передачи | Шаблон handoff по схеме; проверяемо
Сборщик | Разрешает смысловые противоречия; недетерминированно | Слияние по правилам; падает на неожиданных конфликтах
Надзиратель | Плохо подходит: модель не должна решать про тайм-ауты | Тайм-ауты, бюджеты, повторы — только код
Распространённая и устойчивая конфигурация — гибрид: модель отвечает за декомпозицию и смысловую сборку, код владеет диспетчеризацией, надзором и потолками. Эта разметка возвращается во всех последующих частях, и именно она отделяет рой, выдерживающий production, от роя, работающего только на демонстрации.