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

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

18

Уровень | Что проверяется | Механизм | Стоимость и надёжность

Структура | Форма выхода: схема, типы, обязательные поля | Валидатор схемы, парсер | Дёшево, детерминированно

Синтаксис домена | Выход синтаксически валиден в своей области (код парсится, SQL разбирается, документ собирается) | Компилятор, линтер, парсер домена | Дёшево, детерминированно

Свойства | Выход удовлетворяет проверяемому свойству (сумма сходится, тесты проходят, ссылки разрешаются) | Тесты, проверки инвариантов на данных, исполнение | Умеренно, в основном детерминированно

Согласованность | Выход не противоречит входу и известному состоянию | Сверка с источником, дифф, перекрёстная проверка | Дорого, частично

Семантика | Выход правильный по смыслу, отвечает на поставленный вопрос | Агент-критик, человек, голосование (см. часть IX) | Дорого, недетерминированно

Намерение | Агент действовал добросовестно, без скрытого обхода | Аудит действий, наблюдаемость (см. часть XI) | Очень дорого, частично

Практический вывод из лестницы: проверяемые постусловия нужно проектировать так, чтобы они опускались как можно ниже по этой лестнице. Контракт «вернуть корректный ответ» (семантика) почти непроверяем; контракт «вернуть ответ в схеме X, где поле total равно сумме полей items[].amount, и все упомянутые идентификаторы существуют в переданном каталоге» (структура плюс свойства) проверяется детерминированно. Искусство контракта — превращать семантические обещания в проверяемые свойства везде, где это возможно, и честно помечать остаток как непроверяемый.

Контракт может быть тотальным или частичным по входу. Тотальный контракт обязуется дать корректный выход на любом входе, удовлетворяющем предусловию, — включая редкие и враждебные случаи. Частичный контракт оговаривает, что на части допустимых входов агент вправе отказаться, вернув явный признак неуспеха вместо результата.

Для агентов частичный контракт почти всегда честнее тотального, и это контринтуитивно. Агент, обязанный «всегда дать ответ», на входе, который он не может обработать, не падает с ошибкой, как код, — он галлюцинирует правдоподобный, но неверный результат. Поэтому самым ценным пунктом постусловия часто оказывается не «вернёт правильный ответ», а «либо вернёт ответ в схеме, либо явно вернёт insufficient_context / cannot_comply / needs_escalation». Это превращает молчаливый отказ (который рой воспринимает как успех и распространяет дальше) в наблюдаемое событие, на которое можно отреагировать. Контракт, разрешающий честный отказ, — основа graceful degradation на уровне роли (см. главу 73).

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

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

Проверка на входе потребителя означает, что следующий агент проверяет предусловие — то есть не доверяет тому, что ему передали, и валидирует вход прежде, чем работать. Это прямое следствие zero trust в рое (см. главу 85): каждый стык — граница недоверия. Плюс — защищает потребителя даже от производителя, нарушившего контракт. Минус — дублирование проверок и размывание ответственности (кто виноват, если оба не проверили).

Проверка посредником — отдельный валидирующий узел между производителем и потребителем (часто на blackboard или в шине сообщений, см. главы 11 и 23). Плюс — единое, переиспользуемое место контроля; минус — лишний хоп и потенциальный SPOF.

На практике критичные контракты проверяют дважды — на выходе и на входе, — сознательно платя за дублирование, потому что цена пропуска выше. Дешёвые контракты не проверяют вовсе или проверяют только структуру одним валидатором. Распределять проверки равномерно «для порядка» — значит платить координационный налог без привязки к риску.

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

Молчаливое нарушение постусловия. Агент возвращает результат правильной формы, но неверный по содержанию, а содержательная проверка отсутствует или сведена к структурной. Самый частый и самый коварный режим: схема валидна, JSON парсится, поля на месте — и значение в поле выдумано. Рой принимает структурно-валидный мусор за выполненный контракт и распространяет его. Защита — опускать постусловия по лестнице проверяемости (раздел выше), не путать «валидно по схеме» с «верно по смыслу».

Эрозия предусловия (вход вне контракта без сигнала). Производитель потихоньку начинает отдавать вход, который потребитель не обязывался обрабатывать: новое поле, изменившийся формат, состояние мира вне предположений. Если предусловие не проверяется на входе, потребитель не отказывает, а обрабатывает мусор и выдаёт правдоподобный результат. В долгоживущем рое предусловия эродируют незаметно, и сбой проявляется далеко от места, где контракт был фактически нарушен. Это смыкается с темой совместимости и версионирования (см. главу 22).

