Логи диалогов: что хранить и как соблюсти приватность
Логи диалогов с корпоративным AI-ассистентом — это одновременно самый ценный и самый опасный актив, который у тебя появляется после запуска сервиса. Ценный — потому что по логам видно, чем реально пользуются сотрудники, где модель ошибается, куда уходит нагрузка. Опасный — потому что в чат с ассистентом люди пишут то же, что написали бы коллеге: черновики контрактов, куски персональных данных клиентов, внутренние пароли «чтобы модель проверила синтаксис», медицинские данные сотрудников при обсуждении больничных. Если логи хранятся без продуманной политики, ты рискуешь превратить AI-сервис в самую большую утечку данных в истории компании.
Разберём, что стоит хранить, что не стоит, и как выстроить хранение логов на srv-ai01 так, чтобы соблюсти и приватность, и требования по разбору инцидентов.
Зачем вообще хранить логи диалогов
Прежде чем резать логи «для приватности», стоит понять, зачем они нужны:
- разбор инцидентов — если агент выдал вредный или ошибочный ответ, нужно понять, на основе какого промпта;
- аудит безопасности — обнаружение попыток prompt injection, экспорта чувствительных данных, злоупотреблений;
- улучшение качества — анализ, какие запросы модель обрабатывает плохо, чтобы дообучать промпты или переключать модель;
- учёт использования — тот самый подсчёт токенов и активности по отделам.
Для каждой цели набор нужных данных разный, и это важно: для учёта токенов не нужно хранить текст диалога, а для аудита безопасности не нужно хранить его вечно.
Уровни детализации логов
Разумно вести не один общий лог, а несколько слоёв с разной степенью детализации и разным сроком хранения.
Уровень 1 — метаданные (хранить долго, 12+ месяцев):
timestamp, user_id, model, token_count, latency_ms, status_code,
plugin_used (web_search / code_interpreter / none)
Уровень 2 — содержимое диалога (хранить ограниченно, 30-90 дней):
полный текст запроса и ответа, с маскированием PII
Уровень 3 — security-события (хранить долго, 12+ месяцев):
сработавшие фильтры prompt injection, попытки доступа
к запрещённым инструментам, аномалии по rate limit
Такое разделение позволяет держать полный текст диалогов недолго — ровно столько, сколько нужно для разбора инцидентов и улучшения качества, — а метаданные и security-события хранить дольше, потому что они сами по себе не содержат чувствительного контента.
Маскирование персональных данных в логах
Прежде чем логировать полный текст диалога даже на 30-90 дней, стоит прогнать его через маскирование PII. Простой вариант — regex-based фильтр для очевидных паттернов:
import re
PII_PATTERNS = {
"email": r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
"phone": r"\+?\d{1,3}[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}",
"card": r"\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b",
}
def mask_pii(text: str) -> str:
for label, pattern in PII_PATTERNS.items():
text = re.sub(pattern, f"[{label.upper()}_REDACTED]", text)
return text
Для более надёжного результата регулярки стоит дополнить отдельной небольшой NER-моделью (даже локальной, вроде spaCy с русской моделью), которая распознаёт имена, адреса и другие сущности, которые сложно поймать регулярным выражением. Маскирование лучше применять на этапе записи лога, а не постфактум — если сырые данные хотя бы раз попали на диск в незамаскированном виде, «постфактум» уже не поможет.
Хранение и доступ к логам
Логи с содержимым диалогов должны храниться отдельно от общей инфраструктуры логирования компании (той же, куда льются логи веб-сервера или CI/CD), потому что доступ к ним требует более узкого круга людей.
# структура на srv-ai01
/var/log/ai-service/
metadata/ # уровень 1, права 640, широкий доступ для DevOps
conversations/ # уровень 2, права 600, доступ только security-team
security-events/ # уровень 3, права 640, доступ DevOps + security
Для ротации и автоматического удаления логов уровня 2 по истечении срока хранения подойдёт простой logrotate или cron-задача:
# /etc/cron.d/ai-conversation-retention
0 3 * * * root find /var/log/ai-service/conversations -type f -mtime +90 -delete
Отдельно стоит подумать про право сотрудника на удаление собственных диалогов по запросу — это уже вопрос не только технический, но и юридический, особенно если компания работает с персональными данными под 152-ФЗ или аналогичным регулированием. Технически проще всего реализовать это через привязку каждого лога к user_id и написать простой скрипт удаления по запросу.
Что не стоит хранить вообще
- сырые API-ключи и пароли, которые пользователь мог случайно вставить в чат — их стоит детектить тем же PII-фильтром и вырезать до записи, а не после;
- вложенные файлы целиком в общем логе — если пользователь прикрепил документ, разумнее хранить хэш и метаданные файла, а не его содержимое вместе с остальными логами;
- содержимое диалогов из сессий, помеченных как «приватные» или «инкогнито», если такой режим у тебя предусмотрен в Open WebUI — в этом случае не должно оставаться и следа в уровне 2, только агрегированные метаданные уровня 1.
Согласие пользователей и прозрачность
Отдельный, часто упускаемый момент — сотрудники должны заранее знать, что их диалоги с AI-ассистентом логируются, в каком объёме и на какой срок. Это не только вопрос доверия внутри компании, но и юридическая необходимость, если политика обработки персональных данных требует информированного согласия. Практическое решение — короткое уведомление при первом входе в Open WebUI с ссылкой на политику хранения логов, и режим «приватного чата» для случаев, когда сотруднику нужно обсудить с ассистентом что-то заведомо не подлежащее длительному хранению.
# баннер согласия, показывается один раз при первом входе
ENABLE_SIGNUP_CONSENT_BANNER=true
CONSENT_BANNER_TEXT=Диалоги с ассистентом сохраняются для аудита
безопасности и повышения качества сервиса. Полный текст диалогов
хранится 90 дней. Подробнее: itpages.lab.local/policies/ai-logging
Режим приватного чата стоит реализовать так, чтобы в него не попадали метаданные уровня 2 вообще, но при этом сохранялись базовые метаданные уровня 1 (сам факт обращения, количество токенов) — это нужно для честного учёта нагрузки на srv-ai01, не раскрывая содержимого. Важно явно предупреждать, что приватный режим не отменяет фильтры безопасности: попытки prompt injection или запросы к запрещённым инструментам всё равно должны логироваться в security-events, даже из приватной сессии, иначе этот режим превращается в дыру для обхода контроля.
Экспорт логов для внешнего аудита
Если компания подпадает под внешний комплаенс-аудит (ISO 27001, SOC 2 или отраслевые требования), полезно заранее продумать формат экспорта логов security-событий в читаемом для аудитора виде, не раскрывая при этом содержимого чувствительных диалогов.
# экспорт агрегированной статистики без содержимого диалогов
SELECT
date_trunc('day', created_at) AS day,
user_id,
event_type,
count(*) AS occurrences
FROM security_events
WHERE created_at >= now() - interval '90 days'
GROUP BY 1, 2, 3
ORDER BY 1 DESC;
Такой отчёт показывает аудитору, что система контролируется и события фиксируются, но не требует раскрытия персональных данных сотрудников или клиентов, упомянутых в самих диалогах.
Разные политики хранения для разных типов агентов
Не все агенты одинаково чувствительны с точки зрения хранения логов. Внутренний ассистент для код-ревью, скорее всего, не работает с персональными данными вообще, тогда как агент поддержки клиентов почти гарантированно сталкивается с ними в каждом втором диалоге. Разумно вести не единую политику на все случаи, а профиль хранения для каждого класса агентов.
# профили хранения логов по типу агента
retention_profiles:
code-review-assistant:
conversations_days: 180 # низкая чувствительность, дольше хранение
pii_masking: false # PII почти не встречается в коде
support-assistant:
conversations_days: 30 # высокая чувствительность, короче срок
pii_masking: true
pii_strict_mode: true # более агрессивное маскирование
analytics-assistant:
conversations_days: 60
pii_masking: true
Такая дифференциация требует чуть больше настройки на старте, зато отражает реальный профиль риска каждого агента, вместо универсального компромисса, который для одних агентов избыточно строгий, а для других недостаточно осторожный.
Частые ошибки и как их избежать
- Один общий лог без разделения по уровням чувствительности. Итог — либо хранишь всё вечно с огромным риском утечки, либо удаляешь всё быстро и теряешь возможность разбирать инциденты. Раздели логи на слои с самого начала.
- Маскирование PII применяется постфактум скриптом раз в неделю. Всю неделю чувствительные данные лежат на диске в открытом виде. Маскируй на этапе записи, до того как данные попадут на диск.
- Доступ к логам диалогов есть у всей DevOps-команды «на всякий случай». Чем шире круг доступа, тем выше риск внутренней утечки. Ограничивай доступ к содержимому диалогов явным списком людей с обоснованной необходимостью.
Логи и обучение на данных компании
Соблазн использовать накопленные логи диалогов для дообучения собственной модели (fine-tuning) под специфику компании понятен — это потенциально улучшает качество ответов. Но здесь стоит быть особенно осторожным: если в логах остались персональные данные клиентов или сотрудников, модель, обученная на них, рискует «запомнить» и впоследствии случайно воспроизвести эти данные в ответах другим пользователям.
- Используй для fine-tuning только логи, прошедшие полное маскирование PII, и в идеале — отдельно проверенные человеком перед включением в обучающий набор;
- Никогда не включай в обучающий набор диалоги из сессий, помеченных как приватные, даже если формально срок их хранения ещё не истёк;
- Получи явное согласие на использование диалогов для обучения отдельно от согласия на их хранение — это разные по последствиям действия, и объединять их в одном пункте политики не стоит.
Практический пример структуры записи лога
Для наглядности — как может выглядеть единая запись, объединяющая все три уровня детализации в одном событии, прежде чем она распределяется по соответствующим хранилищам:
{
"timestamp": "2026-08-28T14:32:10Z",
"user_id": "e.ivanova",
"session_id": "a1b2c3d4",
"model": "support-assistant",
"token_count": {"input": 340, "output": 128},
"latency_ms": 1840,
"plugin_used": "web_search",
"content": {
"request": "[замаскировано, хранится 90 дней]",
"response": "[замаскировано, хранится 90 дней]"
},
"security_flags": {
"injection_detected": false,
"pii_masked_count": 2
}
}
Такая структура позволяет одним запросом получить метаданные для дашборда учёта нагрузки (см. статью про ограничение доступа и учёт токенов), не поднимая при этом чувствительное содержимое диалога, доступ к которому строго ограничен.
Что дальше
Безопасность и эксплуатация AI-инфраструктуры — вот с чем ещё стоит разобраться: