3,583 papers
arXiv:2610.16333 77 12 окт. 2026 г. FREE

TRAP / ReplayBench: агент принимает настоящий, но чужой результат инструмента за доказательство для текущей задачи

КЛЮЧЕВАЯ СУТЬ
Агент отменил не тот заказ, хотя ни один инструмент не соврал и никто ничего не взламывал. Фишка: атака без подделки. Ответ инструмента настоящий, он просто от другой задачи. Исследование TRAP (набор тестов ReplayBench) позволяет проверить своего агента на такую подмену: старый ответ из кэша, повтор из очереди, результат после сбоя. У ответа инструмента нет «паспорта», поэтому модель видит только правдоподобный текст и принимает его как свой. Почти все ошибки случаются на шаге, где модель решает, годится ли результат для её задачи.
Адаптировать под запрос
⚡

TL;DR

Исследование показывает: LLM-агент почти не проверяет, к какой задаче относится результат инструмента. Если в контекст агента попал реальный, неизменённый ответ инструмента от другой задачи, от устаревшего состояния или повторно доставленный из очереди, модель обычно использует его как факт. Ответ из кэша, очереди или восстановления после сбоя читается как «свежий» и «мой».

Боль выглядит так. Агент проверил статус заказа №1837, потом получил независимую задачу «отмени заказ №2941». В контекст попал старый результат по №1837, и агент отменяет не тот заказ. Никто ничего не взламывал и не подделывал, ответ инструмента был правдивым. Причина проста: у модели нет «паспорта» результата, она видит только текст. Текст выглядит правдоподобно, значит, модель продолжает работу.

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

📌

Схема цепочки отказа

ШАГ 1: Доставка → реальный результат от другой задачи попал в контекст агента
        (через кэш, очередь или восстановление после сбоя)
ШАГ 2: Генерация действия → модель принимает его как доказательство и формирует
        неверный вызов инструмента   ← ЗДЕСЬ ломается почти всё
ШАГ 3: Приёмка → слой исполнения пропускает действие (нет проверки)
ШАГ 4: Итог → в системе остаётся неверное состояние (не тот заказ отменён, письмо не туда)

Защита: проверка «результат ещё валиден для ЭТОЙ задачи?» ДО шага 3

Четыре вида нарушений, которые проверяли:

T01 Чужой объект    — результат от задачи А применён в задаче Б
T02 Устаревший      — результат был верен, но контекст изменился (права, статус, политика)
T03 Дубль           — уже выполненное действие выполняется повторно
T04 Не по порядку   — результаты применены в порядке, нарушающем цепочку состояний
🚀

Пример применения

Задача: Вы продавец на Ozon и ведёте поддержку через автоматизацию в n8n. Агент по очереди обрабатывает обращения: «проверь статус», «отмени», «верни деньги». В цепочке есть ретраи и очередь. После сбоя сценарий перезапускается и подсовывает агенту старый ответ API по заказу из прошлого обращения. Нужно, чтобы агент не совершал необратимое действие по чужому результату.

Промпт (системная инструкция агенту):

Ты агент поддержки интернет-магазина. Текущая задача: отменить заказ №2941 по
запросу клиента Ивана Петрова.

Правила работы с результатами инструментов:
1. Каждый результат инструмента имеет паспорт: task_id, order_id, state_version,
   result_age. Результат без паспорта считай непроверенным.
2. Перед любым необратимым действием (отмена, возврат, удаление) выпиши в ответе:
   - номер заказа из ТЕКУЩЕЙ задачи: №2941
   - номер заказа из результата, на который опираешься
   - возраст результата и версию состояния
3. Если номера не совпадают, паспорт отсутствует или результат старше 5 минут, НЕ
   выполняй действие. Вызови инструмент get_order_status заново и опирайся
   только на свежий ответ.
4. Если результат пришёл «из ниоткуда» (ты его не запрашивал в этой задаче),
   считай это сигналом тревоги и запроси данные заново.

Результат: Перед отменой агент выведет короткую сверку: номер заказа из задачи, номер из результата, возраст и версию. Если данные расходятся, он остановится и заново запросит статус. Гарантии это не даёт. Статья проверяла только показ полей свежести в самих результатах, а не такую текстовую инструкцию. Но формулировка заставляет модель явно сопоставить два идентификатора, а не молча продолжить.

