Результат переноса и почему он меняет модель угроз
Состязательные возмущения, оптимизированные против одной модели, регулярно ухудшают работу другой. Это не особенность родственных чекпоинтов. Перенос сохраняется между архитектурами, между токенизаторами и между различными обучающими конвейерами, породившими рассматриваемые модели. Возмущению не нужно знать веса цели; ему достаточно сдвинуть вход в область, где представление цели смещается так, что это меняет выход.
Для приложения на одной модели это неудобство. Для стека агента это структурная проблема. Продакшн-запуск агента не остаётся внутри одной модели. Он маршрутизирует: дешёвая модель для классификации, более крупная для планирования, специализированная для извлечения, третья для финального синтеза. Каждый переход — это новая модель, и каждый переход — это место, куда может повторно войти перенесённое возмущение. Укрепление одной модели в этой цепочке повышает стоимость атаки для этого сегмента и ничего не делает для остальных. Путь остаётся открытым, потому что путь — это и есть цепочка.
Вывод неудобен, но прост: если возмущение переносится, а стек маршрутизирует, то стратегия защиты по каждой модели — это не защита. Это издержки, навязанные защищающейся стороне.
Почему агенты усиливают перенос
В чат-приложении успешное возмущение даёт плохой ответ. В агенте оно даёт плохое действие. Разница — в цикле.
Текст становится инструкцией
Шаг агента порождает текст, который следующий шаг потребляет как инструкцию, а не просто как контекст. Внутри запуска нет жёсткой границы между «выходом модели» и «входом модели». Выход планировщика — это промпт исполнителя. Возмущение, сдвигающее представление планировщика на небольшую величину, сдвигает план, и сдвинутый план затем воспринимается как авторитетный всем, что находится ниже по потоку. Возмущению не нужно выживать при перекодировании в виде строки атаки; оно выживает в виде правдоподобной инструкции.
Аргументы генерируются, а не парсятся
Аргументы инструментов порождаются моделью. Когда внутреннее представление значения в модели сдвигается, испускаемый ею аргумент сдвигается вместе с ним. Если инструмент принимает строки свободной формы, этот сдвиг попадает прямо в вызов. Путь, который был /data/reports/q3, становится /data/reports/q3/../../secrets, или адрес получателя приобретает лишний символ, или числовой порог смещается на порядок. Модель не решала это сделать; она испустила то, что подсказало её сдвинутое представление. С точки зрения инструмента вызов неотличим от легитимного.
Передачи пересекают границы без сетевого события
![]()
Мультиагентные системы усугубляют это. Когда агент A передаёт управление агенту B, возмущение пересекает границу модели внутри того же процесса, без сетевого события, без завершения TLS и без журнала исходящего трафика. Нет места, куда можно поместить сетевой контроль, который увидит передачу, потому что передача — это вызов функции. Каждая модель в рое — потенциальная точка повторного входа, и возмущение может быть повторно усилено на каждой из них.
Защита на границе инструментов
Устойчивая точка контроля — не модель. Это интерфейс между агентом и его инструментами. Этот интерфейс существует независимо от того, какая модель породила вызов, и это последнее место, где сдвинутое представление можно поймать до того, как оно станет эффектом в мире.
Принцип — относиться к модели как к недоверенной генерации входа, потому что это и есть то, чем она является. Модель — это компонент, порождающий кандидатные аргументы. Граница решает, допустимы ли эти кандидаты.
Валидируйте по типизированной схеме, отклоняйте по форме
Каждый вызов инструмента валидируется по явной схеме перед исполнением. Валидация — по форме, а не по намерению. Классификация намерения — это задача модели, и она наследует уязвимости модели; валидация формы — это задача парсера, и она их не наследует. Если схема говорит, что recipient — это строка, соответствующая домену из списка разрешённых, то возмущённый получатель не проходит сопоставление. Границе не нужно знать, почему аргумент неверен. Ей нужно знать, что он не той формы.
TOOL_SCHEMAS = {
"send_report": {
"type": "object",
"required": ["recipient", "report_id", "format"],
"properties": {
"recipient": {"type": "string", "pattern": r"^[^@]+@reports\\.internal$"},
"report_id": {"type": "string", "pattern": r"^RPT-[0-9]{6}$"},
"format": {"type": "string", "enum": ["pdf", "csv"]},
},
"additionalProperties": False,
}
}
Ограничивайте диапазоны и перечисления
Строки свободной формы — это то, где живут возмущения. Каждый аргумент, который может быть перечислением, должен им быть. Каждый числовой аргумент должен нести ограниченный диапазон. Тогда возмущённый аргумент выпадает за пределы допустимого множества и отклоняется по построению, а не классификатором, который сам может быть переносимым.
Требуйте происхождение для каждого аргумента
Каждый аргумент несёт метаданные: какой шаг его породил, из какого источника, под какой моделью. Происхождение не останавливает возмущение, но делает сбой читаемым. Когда происходит отклонение, запись о происхождении показывает, пришёл ли аргумент из извлечённого документа, из реплики пользователя или из предыдущего шага модели. Это различие определяет, где находится исправление —

