3,583 papers
arXiv:2610.04195 73 3 окт. 2026 г. FREE

MemLeak: как общая векторная память агента отдаёт один пользователь другому его заметки

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

TL;DR

MemLeak показывает, как память ИИ-агента, общая для многих пользователей, сама, без взлома, подмешивает чужие заметки в ответ. Агент превращает запрос в вектор (набор чисел, отражающий смысл), ищет в общем хранилище три самые похожие по смыслу заметки и кладёт их в контекст модели. Хранилище проверяет сходство по смыслу, а не то, чьи это заметки. Поэтому запрос руководителя про повышение зарплаты находит заметку сотрудника про оффер конкурента.

Главная находка неприятная: такой ответ выглядит лучше обычного. Модель получает конкретные цифры и детали, отвечает точнее и полезнее, а чужие данные в нём явные. Проверка «ответ хороший?» утечку не ловит, она её поощряет. Утечка при этом не зависит от «близости» людей: заметки соседа по отделу и сотрудника из другого департамента попадают в выдачу почти одинаково часто. Атакующему не нужно знать точные запросы жертвы. Ему достаточно писать заметки на ту же тему и в той же роли.

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

🔬

Схема метода

КАК ПРОИСХОДИТ УТЕЧКА (без всякой атаки)
ШАГ 1: запрос Боба → вектор
ШАГ 2: поиск топ-3 по сходству в ОБЩЕМ хранилище → в выдаче заметки Алисы
ШАГ 3: заметки Алисы попадают в контекст модели → ответ Бобу с данными Алисы

ЧТО СРАБОТАЛО
Раздельные хранилища на каждого пользователя → утечка 0%
Жёсткий фильтр владельца ПОСЛЕ поиска (отбросить всё чужое) → заражение ответов минимально, на обеих моделях

ЧТО СРАБОТАЛО ЧАСТИЧНО ИЛИ НЕ СРАБОТАЛО
Фильтр «подозрительных» заметок → пропускает часть чужого
Метка пользователя в тексте перед векторизацией → немного снижает
Сдвиг векторов по пользователям → делает хуже
🚀

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

Задача: в компании на 800 человек запустили HR-ассистента с «долгой памятью». Он помнит, что обсуждали сотрудники. Руководитель отдела разработки спрашивает: «Как мне вести разговор о повышении Ивану?» А в общем хранилище лежит заметка самого сотрудника, где он писал про оффер от конкурента и свои зарплатные ожидания. Вы не пишете код сами, но собираетесь поручить аудит код-агенту (Claude Code, Cursor).

Промпт:

Ты — инженер по безопасности данных. Вот код нашего HR-ассистента с памятью 
(папка /assistant). Память хранится в одной векторной базе для всех сотрудников, 
у записей есть поле user_id.

Задача:
1. Найди, где выполняется поиск по памяти и где результаты попадают в промпт модели.
2. Проверь: фильтр по user_id стоит ДО поиска, ПОСЛЕ поиска или нигде?
3. Добавь жёсткий фильтр ПОСЛЕ поиска: из найденных записей отбрасывай все, где 
   owner != id текущего пользователя. Если общий доступ задуман (проектные 
   пространства), разрешай только записи из явного списка пространств пользователя.
4. Напиши тест: положи в память пользователя A запись с уникальной фразой 
   «КАНАРЕЙКА-742», сделай запрос от пользователя B по той же теме 
   и убедись, что фразы нет ни в выдаче, ни в ответе.

Результат: агент покажет, где в коде ищется память и стоит ли там проверка владельца. Затем он предложит правку: фильтр сразу после поиска и тест с «канарейкой». Вам нужно прогнать тест и подтвердить, что чужая фраза не просачивается.

🧠

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

Слабость. Векторный поиск отвечает на вопрос «что похоже по смыслу?», а не «что можно показывать этому человеку?». Заметки двух коллег с одной ролью и словарём (зарплата, бюджет, уязвимости) лежат рядом. Любая метаданная вроде user_id в такой схеме остаётся мягкой подсказкой, которую поиск может обойти.

