Ранас Мукминов – Оркестрация ИИ-агентов. Claude Opus 4.7 (страница 45)
Contract-net — старейший и наиболее цитируемый протокол распределения задач через торги; он формализован в исследованиях распределённого ИИ задолго до появления языковых агентов и описывает именно разнесение оценки и присуждения. В терминах ролей предыдущего раздела contract-net задаёт конкретный сценарий взаимодействия инициатора (manager) и подрядчиков (contractors) и фиксирует словарь сообщений.
Канонический цикл contract-net:
manager -> *broadcast* : task-announcement(task, eligibility, deadline)
contractor -> manager : bid(estimate) // только пригодные отвечают
contractor -> manager : (молчание) // непригодные не отвечают
manager -> winner : award(contract)
manager -> losers : reject (опционально)
winner -> manager : inform-result(result) | failure
Существенные свойства, которые делают contract-net именно протоколом, а не просто идеей аукциона:
Объявление содержит критерий пригодности (eligibility). Manager не просто описывает задачу — он указывает, кто вправе участвовать. Это первичный фильтр: участник, не удовлетворяющий критерию, не тратит ресурс на оценку и не отвечает. Фильтр сокращает число ставок и защищает от шумовых ответов узлов, которые задачу заведомо не потянут.
Ставка добровольна и информативна. Contractor отвечает только если считает себя пригодным; ставка несёт оценку, на основе которой manager выбирает. Молчание — валидный исход, и протокол не требует ответа от всех. Это принципиально для масштаба: при тысяче узлов нельзя ждать тысячу ответов.
Присуждение порождает контракт. Награждение — не просто «выбор», а заключение обязательства: победитель обязуется исполнить, manager обязуется принять результат. Контракт даёт точку для тайм-аутов, отзыва и переаукциона, если исполнитель не справился.
Рекурсивность. Подрядчик, выигравший контракт, может сам стать менеджером для подзадач: декомпозировать полученную работу и разыграть её части на новом раунде торгов. Так contract-net естественно порождает иерархию контрактов (см. главу 9 об иерархиях), но иерархию динамическую, формируемую торгами, а не заданную статически.
Contract-net создавался для распределённых вычислительных узлов, но три его свойства неожиданно хорошо совпадают с природой языковых агентов.
Во-первых, eligibility и bid — это естественно текстовые сообщения. Агент способен прочитать объявление задачи на естественном языке, оценить собственную пригодность рассуждением и сформулировать ставку. Там, где для классических узлов eligibility приходилось кодировать формально, агент справляется с неструктурированным объявлением — ценой риска, что он оценит себя неверно.
Во-вторых, рекурсивность contract-net отражает то, как агенты и так декомпозируют задачи. Агент-подрядчик, получив крупную задачу, может разбить её и делегировать части — это совпадает с паттерном субагентов (см. главу 9). Contract-net лишь добавляет к делегированию механизм выбора исполнителя через торги.
В-третьих, добровольность ставки совпадает с тем, что агент способен отказаться от задачи, для которой не подходит, — если его об этом просят и если он откалиброван. Это большое «если», и оно определяет главную хрупкость рыночных моделей на агентах.
Чтобы рынок был отлаживаемым, сообщения должны быть структурированы, а не свободным текстом (см. главу 25). Иллюстративная схема ставки:
{
"msg": "bid",
"task_id": "T-1042",
"bidder": "worker-7",
"estimate": {
"cost_tokens": 18000,
"confidence": 0.72,
"context_relevance": 0.4,
"current_load": 2
},
"ttl_s": 30
}
Структурированная ставка даёт инициатору машинно-сравнимые поля и оставляет след для последующей трассировки: почему был выбран именно этот участник, на каких числах основано решение. Свободнотекстовая ставка («я хорошо справлюсь с этим») непригодна — её нельзя ни сравнить, ни проверить постмортемом. Поля схемы — это контракт между участником и инициатором; их стабильность важна для долгоживущего роя (см. главу 22 о версионировании).
Чтобы увязать роли, фазы и схему, полезно проследить один раунд на обобщённом, намеренно упрощённом примере. Пусть инициатор имеет подзадачу «извлечь обязательства из набора из сорока договоров и свести их в таблицу». В рое есть несколько воркеров; часть из них в предыдущих подзадачах уже работала с этими же договорами и держит их в контексте, часть — нет. Конкретные числа ниже иллюстративны и служат лишь для наглядности механики, а не как измеренные величины.
Инициатор рассылает дешёвый дайджест: тип задачи, идентификаторы документов, критерий пригодности «доступ к корпусу договоров и инструмент извлечения». Воркеры без доступа молчат — первичный фильтр отсёк их без затрат на оценку. Прошедшие фильтр запрашивают полное описание и оценивают. Воркер A, у которого договоры уже в окне, заявляет низкую стоимость (наполнять контекст не нужно) и умеренную уверенность. Воркер B, которому корпус пришлось бы загружать заново, заявляет высокую стоимость. Воркер C заявляет очень высокую уверенность и низкую стоимость одновременно — сигнал, который инициатор обязан рассматривать с подозрением, а не как лучшую ставку.
Инициатор применяет прозрачную функцию цены, взвешивающую заявленную стоимость, релевантность контекста и текущую загрузку, и сознательно дисконтирует голую заявленную уверенность. Контракт достаётся воркеру A: его низкая стоимость объясняется проверяемым фактом — релевантный контекст уже у него, — а не самооценкой. Остальным уходит явный отказ, освобождающий их зарезервированные ресурсы. Воркер A исполняет работу в пределах тайм-аута; результат проходит критерий приёмки (таблица покрывает все сорок договоров и валидна по схеме), и контракт закрывается. Если бы A превысил тайм-аут или вернул неполную таблицу, контракт был бы отозван и переразыгран — возможно, теперь его взял бы B, чья высокая стоимость оправдана как цена надёжного запасного исполнителя.
Этот проход показывает, ради чего вообще затевается рынок: работа притянулась к узлу, где её исполнение объективно дешевле, и это притяжение возникло из локального знания воркера A о собственном контексте, которого у инициатора не было. Он же показывает, что без дисконта самооценки и без settlement с тайм-аутом и приёмкой тот же механизм легко выбрал бы воркера C и тихо принял бы его правдоподобный, но непроверенный результат.
Рыночная топология добавляет к общим режимам отказа коммуникации (см. главу 30) собственный класс сбоев, специфичный для торгов. Эти режимы нужно проектировать против них с самого начала — они не «крайние случаи», а типичное поведение нечестных или плохо откалиброванных участников.
Главная уязвимость рынка на агентах — ставка отражает не реальную пригодность, а склонность агента к самооценке. Языковые агенты систематически плохо калиброваны: заявленная уверенность слабо коррелирует с фактической вероятностью успеха. В директивной топологии это терпимо, потому что оценку делает оркестратор по объективным признакам. В рыночной — самооценка участника прямо управляет распределением.
Следствие — проклятие победителя (winner's curse). Контракт систематически достаётся не самому пригодному участнику, а самому склонному к завышению уверенности. Агент, который всегда заявляет уверенность 0.95, выиграет все торги, где конкурирует с честным агентом, заявляющим калиброванные 0.7, — и провалит ровно те задачи, где честный отказался бы. Рынок без защиты от этого систематически выбирает худшего исполнителя из тех, кто переоценивает себя сильнее всех.
Защита — не доверять заявленной уверенности как единственному критерию. Опираться на объективные, проверяемые компоненты цены (релевантность контекста, загрузка), а заявленную уверенность либо игнорировать, либо корректировать по истории. Если ведётся репутация (см. ниже), систематический переоценщик со временем штрафуется. Без репутации единственная надёжная защита — обязательная верификация результата, превращающая провал в немедленный отзыв контракта, а не в принятый плохой ответ.
Рынок предполагает конкуренцию ставок. Если участники однородны или их ставки скоррелированы, конкуренция вырождается: все ставят примерно одинаково, и выбор становится произвольным. Для агентов это частый случай: воркеры на одной модели с одним системным промптом дают сильно скоррелированные оценки (см. главу 63 о коррелированных ошибках). Тогда аукцион — дорогая имитация выбора, который с тем же успехом сделал бы случайный выбор.
Это не злонамеренный сговор, а структурное свойство: однородные исполнители не образуют рынка. Диагностика — измерять разброс ставок. Если разброс систематически мал, рыночный механизм не приносит пользы и его стоит заменить директивным назначением.
Если функция цены устойчиво благоволит одному типу участников, остальные перестают выигрывать и голодают (см. главу 33). Хуже: победитель, перегруженный выигранными контрактами, становится узким местом — все торги стянулись к нему, а он не справляется с объёмом. Рынок, оптимизирующий только пригодность и игнорирующий загрузку, сам себя загоняет в монополизацию.
Защита — включать текущую загрузку в цену с растущим штрафом: чем больше контрактов держит участник, тем выше его эффективная ставка. Это превращает рынок в саморегулирующийся балансировщик. Без учёта загрузки рынок концентрирует работу на «лучшем» исполнителе до его насыщения и деградации.