18+
реклама
18+
Бургер менюБургер меню

Ранас Мукминов – Оркестрация ИИ-агентов. Claude Opus 4.7 (страница 32)

18

Сборка — это не обратная операция к разбиению. Разбиение делит целое на части по областям; сборка обязана восстановить целое из частей, которые могли измениться независимо и потерять взаимную согласованность. Между этими операциями прошло параллельное исполнение, и его недетерминизм означает, что собранный результат нужно проверять как целое, а не доверять ему на основании того, что каждая часть по отдельности корректна (см. главу 37 о верификации итога).

Стратегий несколько, и выбор зависит от природы задачи и от того, что считать правильным результатом.

Стратегия | Когда применима | Главный риск

Конкатенация | Части независимы и просто соединяются (разделы документа, непересекающиеся файлы) | Скрытая несогласованность на стыках частей

Слияние с разрешением конфликтов | Части пересекаются, конфликты возможны и разрешимы | Неверное разрешение; потеря работы при автоматическом выборе

Голосование/кворум | Воркеры решали похожее, нужен консенсус | Коррелированные ошибки проходят большинством (см. главу 39)

Редукция (свёртка) | Результаты агрегируются в сводку (суммирование, ранжирование) | Потеря деталей; искажение при свёртке

Выбор лучшего | Спекулятивное исполнение, нужен один результат из многих | Критерий выбора недетерминирован или подвержен манипуляции

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

Отдельный вопрос — что делать, когда пришли не все результаты. Часть воркеров зависла, была отброшена по тайм-ауту или вернула мусор. У сборщика два пути: ждать всех (и тогда один медленный воркер задерживает весь результат, а один упавший — блокирует его навсегда без надзора) или собирать из того, что есть (graceful degradation, см. главу 73). Второй путь требует, чтобы система заранее знала, какие части обязательны, а какие можно опустить: отсутствие необязательной части даёт неполный, но полезный результат; отсутствие обязательной означает провал всей задачи.

Это решение нельзя отложить на момент сборки — оно должно быть встроено в декомпозицию как разметка критичности подзадач. Без неё сборщик не отличает «не пришла второстепенная деталь» от «не пришла половина результата» и либо тихо выдаёт неполноту за полный результат, либо проваливает задачу из-за несущественной потери. Разметка критичности — это контракт между декомпозитором и сборщиком, и она замыкает три фазы в единое целое.

Тихое проглатывание потерь. Сборщик принимает то, что пришло, не проверяя, всё ли пришло. Дыры из разбиения и отброшенные воркеры дают неполный результат, который выглядит полным. Это самый частый и самый опасный отказ fan-in. Защита — сверка собранного с ожидаемым: сборщик должен знать, сколько частей он ждёт и какие из них обязательны.

Неверное разрешение конфликта. При слиянии пересекающихся результатов сборщик выбирает неверную версию или комбинирует несовместимые. Защита — минимизация конфликтов на разбиении (непересекающиеся области изменения) и явная, а не неявная стратегия разрешения там, где конфликты неизбежны.

Сборщик как новое узкое место. Если сборка тяжёлая — например, сборщик-агент перечитывает все результаты воркеров целиком, — она сериализует то, что исполнялось параллельно, и съедает выигрыш (см. главу 57). На пределе вся экономия от параллельного исполнения уходит в стоимость и время сборки. Защита — иерархическая сборка: результаты сводятся не одним сборщиком разом, а деревом частичных сборок (это уже шаг к иерархии, см. главу 9).

Потеря согласованности целого. Каждая часть корректна по отдельности, но вместе они несогласованны — следствие ложной независимости из разбиения. Защита — верификация итога как целого отдельным шагом после сборки, а не доверие к корректности по индукции от частей.

Паттерн раскрывается, когда совпадают несколько условий. Задача естественно делится на независимые части с непересекающимися областями. Частей достаточно много, чтобы параллелизм окупал координационный налог, но не настолько, чтобы взорвать стоимость. Результаты частей складываются в целое предсказуемым образом — конкатенацией, слиянием по понятным правилам или свёрткой. Подзадачи примерно равны по объёму, так что воркеры завершаются примерно одновременно и пул не простаивает в ожидании одного отстающего. При этих условиях fan-out/fan-in даёт почти линейный выигрыш по времени при умеренном росте стоимости — лучший исход, на который вообще можно рассчитывать в оркестрации.

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