Сильная сторона. Модель добросовестно использует всё, что лежит в контексте. Она не отличает «свои» факты от «чужих». Поэтому чужие данные делают ответ точнее, и именно в этом опасность.

Как фильтр обходит слабость. Проверка владельца в коде после поиска не зависит ни от сходства, ни от модели. Чужое просто не доходит до контекста. Модель не может раскрыть то, чего не видела. Фильтр добавляет около 1,4 мс на запрос.

Рычаги: - Хранилище по пользователю → полная изоляция, но теряется задуманный общий доступ. - Жёсткий фильтр после поиска → оставляет общую базу и закрывает утечку. - Явный список разрешённых общих пространств → нужен там, где обмен между людьми задуман (проекты, HR, безопасность). - Количество записей в выдаче (top-k) → чем больше, тем шире окно для чужих записей.

📋

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

Это не промпт из статьи, а прикладная форма её вывода: поручение код-агенту внедрить жёсткий фильтр владельца.

Ты — инженер по безопасности данных. Проверь и исправь доступ к памяти агента.

<Контекст>
Проект: {путь_к_проекту}
Хранилище памяти: {тип_хранилища}
Как задан владелец записи: {поле_владельца}
Разрешённые общие пространства: {общие_пространства_или_нет}


<Шаги>
1. Найди место, где выполняется поиск по памяти.
2. Найди место, где найденные записи попадают в промпт модели.
3. Определи: проверка владельца стоит до поиска, после или отсутствует.
4. Добавь жёсткую проверку ПОСЛЕ поиска: запись проходит, только если
   владелец = текущий пользователь ИЛИ запись из {общие_пространства}.
5. Напиши тест с канарейкой: уникальная фраза в памяти пользователя A,
   запрос от пользователя B по той же теме. Фразы быть не должно ни в выдаче,
   ни в ответе.
6. Опиши, что осталось незакрытым.

Подставьте путь к проекту, тип хранилища (например, Chroma, pgvector), поле, где хранится владелец, и список общих пространств, если они есть.

🚀 Быстрый старт — вставь в чат или в код-агента:

Вот шаблон аудита памяти агента. Адаптируй под мой проект: [опиши, как устроена твоя память]. 
Задавай вопросы, чтобы заполнить поля.

[вставить шаблон выше]

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

⚠️

Ограничения

⚠️ Малая выборка: по 10 запросов на условие, всё на синтетических заметках, написанных вручную. Авторы сами называют работу доказательством существования проблемы, а не оценкой реальной частоты. Цифры вроде «70–100%» не переносите на свою компанию.

⚠️ Вывод не про промпт: защита сделана в коде хранилища, а не в тексте инструкции. Статья не проверяла, помогает ли фраза в системном промпте вроде «не раскрывай чужие данные». Не считайте, что она заменит фильтр.

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

⚠️ Фильтр «подозрительности» и правки векторов: эвристическая проверка пропускает заметную часть чужого. Сдвиг векторов по пользователям делал только хуже. Для надёжной векторной привязки к пользователю нужно обучать отдельную модель, а это уже вне ваших возможностей.

⚠️ Мониторинг качества не поможет: загрязнённые ответы часто выглядят полезнее чистых. Ловить утечку по оценке ответов нельзя, нужны специальные тесты.

🔍

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

Авторы из Workday построили четыре пары вымышленных пользователей: соседи по команде, один отдел, разные департаменты, разные компании (контроль). У каждого по 10 заметок на чувствительные темы: зарплаты, уязвимости, кадровые решения. На каждого по 10 запросов. Поиск проверяли в двух вариантах: простом словарном (TF-IDF) и «как в продакшене» (небольшая модель эмбеддингов MiniLM). Каждый вариант прогоняли в общем хранилище и в раздельных, каждому пользователю своём.

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

Ответы генерировали Gemini 2.5 Flash и Claude Sonnet 4.5, они же ставили оценки заражения и полезности. Для проверки судьи два автора вручную разметили девять ответов. Судья скорее занижал утечку, поэтому цифры считаются нижней границей.