🧠

Почему это работает (и почему не работает «в лоб»)

Слабость LLM. Модель читает контекст как единый текст. Если в нём есть правдоподобный результат инструмента, она по умолчанию считает его частью текущей работы. Откуда он взялся, модель не видит, потому что у текста нет «паспорта». Исследование показывает: почти все ошибки возникают именно на шаге, где модель решает, годится ли этот результат для её задачи.

Что модель умеет хорошо. Она сверяет явные поля. Когда в результате есть возраст и версия состояния, она начинает замечать устаревшее. В статье при сильно устаревшем результате доля ошибок упала с 100% до 60%. Это улучшение, но не защита.

Как применить. Идея в том, чтобы превратить неявную проверку «моё ли это?» в явное сравнение двух значений. Рычаги: - Паспорт результата (ID задачи, ID объекта, версия, возраст): чем больше полей, тем больше за что зацепиться модели. - Порог свежести (в примере 5 минут): подстройте под скорость изменения ваших данных. - Список «необратимых» действий: чем короче список, тем реже агент тормозит на безопасных операциях. - Повторный запрос вместо доверия: для критичных действий пусть агент всегда запрашивает данные заново.

Главный урок статьи: на модель в этом месте полагаться нельзя. Надёжная защита стоит вне модели: среда выполнения должна проверять, что результат принадлежит этой задаче.

📋

Шаблон промпта

Это моя сборка на основе проверенного в статье (явные поля свежести помогают, но не гарантируют). Саму формулировку инструкции авторы не тестировали.

Ты агент, который выполняет {тип_задач} через инструменты.
Текущая задача: {описание_задачи}. Объект задачи: {id_объекта}.

Правила работы с результатами инструментов:
1. У каждого результата ожидается паспорт: {поля_паспорта}.
   Результат без паспорта считай непроверенным.
2. Перед любым необратимым действием ({список_необратимых_действий})
   выпиши в ответе:
   - объект текущей задачи
   - объект из результата, на который опираешься
   - возраст и версию результата
3. Остановись и запроси данные заново, если:
   - объекты не совпадают
   - паспорта нет
   - результат старше {порог_свежести}
   - ты не запрашивал этот результат в текущей задаче
4. Уже выполненное необратимое действие повторно не выполняй.
   Сначала проверь текущее состояние объекта.

Что подставлять: - {тип_задач}: поддержка, финансы, почта, CRM, облачные файлы. - {поля_паспорта}: ID задачи, ID объекта, версия состояния, время получения. - {список_необратимых_действий}: отмена, возврат, удаление, отправка письма, выдача прав. - {порог_свежести}: время, за которое ваши данные могут измениться.

Паспорт должен добавлять ваша автоматизация, например поле в узле n8n или в ответе инструмента. Сама модель его не создаст. Если такой возможности нет, оставьте пункт 2 и пункт 3 (кроме условий про паспорт).

⚠️

Ограничения

⚠️ Промпт не заменяет защиту в среде выполнения: Полностью ошибки в тестах убрала только проверка валидности в самой среде. Инструкции модели и пометки свежести снижают риск, но не закрывают его.

⚠️ Явные пометки не гарантируют: Для свежих и среднего возраста результатов ошибки оставались на максимуме независимо от того, были ли поля свежести видны. Помогало только при сильно устаревших данных, и то частично.

⚠️ Устойчивость сильно зависит от модели: Одни модели ошибались почти всегда, другие почти никогда. Если вы выбираете модель для агента с необратимыми действиями, проверьте её на своём сценарии с подсунутыми результатами.

⚠️ «Хорошая» защита от одного вида подмены не защищает от других: Одна из моделей отвергала результаты от другого объекта, но принимала устаревшие, повторные и пришедшие не по порядку. Сверка ID не спасает от остальных видов подмены.

⚠️ Тестовые условия: Всё проверено в песочнице, по 10 запусков на конфигурацию. Часть результатов в предоставленном тексте оборвана (изменения среды, сравнение с prompt injection, детали защиты). Эти выводы я здесь не пересказываю.

⚠️ Читателю без кода часть вывода недоступна: Сама защита (привязка результата к контексту) строится в коде агентной системы. В чате и в инструкции агенту доступна только «страховочная» часть.