Стоит назвать и противоположный полюс — задачи, у которых форма обманчиво подходит под fan-out, а суть нет. Если элементы выглядят независимыми, но связаны общим неявным контекстом, который меняется по ходу работы, плоское разбиение разорвёт эту связь и каждый воркер примет своё локальное решение, несовместимое с решениями соседей. Признак такой задачи — невозможность сформулировать самодостаточный критерий готовности подзадачи без ссылки на результаты других подзадач. Если критерий готовности части неизбежно ссылается на соседей, независимости нет, как бы убедительно она ни выглядела, и паттерн даст ложную независимость на сборке.

Граница применимости проходит по четырём осям, и пересечение любой из них — сигнал, что нужна другая топология.

Зависимости между подзадачами. Если результат одной подзадачи нужен другой, чистый fan-out/fan-in неприменим: воркеры не видят друг друга. Слабые зависимости спасаются волнами параллелизма по графу (см. главу 32); сильные и последовательные требуют конвейера (см. главу 10). Попытка втиснуть зависимую задачу в плоский fan-out даёт ложную независимость и рассогласование на сборке.

Глубина. Если задача требует не одного уровня разбиения, а вложенного — подзадачи сами разбиваются на под-подзадачи, — плоский оркестратор перегружается: один узел держит слишком широкое дерево. Естественное продолжение — рекурсия: воркер становится оркестратором своего поддерева (см. главу 9 об иерархиях). Глубина — это не отказ паттерна, а его развитие в иерархию, и у неё своя цена координации.

Неравномерность подзадач. Если подзадачи сильно различаются по объёму, пул простаивает: K-1 воркеров закончили и ждут одного тяжёлого. Параллелизм вырождается в скорость самого медленного воркера. Это случай для динамической диспетчеризации и балансировки (см. главу 33), а в пределе — для пересмотра декомпозиции, разбивающей тяжёлые подзадачи мельче.

Дорогая или недетерминированная сборка. Если результаты не складываются предсказуемо — требуют сложного согласования, многократного пересмотра, разрешения смысловых конфликтов, — сборка становится узким местом и съедает выигрыш. Это сигнал, что задача плохо ложится на fan-in: возможно, она требует доски для постепенного совместного построения результата (см. главу 11) или дебатов для согласования (см. главу 64), а не одномоментной сборки.

Горизонтальное масштабирование fan-out/fan-in — увеличение числа воркеров — упирается в потолок раньше, чем подсказывает интуиция, и причина в последовательной доле (см. главу 56, главу 60). Разбиение, диспетчеризация и сборка не параллелятся: их делает оркестратор последовательно. Чем больше воркеров, тем больше эта последовательная доля относительно параллельной работы, и тем меньше отдача от каждого следующего воркера. На некотором числе воркеров добавление новых перестаёт ускорять результат и начинает только увеличивать стоимость и нагрузку на оркестратор — точка убывающей отдачи (см. главу 60).

Второй потолок — сам оркестратор. Он единственный держит полную картину, единственный делает разбиение и сборку, единственный надзирает за всеми воркерами. С ростом ширины растёт его нагрузка: больше контекста удерживать, больше результатов сводить, больше воркеров отслеживать. В пределе оркестратор перестаёт справляться, и его перегрузка — это перегрузка SPOF, отказ которого роняет весь рой (см. главу 75). Лечится это либо иерархией, разносящей нагрузку по дереву оркестраторов, либо уменьшением ширины до управляемой.

Отсюда практический вывод: fan-out/fan-in масштабируется вширь хорошо в пределах десятков воркеров на одного оркестратора, ориентировочно, и плохо — за этим пределом без перехода к иерархии. Точная граница зависит от тяжести разбиения и сборки, но сам факт существования границы не зависит от реализации: это структурное свойство звездообразной топологии с единым центром.

Значение fan-out/fan-in не в том, что он покрывает все случаи — он их не покрывает, — а в том, что он задаёт точку отсчёта. Любую более сложную топологию полезно описывать как отклонение от него: иерархия добавляет рекурсию, конвейер добавляет последовательные зависимости, доска убирает центр и вводит косвенную координацию, рынок заменяет диспетчеризацию торгами. Понимая, какое именно ограничение базового паттерна снимает каждая из них и какой ценой, инженер выбирает топологию осознанно, а не по моде (см. главу 15 о выборе топологии). И почти всегда правильный порядок рассуждения — начать с fan-out/fan-in и отступить от него ровно настолько, насколько вынуждает задача, не дальше.