English translation is not available yet. Showing Russian content.

Как вы делаете агента «забывающим» (для GDPR / privacy compliance)?

Краткий тезис

Агент «забывает» через механизм explicit forget — явное удаление данных пользователя из всех хранилищ памяти (буфер диалога, векторная БД, кэш LLM, логи). Ключевые требования: полное и необратимое удаление, логирование факта удаления для аудита, невозможность восстановления. Для compliance с GDPR (право на забвение) и другими privacy-регуляциями агент должен гарантировать, что после запроса пользователя его данные недоступны ни в каком виде.


1. Термины и контекст

GDPR (General Data Protection Regulation) — европейский регламент о защите персональных данных. Статья 17 даёт пользователю право на забвение (right to erasure): компания обязана удалить все персональные данные по запросу.

Privacy compliance — соответствие системы требованиям privacy-регуляций (GDPR, CCPA, и др.). Для AI-агентов это означает контроль над тем, какие данные о пользователе хранятся, как долго и как удаляются.

«Забывающий» агент — агент, который может по команде удалить всю информацию о конкретном пользователе из своей памяти (как краткосрочной — buffer memory, так и долгосрочной — vector memory, эмбеддинги, логи).

Memory (память агента) — хранилище данных, которые агент использует для персонализации и контекста. Делится на:

  • Buffer memory — история последних сообщений (обычно в оперативной памяти или Redis).
  • Vector memory — эмбеддинги и документы, сохранённые в векторной БД (например, Chroma, Pinecone, Qdrant) для долгосрочного контекста.
  • LLM cache — кэш ответов LLM, который может содержать персональные данные.
  • Audit log — журнал действий агента, часто содержит user_id, запросы, ответы.

Explicit forget — явный вызов функции удаления данных по идентификатору пользователя. В отличие от implicit forget (автоматическое забывание через TTL или перезапись), explicit forget гарантирует немедленное и полное удаление.


2. Почему агенты особенно уязвимы с точки зрения privacy

Обычная RAG-система хранит документы в векторной БД, но редко хранит историю диалогов. Агент же:

  • Сохраняет историю диалогов (buffer memory) для поддержания контекста.
  • Сохраняет персонализированные эмбеддинги (например, предпочтения пользователя, факты о нём) в vector memory.
  • Логирует все вызовы инструментов (tool calls), которые могут содержать персональные данные (например, email, адрес).
  • Кэширует ответы LLM, где могут быть упомянуты персональные данные.

Если агент не умеет «забывать», то:

  • Данные остаются в системе навсегда.
  • При запросе другого пользователя (или при атаке) можно извлечь чужие данные.
  • Нарушается GDPR, штрафы до 4% годового оборота.

3. Explicit forget: механизм удаления из памяти

Процесс состоит из четырёх шагов:

  1. Идентификация пользователя — агент должен знать, какие данные принадлежат какому user_id.
  2. Удаление из buffer memory — очистка истории диалогов для данного user_id.
  3. Удаление из vector memory — удаление всех документов и эмбеддингов, помеченных этим user_id.
  4. Удаление из кэша и логов — очистка LLM cache и audit log (с учётом требований к аудиту — см. раздел 5).

Важно: удаление должно быть каскадным — если данные дублируются в нескольких хранилищах, нужно удалить везде.


4. Техническая реализация (пример на Python)

Допустим, агент использует LangChain с ConversationBufferMemory и векторную БД Chroma.

import chromadb
from langchain.memory import ConversationBufferMemory
from langchain.schema import HumanMessage, AIMessage

class ForgetfulAgent:
    def __init__(self, user_id: str):
        self.user_id = user_id
        # Buffer memory — храним историю в dict по user_id
        self.buffer_memory = {}  # user_id -> list of messages
        # Vector memory — клиент Chroma
        self.chroma_client = chromadb.PersistentClient(path="./chroma_db")
        self.collection = self.chroma_client.get_or_create_collection(
            name="agent_memory",
            metadata={"hnsw:space": "cosine"}
        )
        # Audit log — простой файл
        self.audit_log_path = "audit.log"

    def add_to_buffer(self, message: str, role: str):
        if self.user_id not in self.buffer_memory:
            self.buffer_memory[self.user_id] = []
        self.buffer_memory[self.user_id].append(
            {"role": role, "content": message}
        )

    def add_to_vector(self, text: str, metadata: dict):
        # Добавляем документ с метаданными, содержащими user_id
        self.collection.add(
            documents=[text],
            metadatas=[{**metadata, "user_id": self.user_id}],
            ids=[f"{self.user_id}_{uuid.uuid4()}"]
        )

    def forget_user(self):
        """Explicit forget: удалить все данные пользователя."""
        # 1. Очистить buffer memory
        if self.user_id in self.buffer_memory:
            del self.buffer_memory[self.user_id]

        # 2. Удалить из vector memory (Chroma)
        # Получаем все ID документов пользователя
        results = self.collection.get(
            where={"user_id": self.user_id}
        )
        if results["ids"]:
            self.collection.delete(ids=results["ids"])

        # 3. Очистить LLM cache (если есть)
        # Например, инвалидировать кэш по префиксу user_id
        # cache.invalidate(user_id=self.user_id)

        # 4. Записать в audit log (см. раздел 5)
        self._log_forget()

    def _log_forget(self):
        with open(self.audit_log_path, "a") as f:
            f.write(f"{datetime.utcnow().isoformat()} | FORGET | user_id={self.user_id} | status=success\n")

