Налаштування логування запитів та відповідей AI-моделі
Логування всіх запитів/відповідей AI-моделі є основою для налагодження, аудиту, аналізу якості та compliance. Для LLM це нетривіально: великі обсяги даних, PII у промптах, висока кардиналіти значень.
Структуроване логування
import structlog
import json
from datetime import datetime
logger = structlog.get_logger()
def log_llm_request(
request_id: str,
user_id: str,
model: str,
messages: list[dict],
response: str,
usage: dict,
latency_ms: float,
error: str | None = None
):
# PII-фильтрация перед логированием
safe_messages = pii_filter(messages)
logger.info(
"llm_request",
request_id=request_id,
user_id=hash_user_id(user_id), # псевдонимизация
model=model,
prompt_tokens=usage.get("prompt_tokens"),
completion_tokens=usage.get("completion_tokens"),
total_tokens=usage.get("total_tokens"),
cost_usd=calculate_cost(model, usage),
latency_ms=latency_ms,
has_error=error is not None,
error_type=type(error).__name__ if error else None,
# Полные сообщения только если включено детальное логирование
messages=safe_messages if DETAILED_LOGGING else None,
response_preview=response[:200] if response else None, # первые 200 символов
)
Зберігання повних логів (для аудиту)
Для compliance потрібні повні запити/відповіді. Окремо від стандартних логів великий обсяг:
class LLMRequestStore:
def __init__(self, s3_bucket: str, retention_days: int = 90):
self.s3 = boto3.client("s3")
self.bucket = s3_bucket
self.retention_days = retention_days
def store(self, request: LLMRequest) -> str:
# Путь: year/month/day/hour/request_id.json.gz
key = f"llm-logs/{datetime.utcnow().strftime('%Y/%m/%d/%H')}/{request.id}.json.gz"
data = gzip.compress(json.dumps(request.to_dict()).encode())
self.s3.put_object(
Bucket=self.bucket,
Key=key,
Body=data,
ContentEncoding="gzip",
)
return key
Захист персональних даних у логах
import re
class PIIFilter:
PATTERNS = [
(r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b', '[CARD_NUMBER]'),
(r'\b\d{3}-\d{2}-\d{4}\b', '[SSN]'),
(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', '[EMAIL]'),
(r'\+?[\d\s\-\(\)]{10,15}', '[PHONE]'),
]
def filter(self, text: str) -> str:
for pattern, replacement in self.PATTERNS:
text = re.sub(pattern, replacement, text)
return text
Retention та архівування
Гарячі логи (< 7 днів): Elasticsearch або ClickHouse для швидкого пошуку. Теплі (7-30 днів): S3 Standard. Холодні (30-365 днів): S3 Glacier Instant Retrieval. Вилучення: після retention_days через S3 Lifecycle rules.