Что удивило: «организационная близость» людей почти не предсказывает утечку. Заметно ниже она только между разными компаниями. Ещё один эксперимент, попытка передавать скрытые биты через размещение заметок, провалился. Так что угроза в основном случайная утечка, а не тайный канал. Для практики вывод такой: изоляцию нельзя строить на «мы же в одной команде» или на мягких метках владельца.

💡

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

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

Под аккаунтом A добавь в память уникальную фразу «КАНАРЕЙКА-742 — премия отдела закупок 340 тыс. ₽». Под аккаунтом B (другая роль, та же тема) задай три вопроса про премии и бюджеты закупок. Если фраза или её детали появились в ответе, память не изолирована. Жалуйтесь вендору или отключайте общую память.

🔗

Ресурсы

  • MemLeak: Cross-User Semantic Leakage in Multi-Tenant AI Agent Memory
  • Авторы: Priyanka Mudgal, Kai Zhao, Guilin Zhang, Andy Olsen, Ezekiel Miller, Xu Chu, Aletta Johanna Blanken (Workday AI Research)
  • Воркшоп NeurIPS 2026: Personalized, aligned, long-term memory for AI systems
  • Упомянутые системы памяти: Mem0, A-Mem, MemOS, MemGPT

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

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

Парадокс: когда агент подмешивает чужие заметки, его ответ выглядит лучше обычного. Поэтому проверка «ответ хороший?» утечку не ловит, а поощряет. Жёсткий фильтр по владельцу после поиска позволяет оставить общую память и не отдавать одному пользователю заметки другого. Хранилище ищет по смыслу и не знает, чьи это заметки, поэтому запрос руководителя про повышение зарплаты находит заметку сотрудника про оффер конкурента. Взлом не нужен. Фильтр в коде отсекает всё чужое до того, как оно попадёт в контекст модели, и заражение ответов падает до минимума на обеих проверенных моделях.

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

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

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

Векторный поиск отвечает на вопрос «что похоже?». Заметки двух коллег с одной ролью и словарём (зарплата, бюджет, уязвимости) лежат рядом. Поле user_id для такого поиска остаётся мягкой подсказкой, которую он обходит. Модель в этой схеме добросовестна и вредна одновременно: она использует всё, что лежит в контексте, и не отличает свои факты от чужих. Чужие цифры делают ответ точнее, в этом и опасность. Ещё одна неприятная деталь: заметки соседа по отделу и сотрудника из другого департамента попадают в выдачу почти одинаково часто. Атакующему не нужно знать запросы жертвы, ему хватает заметок на ту же тему и в той же роли. Фильтр в коде не зависит ни от сходства, ни от модели: чужое не доходит до контекста, а модель не раскроет то, чего не видела. Цена фильтра около 1,4 мс на запрос. Раздельные хранилища на каждого пользователя тоже дают утечку 0%. Мягкие меры слабее: фильтр «подозрительных» заметок пропускает часть чужого, метка пользователя в тексте помогает слегка, а сдвиг векторов по пользователям делает хуже.

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

Безопасность агентов → ассистенты с долгой памятью на много пользователей (HR, поддержка, внутренние боты компании), особенно когда все заметки лежат в одной векторной базе. Для общих пространств (проекты, HR, безопасность) нужен явный список, иначе фильтр сломает задуманный обмен. НЕ подходит как замена код-защите: статья не проверяла, помогает ли фраза в системном промпте вроде «не раскрывай чужие данные». Выборка маленькая: по 10 запросов на условие, заметки синтетические. Реальную частоту утечки в вашей компании цифры из статьи не покажут.

Мини-рецепт

1. Найди дыру: попроси код-агента показать, где идёт поиск по памяти и где найденное попадает в промпт модели.
2. Проверь, где стоит замок: фильтр по владельцу до поиска, после или его нет совсем.
3. Поставь охранника на выходе: после поиска запись проходит, только если владелец равен текущему пользователю или запись из разрешённого общего пространства.
4. Подбрось канарейку: положи в память пользователя A уникальную фразу <фраза>КАНАРЕЙКА-742. Спроси от пользователя B по той же теме.
5. Смотри в двух местах: фразы не должно быть ни в выдаче поиска, ни в ответе модели.
6. Прогони тест сам: код-агент напишет тест, но подтвердить, что чужое не просачивается, должен ты.
7. Выпиши остатки: пусть агент перечислит, что осталось незакрытым, например спорные общие пространства.