Нарушение инварианта в середине работы. Инвариант, проверяемый только постфактум, бесполезен против вреда, причинённого в процессе. Агент с инвариантом «не трогает чужие артефакты», проверенным лишь по финальному диффу, мог в середине перезаписать чужой файл и восстановить его — финал чист, вред причинён (например, конкурирующий агент прочитал испорченную версию). Инварианты должны принуждаться непрерывно — правами и песочницей (см. главы 16, 88), а не проверяться в конце.

Контракт, обойдённый инъекцией. Если часть контракта держится на промпте, а не на внешнем механизме, prompt injection через вход или общую память может заставить агента нарушить собственное обещание, оставаясь при этом «уверенным», что он его соблюдает (см. главу 86). Это фундаментальная причина, по которой инварианты безопасности нельзя оставлять на уровне промпта: единственная надёжная граница — внешняя и принудительная.

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

Перекладывание ответственности через контракт. Социотехнический режим: каждый агент формально соблюдает свой контракт, но на стыке между контрактами есть зазор, за который не отвечает никто (см. главу 20). Контракты сходятся по структуре, но между «что гарантировал producer» и «на что рассчитывал consumer» остаётся непокрытая щель. Сами по себе контракты этот зазор не закрывают — его нужно искать отдельно.

Рой собирается из агентов так же, как программа из функций: выход одного становится входом другого. Условие корректной сборки — совместимость контрактов на стыке. Формально: постусловие producer должно влечь предусловие consumer. Producer гарантирует $P_{out}$; consumer требует $P_{in}$; стык корректен, если $P_{out} \Rightarrow P_{in}$ — всё, что producer обещает на выходе, удовлетворяет тому, что consumer требует на входе. Если это не так, между ними нужен адаптер, либо стык — латентный баг, который выстрелит на входе, попавшем в зазор между гарантией и требованием.

Это переносит на рой классические правила вариантности, и они контринтуитивны ровно так же, как в типах. Чтобы агента можно было безопасно подставить вместо другого (например, заменить реализацию роли, см. главу 17 о специализации и главу 22 о версиях), его контракт должен быть совместим по подстановке: он вправе требовать на входе не больше, чем оригинал (предусловие можно только ослаблять), и обязан гарантировать на выходе не меньше (постусловие можно только усиливать). Агент, который «требует более чистый вход» или «иногда возвращает меньше полей», не является корректной заменой, даже если в типичных случаях работает.

Изменение контракта при замене агента | Безопасно для подстановки | Эффект на стыке

Ослабить предусловие (принимать больше входов) | Да | Старые producer'ы продолжают работать

Усилить предусловие (требовать более чистый вход) | Нет | Producer'ы, дававшие допустимый ранее вход, ломают стык

Усилить постусловие (гарантировать больше) | Да | Consumer'ы получают не меньше, чем рассчитывали

Ослабить постусловие (гарантировать меньше) | Нет | Consumer'ы, рассчитывавшие на гарантию, получают мусор

Эту цепочку рассуждений стоит проследить на коротком составном примере. Пусть конвейер из трёх ролей: разведчик собирает факты, аналитик строит из них вывод, исполнитель вносит изменение. Разведчик гарантирует на выходе «список фактов в схеме, каждый факт снабжён ссылкой на источник» — но не гарантирует, что фактов достаточно для вывода. Аналитик требует на входе именно «факты со ссылками» (совместимо с выходом разведчика) и гарантирует «вывод плюс признак уверенности; при недостатке фактов — явный отказ» (частичный контракт). Исполнитель требует «вывод с уверенностью не ниже порога» и гарантирует необратимое изменение только при выполнении этого предусловия. Стык разведчик—аналитик корректен, потому что постусловие первого влечёт предусловие второго. Стык аналитик—исполнитель корректен только потому, что аналитик честно отказывается при недостатке фактов: без этого пункта исполнитель получил бы низкоуверенный вывод, оформленный как обычный, и применил бы необратимое изменение на слабых основаниях. Видно, что прочность всего конвейера определяется не сильнейшим звеном, а совместимостью на самом слабом стыке и честностью отказа на нём.