Отказывайте закрыто и делайте повторную попытку явной
Отклонённый вызов не повторяется молча с ослабленной схемой. Он отказывает закрыто и выдаёт типизированную ошибку. Агент может повторить попытку, но повторная попытка — это намеренный путь с собственным логированием, а не запасной вариант, который тихо расширяет допустимое множество.
Чего это стоит
Компромисс реален, и о нём следует сказать прямо.
Поддержка схем становится первостепенной инженерной задачей с собственным ритмом ревью. У каждого инструмента есть схема, у каждой схемы есть владелец, и каждое изменение схемы — это событие ревью. Это не бесплатно. Это цена наличия точки контроля, которая не зависит от того, какая модель находится в таблице маршрутизации в этом квартале.
Некоторые легитимные вызовы отказывают закрыто. Модель, которая породила бы допустимый, но слегка необычный аргумент, теперь получает отклонение. Путь повторной попытки должен быть явным, наблюдаемым и ограниченным, иначе система будет выглядеть теряющей возможности. Команды, пропускающие этот шаг, обнаруживают, что их доля отклонений низка, но доля завершений ещё ниже.
Покрытие измеряется в валидированных вызовах инструментов, а не в укреплённых промптах. Это иная метрика, чем та, которую отслеживает большинство команд, и она меняет то, что показывают дашборды. Дашборд укрепления промптов может выглядеть здоровым, пока граница инструментов остаётся незащищённой.
Измерение
Граница измерима так, как укрепление модели — нет.
- Внедряйте известные возмущения переноса в стейджинге и фиксируйте долю отклонений на границе. Возмущения генерируются против одной модели и воспроизводятся против маршрутизируемого стека. Метрика — доля, которую граница ловит до исполнения.
- Отслеживайте, сколько отклонений происходит после того, как инструмент уже был вызван в том же запуске. Отклонение, приходящее после того, как эффект уже наступил, — это не контроль; это запись в журнале. Это число следует свести к нулю, и любое ненулевое значение указывает на границу, расположенную слишком поздно в шаге.
- Записывайте распределение происхождения по отклонённым аргументам. Если отклонения кластеризуются на одном источнике, проблема находится выше по потоку от границы, и схема делает свою работу, локализуя её.
Ни одна из этих метрик не требует доступа к весам модели или к процедуре оптимизации атакующего. Они требуют лишь того, чтобы граница существовала и вела журнал.
Стратегический момент в том, что защиты для отдельных моделей — это движущаяся цель против переносимого возмущения. Граница инструментов — это фиксированная цель против того же возмущения, потому что ей не важно, какая модель породила вызов. Ей важно лишь, допустим ли вызов. Это свойство, которое защищающаяся сторона действительно может удерживать.