Примеры

[ПЛОХО]: `Добавь в системный промпт: не раскрывай данные других сотрудников` [ХОРОШО]: `Найди в коде поиск по памяти. После поиска оставляй только записи, где owner равен id текущего пользователя, либо записи из списка разрешённых общих пространств. Напиши тест: фраза КАНАРЕЙКА-742 из памяти пользователя A не должна появиться ни в выдаче, ни в ответе пользователю B.` [ПЛОХО]: `Проверь, что ответы HR-ассистента точные и полезные` [ХОРОШО]: `Руководитель спрашивает, как вести разговор о повышении Ивану. Проверь, не попала ли в выдачу заметка самого Ивана про оффер конкурента. Если попала, поставь жёсткий фильтр владельца после поиска.`
Источник: MemLeak: Cross-User Semantic Leakage in Multi-Tenant AI Agent Memory
ArXiv ID: 2610.04195 | Сгенерировано: 2026-10-06 05:00

Проблемы LLM

ПроблемаСутьКак обойти
Модель не отличает «свои» данные от «чужих» в контекстеПодаёшь в контекст заметки, найденные поиском по смыслу. Среди них оказываются записи другого пользователя. Модель использует всё, что видит. Она не спрашивает, можно ли это показывать. Поиск по смыслу тоже не проверяет, чьи это записи. Чужое просачивается в ответ без всякой атаки. Проблема касается любой общей базы: память агента, поиск по документам, общая историяНе проси модель «не раскрывай чужое». Убирай чужое до того, как оно попало в контекст. После поиска отбрось все записи, где owner != текущий пользователь. Если общий доступ задуман, оставляй записи только из явного списка разрешённых пространств

Методы

МетодСуть
Жёсткий фильтр владельца после поиска, плюс проверка «канарейкой»Поиск вернул записи. Перед вставкой в контекст проверь код-проверкой: запись принадлежит этому пользователю или разрешённому общему пространству. Остальные выбрось. Почему работает: модель не может раскрыть то, чего не видела. Проверка в коде не зависит ни от смысла запроса, ни от модели. Стоит почти ничего по времени. Как проверить: положи в данные пользователя A уникальную фразу, например КАНАРЕЙКА-742. Задай вопрос от пользователя B на ту же тему. Фразы не должно быть ни в найденных записях, ни в ответе. Когда да: общая база на многих людей, личные или рабочие данные. Когда не нужно: база и так раздельная на каждого пользователя. Оговорка: если общий доступ нужен по задумке, одного сходства по смыслу мало. Нужен явный список того, что можно делить. Помогает и меньшее число записей в выдаче: окно для чужого уже
📖 Простыми словами

MemLeak: Cross-User Semantic Leakage in Multi-TenantAIAgentMemory

arXiv: 2610.04195

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

Это как зайти в офисную курилку и вслух спросить, кому пора поднять зарплату. А стоящий рядом стажёр тут же достаёт из кармана чужой дневник с оффером от конкурента и радостно зачитывает его вслух, ведь тема-то совпала. Ассистент не пытается навредить, он просто работает как болтливая сплетница, у которой напрочь отсутствует понимание чужих границ.

Исследователи назвали эту уязвимость MemLeak, и ломать систему ради неё не нужно. Алгоритм просто переводит запрос в эмбеддинг и тащит в контекст топ-3 самые похожие заметки со всего сервера. Руководитель спрашивает про повышение сотрудника, а база подмешивает черновик самого работника с планами уволиться, потому что словарь совпал: грейды, пересмотр, оклад. Банальный user_id при кривой архитектуре остаётся лишь слабой рекомендацией, на которую поиск легко забивает.

Тестировали механику на корпоративном ассистенте компании на 800 человек, но грабли абсолютно универсальны. Та же дыра появится везде, где общее хранилище делят разные люди: чаты поддержки, CRM-системы или код-агенты вроде Cursor. Если память общая, а фильтрация смысловая, то чужие секреты лежат на расстоянии одного запроса.

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

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

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

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