🔍

Как исследовали

Исследователи придумали атаку, не требующую ничего взламывать: берём настоящий неизменённый результат инструмента из одной задачи и в нужный момент подсовываем его другой. Для этого собрали ReplayBench: 19 сценариев (почта, календарь, платежи, облачные файлы, управление доступом, инциденты), четыре вида нарушений и три способа доставки (хранилище событий, очередь, кэш сессии). Агенту никто не говорил делать ошибку, он просто продолжал работу. Результат проверял независимый скрипт, который смотрел на итоговое состояние песочницы.

Проверили восемь моделей на трёх агентных фреймворках, обычно по 10 запусков на конфигурацию. При подмене результата от другой задачи в среднем 65% запусков заканчивались неверным итоговым состоянием, а 38% конфигураций (64 из 168) ломались во всех запусках. Если брать весь набор нарушений, доля ошибок доходила до 99%.

Дальше идёт самое интересное. 74% разброса объясняется выбором модели: самая уязвимая ошибалась почти всегда (~97%), самая устойчивая почти никогда (~2%). Фреймворк объясняет меньше 1%, то есть смена LangGraph на LlamaIndex ничего не меняет. Разбор по стадиям показал, что результат доходит до агента почти всегда, а ошибка возникает именно в момент, когда модель решает, использовать его или нет.

Удивили три вещи. Во-первых, схожесть объектов не влияла: путаница не зависела от того, похожи ли идентификаторы. Во-вторых, модель, которая отвергала подмену «чужого объекта», на других нарушениях ломалась на 100%: она умеет сверять ID, но не проверяет актуальность и порядок. В-третьих, пометки свежести помогают лишь частично.

Практический вывод: не надейтесь, что модель сама заметит подмену. Закладывайте проверку вне модели, а модели оставьте роль вторичной страховки.

💡

Адаптации и экстраполяции

🔧 Техника: вынести сверку в отдельный шаг → дополнительный барьер. Это экстраполяция, в статье не проверялась. Вместо одной инструкции дайте агенту два прохода.

Шаг 1 (проверка): Перед действием заполни таблицу:
| что требует задача | что утверждает результат | совпадает? |
Не вызывай инструменты изменения, пока все строки не «да».

Шаг 2 (действие): Выполняй только если проверка пройдена.

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

🔧 Техника: ручной «красный тест» своей автоматизации → найти дыры до продакшена.

Я подсуну в твою задачу результат инструмента, который относится к другому
объекту. Если ты увидишь расхождение, остановись и объясни, на каком поле
заметил. Если не увидишь, продолжай как обычно.

Идея взята из дизайна исследования: подмена реального результата и проверка итогового состояния. В автоматизации сами подсуньте старый ответ API и посмотрите, что сделает агент.

🔗

Ресурсы

  • Работа: TRAP: Tool-result Replay Attack for Probing Misbinding in LLM Agents
  • Авторы: Yutong Zhi, Jie Zhang, Xiuqi Lu, Jiayuan Xu, Jing Shao
  • Организации: Tsinghua University, Shanghai AI Laboratory
  • Код и бенчмарк: https://github.com/Phitooo/Replaybench
  • Упомянутые инциденты: отчёт LangGraph.js о загрязнении состояния между потоками; CVE-2026-25536 в MCP TypeScript SDK (ответы уходят не тому клиенту)

📋 Дайджест исследования

Ключевая суть

Агент отменил не тот заказ, хотя ни один инструмент не соврал и никто ничего не взламывал. Фишка: атака без подделки. Ответ инструмента настоящий, он просто от другой задачи. Исследование TRAP (набор тестов ReplayBench) позволяет проверить своего агента на такую подмену: старый ответ из кэша, повтор из очереди, результат после сбоя. У ответа инструмента нет «паспорта», поэтому модель видит только правдоподобный текст и принимает его как свой. Почти все ошибки случаются на шаге, где модель решает, годится ли результат для её задачи.

Принцип работы

