24. Data и Privacy¶
Зачем это нужно?¶
Агент работает с персональными данными пользователей (email, телефон, адрес). Эти данные попадают в логи и отправляются в LLM API. Без защиты данных вы нарушаете GDPR и рискуете утечкой данных.
Реальный кейс¶
Ситуация: Агент обрабатывает запрос пользователя: "Мой email john@example.com, телефон +7-999-123-4567. Создай тикет".
Проблема: PII попадает в логи, трейсы и вывод инструментов без маскирования. При утечке логов персональные данные попадают в чужие руки.
Решение: Обнаружение и маскирование PII на границе логов, трассировок и вывода инструментов (вход модели не трогаем — агенту нужны данные), redaction по умолчанию, защита секретов, TTL для хранения.
Junior employee model. Data privacy для агента — это те же data-policies, что для нового сотрудника. Он подписывает NDA, не пересылает клиентские email-ы наружу, не складывает скриншоты с PII в публичный канал, удаляет персональные данные после отчётного периода (retention). Агент так же: не пишет PII в спаны/логи как есть (см. Гл. 19, правило про redacted-атрибуты), не передаёт лишнее в LLM-провайдера, удаляет данные по TTL. Принципиально нового тут ничего нет — это адаптация знакомых GDPR-практик к коду. Подробная рамка — Предисловие → Ментальная модель и Гл. 17: Security и Governance.
Теория простыми словами¶
Что такое PII?¶
PII (Personally Identifiable Information) — это данные, которые позволяют идентифицировать человека: email, телефон, адрес, паспорт.
Что такое redaction?¶
Redaction — это удаление чувствительных данных из логов перед сохранением.
Как это работает (пошагово)¶
Шаг 1: Обнаружение и маскирование PII — на границе, не на входе модели¶
Вход модели НЕ маскируем. Если пользователь пишет «Мой email john@example.com, создай тикет» — агенту нужен этот email, чтобы тикет создать.
sanitizePIIна входе модели деградирует задачу: агент видит[EMAIL_REDACTED]и не может выполнить запрос. Маскирование применяется на границе: логи, трейсы, вывод инструментов — везде, где данные покидают процесс и попадают в хранилище, которое могут прочитать.
Сама функция маскирования выглядит так:
import "regexp"
func sanitizePII(text string) string {
// Маскируем email
emailRegex := regexp.MustCompile(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b`)
text = emailRegex.ReplaceAllString(text, "[EMAIL_REDACTED]")
// Маскируем телефон (российский и международный формат)
phoneRegex := regexp.MustCompile(`[\+]?[78]\s?[\(-]?\d{3}[\)-]?\s?\d{3}[-]?\d{2}[-]?\d{2}`)
text = phoneRegex.ReplaceAllString(text, "[PHONE_REDACTED]")
return text
}
Примечание: Регулярки выше — упрощённый пример для обучения. В production используйте специализированные библиотеки: Microsoft Presidio для PII detection, truffleHog или detect-secrets для поиска секретов в коде. Они покрывают десятки форматов данных и регулярно обновляются.
Шаг 2: Защита секретов¶
Никогда не логируйте секреты:
func sanitizeSecrets(text string) string {
// Удаляем паттерны типа "password: ..."
secretRegex := regexp.MustCompile(`(?i)(password|api_key|token|secret)\s*[:=]\s*[\w-]+`)
text = secretRegex.ReplaceAllString(text, "[SECRET_REDACTED]")
return text
}
Шаг 3: Redaction логов¶
Удаляйте чувствительные данные из логов:
func logWithRedaction(runID string, data map[string]any) {
sanitized := make(map[string]any)
for k, v := range data {
if str, ok := v.(string); ok {
sanitized[k] = sanitizePII(sanitizeSecrets(str))
} else {
sanitized[k] = v
}
}
logJSON, _ := json.Marshal(sanitized)
log.Printf("AGENT_RUN: %s", string(logJSON))
}
Где это встраивать в нашем коде¶
Точка интеграции: граница логов, а не вход модели¶
В labs/lab05-human-interaction/main.go вход пользователя уходит в модель как есть — агенту нужны данные для задачи. Маскирование — в момент записи в лог/трейс:
// Вход модели НЕ маскируем — иначе агент потеряет email клиента
messages = append(messages, llm.Message{Role: "user", Content: rawInput})
// Маскируем на границе логов/трейсов (redacted-атрибуты — гл. 19)
logWithRedaction(runID, map[string]any{"user_input": rawInput})
Границы редакции: сводка¶
| Граница | Что делаем |
|---|---|
| Вход модели | Не маскируем — маскирование входа деградирует задачу |
| Логи | Redaction по умолчанию: санитизация перед записью (Шаг 3) |
| Трейсы | Redacted-атрибуты + хэш для корреляции; реализация в спанах — гл. 19 |
| Вывод инструментов | Маскирование по умолчанию: инструмент не должен возвращать PII/секреты в открытом виде (гл. 16, гл. 17) |
| Хранение | TTL/retention для логов и трейсов (Ошибка 3 ниже; гл. 19 пишет трейсы в то же хранилище) |
| Сборка | Секреты — из vault/CI-секретов в момент сборки и деплоя, не из кода и не из env образа |
| GDPR, право на удаление | Лог с correlation ID (хэш идентификатора пользователя) → все трейсы/логи пользователя находятся поиском по ID и удаляются по запросу |
Пара пояснений к таблице:
- Vault при сборке. API-ключи и пароли не живут в
main.goи не запекаются в образ. CI берёт их из vault (или GitHub/GitLab secrets) и выдаёт агенту в рантайме. Тогда утечка образа ≠ утечка секретов. - GDPR и correlation ID. «Забыть меня» требует найти все записи о пользователе. Если каждый лог и трейс несёт
user_hash = hash(userID), удаление — это один запрос к хранилищу по этому ID, а не археология по логам.
Типовые ошибки¶
Ошибка 1: PII попадает в логи¶
Симптом: Email и телефоны пользователей видны в логах.
Решение: Маскируйте PII перед логированием.
Ошибка 2: Секреты логируются¶
Симптом: API ключи и пароли попадают в логи.
Решение: Удаляйте секреты из логов через redaction.
Ошибка 3: Нет Data Retention Policy¶
Симптом: Логи растут бесконечно, дисковое пространство заканчивается. Старые логи содержат PII, но никто их не чистит.
Причина: Нет политики хранения данных. Логи и трассировки записываются без TTL и ротации.
Решение:
// ПЛОХО: логи без ограничения хранения
func writeLog(entry LogEntry) {
file.Write(entry) // Файл растёт бесконечно
}
// ХОРОШО: TTL и ротация
type RetentionPolicy struct {
MaxAge time.Duration // Максимальный срок хранения
MaxSizeMB int // Максимальный размер в MB
}
func (p *RetentionPolicy) Cleanup(logDir string) error {
entries, _ := os.ReadDir(logDir)
for _, entry := range entries {
info, _ := entry.Info()
if time.Since(info.ModTime()) > p.MaxAge {
os.Remove(filepath.Join(logDir, entry.Name()))
}
}
return nil
}
Ошибка 4: Отсутствие шифрования в Transit¶
Симптом: Данные между агентом и LLM API передаются по незащищённому каналу. Man-in-the-middle атака перехватывает запросы с PII.
Причина: HTTP вместо HTTPS, отсутствие TLS-верификации, самоподписанные сертификаты без проверки.
Решение:
// ПЛОХО: HTTP без шифрования
client := &http.Client{}
resp, _ := client.Post("http://api.llm.example.com/v1/chat", ...)
// ХОРОШО: HTTPS + проверка сертификатов
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
},
}
resp, _ := client.Post("https://api.llm.example.com/v1/chat", ...)
Ошибка 5: PII в трассировках¶
Симптом: OpenTelemetry трассировки содержат пользовательские данные. Dashboards и алерты показывают email и телефоны.
Причина: Span-атрибуты и логи добавляются без фильтрации.
Решение: Это каноническое правило курса по редакции: redaction по умолчанию на границе трейсов. Реализация в OpenTelemetry-спанах (redacted-атрибуты + хэш для корреляции) — гл. 19:
// ПЛОХО: PII попадает в span-атрибуты
span.SetAttributes(
attribute.String("user.input", userMessage), // Содержит PII!
)
// ХОРОШО: санитизация перед добавлением в трассировку
span.SetAttributes(
attribute.String("user.input", sanitizePII(userMessage)),
attribute.String("user.input_hash", hashForCorrelation(userMessage)),
)
Мини-упражнения¶
Упражнение 1: Детектор PII¶
Реализуйте детектор PII, который находит email и телефоны в тексте и возвращает список найденных совпадений:
type PIIMatch struct {
Type string // "email", "phone"
Value string // Найденное значение
Start int // Позиция начала
End int // Позиция конца
}
func detectPII(text string) []PIIMatch {
// Реализуйте поиск email и телефонов
// Поддержите форматы: user@example.com, +7-999-123-4567, 8 (999) 123-45-67
}
Ожидаемый результат:
- Находит email-адреса в произвольном тексте
- Находит телефоны в разных форматах (с +7, 8, скобками, дефисами)
- Возвращает позиции совпадений для точечной замены
Упражнение 2: Middleware для Redaction логов¶
Создайте middleware, который автоматически маскирует PII во всех логах агента:
type RedactionMiddleware struct {
next slog.Handler
patterns []RedactionPattern
}
type RedactionPattern struct {
Name string
Regex *regexp.Regexp
Replace string
}
func NewRedactionMiddleware(next slog.Handler) *RedactionMiddleware {
return &RedactionMiddleware{
next: next,
patterns: []RedactionPattern{
{Name: "email", Regex: regexp.MustCompile(`\b[\w.+-]+@[\w.-]+\.\w{2,}\b`), Replace: "[EMAIL]"},
{Name: "phone", Regex: regexp.MustCompile(`[\+]?[78]\s?[\(-]?\d{3}[\)-]?\s?\d{3}[-]?\d{2}[-]?\d{2}`), Replace: "[PHONE]"},
},
}
}
func (m *RedactionMiddleware) Handle(ctx context.Context, r slog.Record) error {
// Реализуйте фильтрацию всех атрибутов записи
}
Ожидаемый результат:
- Все строковые атрибуты проходят через redaction
- Паттерны легко расширяются (добавить ИНН, паспорт, карту)
- Middleware прозрачен для остального кода
Критерии сдачи / Чек-лист¶
Сдано:
- Вход модели не маскируется (агент получает данные задачи)
- PII маскируется на границе логов/трейсов (redaction по умолчанию)
- Вывод инструментов маскируется по умолчанию
- Секреты не логируются и выдаются из vault при сборке, не из кода
- Логи и трейсы хранятся с TTL/retention
- Есть correlation ID для GDPR-права на удаление
Не сдано:
- PII попадает в логи/трейсы в открытом виде
- Вход модели маскируется (агент теряет данные задачи)
- Секреты логируются или запечены в образ
Связь с другими главами¶
- Предисловие → Ментальная модель — Базовая рамка: data-policies для агента — это адаптация data-policies для людей.
- Глава 17: Security и Governance — Threat modeling, RBAC и onboarding-дисциплина для агента; data privacy — частный случай этой рамки.
- Глава 19: Observability и Tracing — Безопасное логирование: правило «PII не попадает в спан в открытом виде», redacted-атрибуты + защищённое хранилище по корреляционному ID.
Что дальше?¶
После понимания Data и Privacy переходите к:
- 25. Production Readiness Index — Оцените готовность вашего агента к продакшену