Важные детали

  • Векторная БД должна поддерживать фильтрацию по метаданным (where clause). Chroma, Pinecone, Qdrant — поддерживают.
  • Если используется shared memory (несколько пользователей в одной коллекции), нужно строго помечать каждый документ user_id.
  • Для buffer memory в production используйте Redis с TTL и возможностью удаления по ключу.

5. Логирование для audit

GDPR требует, чтобы компания могла доказать факт удаления данных. Для этого нужно логировать:

  • user_id (или псевдоним)
  • timestamp удаления
  • action (например, "FORGET")
  • scope (какие хранилища были очищены)
  • status (success / partial / failed)

Логи должны быть append-only (нельзя перезаписывать) и защищены от изменения. Хранить их можно в отдельной защищённой БД или в SIEM-системе.

Пример записи

2025-03-15T10:[[30. Как вы проверяете, что fine-tuned модель не сломала базовые способности|30]]:00Z | FORGET | user_id=abc123 | scope=buffer,vector,cache | status=success

Важно сам лог не должен содержать персональные данные (только идентификатор). Иначе лог тоже придётся удалять при forget — но тогда пропадёт доказательство. Компромисс: хранить лог с псевдонимом и хешем user_id, а сам user_id удалить.


6. Невозможность восстановления

GDPR требует, чтобы удаление было необратимым. Это означает:

  • Жёсткое удаление (hard delete) — данные физически удаляются из хранилища, а не помечаются как удалённые (soft delete).
  • Перезапись — для дисковых хранилищ рекомендуется перезаписывать сектора, чтобы данные нельзя было восстановить через forensic tools.
  • Криптографическое забвение (cryptographic erasure) — данные шифруются ключом, привязанным к пользователю. При forget ключ удаляется, и данные становятся нечитаемыми. Это быстрее, чем физическое удаление, и подходит для облачных БД.

Пример криптографического забвения

from cryptography.fernet import Fernet

# Генерируем ключ для пользователя при регистрации
user_key = Fernet.generate_key()
cipher = Fernet(user_key)

# Шифруем данные перед сохранением
encrypted_data = cipher.encrypt(b"personal data")

# При forget удаляем ключ
del user_key  # или удаляем из хранилища ключей
# Теперь encrypted_data невозможно расшифровать

7. Compliance и юридические аспекты

  • Сроки: GDPR требует удалить данные в течение 30 дней (в некоторых случаях — немедленно). Агент должен поддерживать как синхронный forget (по запросу), так и асинхронный (batch-удаление по расписанию).
  • Уведомление пользователя: после forget агент может сообщить пользователю, что данные удалены (но не обязан, если это нарушает другие законы).
  • Исключения: если данные нужны для юридических обязательств (например, финансовые транзакции), forget может быть частичным. Нужно чётко документировать такие исключения.
  • Data Protection Impact Assessment (DPIA): для агентов с памятью рекомендуется провести DPIA, чтобы оценить риски.

8. Проблемы и компромиссы

ПроблемаОписаниеРешение
Потеря персонализацииПосле forget агент не помнит пользователя, качество падаетПредложить пользователю «мягкий» forget (удалить только часть данных)
Shared memoryЕсли данные пользователя используются для обучения общей модели (например, fine-tuning), удалить их сложноИспользовать федеративное обучение или не хранить персональные данные в обучающей выборке
Audit log vs forgetЛог должен быть неизменным, но может содержать user_idХранить в логе только хеш user_id, а сам user_id удалять
CostПолное удаление из векторной БД может быть дорогим (перестроение индекса)Использовать криптографическое забвение или TTL
Distributed systemsДанные могут быть реплицированы на несколько нодИспользовать distributed delete с гарантией согласованности

9. Альтернативные подходы

  • Data minimization — не хранить персональные данные вообще. Агент работает анонимно, память только сессионная (удаляется при закрытии сессии).
  • Анонимизация — перед сохранением заменять персональные данные на псевдонимы (например, "User_123"). При forget удаляется только маппинг.
  • Дифференциальная приватность — добавлять шум в память агента, чтобы отдельные записи нельзя было восстановить.
  • TTL (time-to-live) — автоматическое удаление данных через заданное время (например, 30 дней). Это implicit forget, но может не удовлетворять GDPR (пользователь может потребовать немедленного удаления).

Пет-проект для закрепления

Задача: Реализовать агента-помощника с памятью, который поддерживает explicit forget по user_id.

Инструменты: Python, LangChain (или простой класс), ChromaDB (векторная БД), Redis (buffer memory), logging.

Шаги:

  1. Создайте класс ForgetfulAgent с методами add_to_buffer, add_to_vector, forget_user.
  2. Используйте ConversationBufferMemory для истории диалогов (или свой dict).
  3. В векторной БД храните документы с метаданными user_id.
  4. Реализуйте forget_user: удаление из buffer, удаление из vector по where={"user_id": ...}, очистка кэша.
  5. Добавьте audit log в отдельный файл.
  6. Напишите тесты:
    • Добавьте данные для пользователя A и B.
    • Вызовите forget для A.
    • Проверьте, что данные A отсутствуют, а данные B остались.
    • Проверьте, что в audit log есть запись о forget.

Ожидаемый результат: Рабочий агент, который после вызова forget_user("user_A") не может восстановить данные A ни из buffer, ни из vector, ни из кэша. Audit log содержит timestamp и user_id.


Связь с другими вопросами

ВопросТема
393Как проектировать память агента (buffer vs vector)
395Как обеспечить безопасность агента (prompt injection, data leakage)
396Как тестировать агента на соответствие GDPR
397Как реализовать data minimization в агентах
398Как использовать шифрование для памяти агента
399Как обрабатывать запросы на удаление данных в multi-tenant системе

Навигация