Цепочка отказа простая. Результат другой задачи попадает в контекст. Модель принимает его за доказательство и вызывает не тот инструмент. Слой исполнения пропускает действие, потому что проверок нет. В системе остаётся неверное состояние. Авторы проверяли четыре вида подмены: - Чужой объект: результат задачи А применили в задаче Б. - Устаревший: результат был верен, но права или статус уже изменились. - Дубль: действие выполнили второй раз. - Не по порядку: результаты применили в порядке, который ломает цепочку состояний. Промпт помогает превратить молчаливое «это моё?» в явное сравнение двух номеров. Перед необратимым действием агент выписывает номер объекта из задачи и номер из результата. Это как кассир, который сверяет фамилию в паспорте с фамилией в билете. Без сверки он просто верит бумажке.

Почему работает

Модель читает контекст как один сплошной текст. Откуда взялся кусок, она не видит. Поэтому правдоподобный результат по умолчанию считается частью текущей работы. Явные поля работают, потому что модель умеет сверять то, что написано на виду. В статье при сильно устаревшем результате доля ошибок упала со 100% до 60%. Это улучшение, но не защита. Для свежих и среднего возраста результатов ошибки оставались на максимуме, видны поля или нет. Полностью ошибки убрала только проверка в самой среде выполнения, а не в промпте. Она до действия сверяет, что результат относится к текущей задаче и состоянию. Устойчивость сильно зависит от модели: одни ошибались почти всегда, другие почти никогда. Выбор фреймворка почти ничего не меняет. Одна модель отвергала чужие объекты, но принимала устаревшие, повторные и пришедшие не по порядку.

Когда применять

Агенты с необратимыми действиями → отмена заказа, возврат денег, удаление, отправка письма, выдача прав. Особенно когда в цепочке есть кэш, очередь, повторные попытки или перезапуск после сбоя. Именно там старый ответ API подсовывается в новую задачу. Промпт-сверка даёт частичную страховку, если в вашей автоматизации (например, в n8n) есть место добавить поля результата. Без этих полей оставьте только пункты про сверку объектов и про запрос данных заново. НЕ подходит как единственная защита для денег и данных. Здесь нужна проверка в коде. Инструкцию авторы не тестировали, они проверяли только показ полей свежести.

Мини-рецепт

1. Добавь паспорт в коде: пусть автоматизация клеит к каждому результату ID задачи, ID объекта, версию состояния и время получения. Модель сама паспорт не создаст.
2. Перечисли необратимое: отмена, возврат, удаление, отправка письма. Чем короче список, тем реже агент тормозит на безопасных операциях.
3. Заставь сверить вслух: перед таким действием агент выписывает объект задачи, объект из результата, возраст и версию.
4. Задай порог свежести: например, 5 минут. Подбери под то, как быстро меняются твои данные.
5. Запрети доверять сомнительному: нет паспорта, объекты не совпали, результат старый или его не запрашивали в этой задаче. Тогда агент запрашивает данные заново.
6. Закрой дубли: уже выполненное необратимое действие повторно не делать, сначала проверить текущее состояние.
7. Поставь замок в среде выполнения: проверка «результат валиден для этой задачи?» до исполнения действия. Это единственное, что в тестах убрало ошибки полностью.
8. Прогони свою модель: подсунь ей чужие, старые и повторные результаты на своём сценарии.

Примеры

[ПЛОХО] : Ты агент поддержки. Отмени заказ №2941 по запросу клиента. (В контексте лежит старый ответ по заказу №1837. Агент молча верит ему и отменяет не тот заказ.)
[ХОРОШО] : Ты агент поддержки. Текущая задача: отменить заказ №2941. Перед отменой выпиши: номер заказа из задачи, номер заказа из результата, возраст результата и версию состояния. Если номера не совпадают, паспорта нет или результат старше 5 минут, действие не выполняй. Вызови get_order_status заново и опирайся только на свежий ответ. Результат, который ты не запрашивал в этой задаче, считай сигналом тревоги. (Агент видит расхождение №1837 и №2941 и останавливается. Гарантии нет, это страховка поверх кода, а не замена ему.)
Источник: TRAP: Tool-result Replay Attack for Probing Misbinding in LLM Agents
ArXiv ID: 2610.16333 | Сгенерировано: 2026-10-15 00:10

Проблемы LLM

ПроблемаСутьКак обойти
Агент принимает настоящий, но чужой результат инструмента за свойРезультат инструмента попадает в контекст из старой задачи, из очереди или после сбоя. Он правдивый и не подделан. Но относится к другому объекту или к старому состоянию. Модель видит только текст. Откуда он взялся, она не знает. Правдоподобный текст читается как свежий и свой. Итог: агент отменяет не тот заказ или шлёт письмо не туда. Проблема касается любых агентов с необратимыми действиями. Подмена бывает четырёх видов: чужой объект, устаревший результат, повтор уже сделанного действия, нарушенный порядокНе полагайся на модель. Главную проверку делай в коде: перед действием сверяй, что результат относится к этой задаче и к текущему состоянию. В запросе это лишь частичная защита: добавь паспорт результата и сверку перед необратимым действием (см. метод ниже). Одна сверка не закрывает все четыре вида подмены. Модель, которая отсекает чужие объекты, может пропускать устаревшие и повторные результаты. Проверяй свою модель на подсунутых результатах всех четырёх видов

Методы

МетодСуть
Паспорт результата и письменная сверка перед необратимым действием — снижает риск, но не убираетЧто делать. Пусть ваш код добавляет к каждому результату инструмента поля: task_id, object_id, state_version, result_age. Сама модель паспорт не создаст. В инструкции агенту напиши: «Перед необратимым действием (отмена, возврат, удаление, отправка) выпиши: объект текущей задачи, объект из результата, возраст и версию результата. Если объекты не совпадают, паспорта нет или результат старше {порог}, не действуй. Запроси данные заново». Добавь два правила. Результат, который ты не запрашивал в этой задаче, считай тревожным сигналом. Уже выполненное действие повторно не выполняй, сначала проверь состояние. Почему работает: без паспорта модель молча продолжает работу. С паспортом у неё есть два значения для сравнения. Письменная сверка заставляет сопоставить их явно, до вызова инструмента. Границы. Проверено только наличие полей свежести в результатах. Эффект частичный: для сильно устаревших результатов доля ошибок падает примерно со 100% до 60%. Для свежих и среднего возраста результатов поля почти не помогают. Саму формулировку инструкции никто не проверял. Когда применять: цепочки с очередями, повторными попытками, кэшем, где есть необратимые действия. Держи список необратимых действий коротким, иначе агент будет тормозить на безопасных операциях. Порог свежести подбирай под скорость изменения ваших данных. Когда не хватит: если ошибка дорогая. Тогда нужна проверка в коде, а запрос остаётся дополнительным слоем
📖 Простыми словами

TRAP:Tool-result Replay Attack for Probing Misbinding inLLMAgents

arXiv: 2610.16333

AI-агенты тупо не умеют проверять, кому именно принадлежит ответ от инструмента. Если модель дернула функцию и увидела валидный ответ API, она не сопоставляет ID сессии или пользователя. Для неё любой реальный результат в контексте — свежий факт под текущую задачу. Это фундаментальная дыра: модель страдает ошибкой связывания (misbinding) и жрёт чужие данные не глядя.

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

Авторы исследования назвали эту уязвимость TRAP (Tool-result Replay Attack). Фишка в том, что никакой хитрый промпт-инъектор тут даже не нужен — достаточно просто подсунуть старый ответ API после сбоя или ретрая задачи. Модель не сомневается: если статус «заказ отменен» или «возврат одобрен» всплыл в контексте, агент принимает его за чистую монету и завершает действие, создавая необратимый финансовый факап.

Тестировали на базовых сценариях, но принцип универсален. Собираешь саппорт-бота на n8n, настраиваешь очереди задач или держишь агрессивное кэширование — ты в зоне риска. Везде, где есть повторная доставка сообщений, агент легко перепутает контекст и привяжет статус пятиминутной давности по прошлому клиенту к новому человеку.

Короче: LLM-агентам нельзя доверять валидацию состояния. Хватит надеяться, что нейросеть «сама сообразит по контексту» сверить номера заказов. Зашивай проверку соответствия ID инструмента и запроса жестко в код бэкенда, иначе автоматизация устроит тебе аттракцион щедрости за твой счет.

Работа с исследованием

Адаптируйте исследование под ваши задачи или создайте готовый промпт на основе техник из исследования.

0 / 2000
~0.5-2 N-токенов ~10-30с
~0.3-1 N-токенов ~5-15с