Темная сторона observability: Что на самом деле нужно логировать в AI-приложениях

12.06.2026 13:00

На дашборде Datadog всё светится зеленым. График RPS ровный, P95 latency держится в рамках 1.5 секунд, HTTP 500 отсутствуют как класс, CPU контейнеров едва переваливает за 30%. Система работает идеально.

Но тут в Slack врывается дежурный саппорта: "Ребята, нейросеть в чате поддержки начала угрожать клиентам и предлагать им купить стиральную машину вместо оформления возврата билетов".

Вы открываете логи. Фильтруете по ID пользователя. Видите: POST /api/v1/chat/completions -> HTTP 200 OK, duration: 1450ms.

Приложение честно отработало запрос. Ошибок в коде нет. Инфраструктура в порядке. Но что именно приложение отправило в OpenAI, и почему модель сошла с ума — абсолютная загадка.

Это классический пример AI-слепоты. Когда компании начинают интегрировать большие языковые модели (LLM) в свои продукты, они переносят на них старые привычки мониторинга. Но в мире недетерминированных систем старые правила перестают работать.

Почему классические метрики лгут

В традиционном бекенде код детерминирован. Если функция падает с NullReferenceException, вам нужен стек-трейс и пара переменных окружения, чтобы воспроизвести баг локально. Вы не логируете полные тела ответов от базы данных на каждый запрос, потому что это мгновенно забьет Elasticsearch или ClickHouse, а пользы не принесет.

В AI-приложениях payload — это и есть логика. Ошибка "Failed to parse JSON" на клиенте возникает не из-за сломанной библиотеки парсинга, а потому что модель уперлась в лимит токенов (finish_reason: length) и оборвала строку на {"user": "Alex", "age": 2.

Или другой сценарий: RAG-пайплайн (Retrieval-Augmented Generation) возвращает пользователю ответ "Извините, я не нашел информации в вашей базе". Технически всё отработало на 100% корректно. Ошибок нет. Но с точки зрения бизнеса — это инцидент. Был ли плохой запрос в векторную базу? Достал ли эмбеддинг не те чанки? Или LLM просто проигнорировала релевантный контекст? Без семантического логирования вы никогда не узнаете правду.

Что на самом деле нужно логировать

Переход от классического к AI-мониторингу требует сдвига фокуса с инфраструктуры на семантику. Вот чек-лист параметров, без которых разбор инцидентов превращается в гадание на кофейной гуще.

1. Полный стейт промпта (Input / Output)

Вам необходимо сохранять точный, посимвольный payload, который ушел в модель, и точный ответ.

  • System Prompt: Какие инструкции были активны в этот момент? (Особенно важно, если вы используете A/B тестирование промптов).
  • Few-shot examples: Какие примеры были подставлены в контекст?
  • User input: Что конкретно ввел пользователь.
  • Raw output: Ответ модели до того, как ваш бекенд попытался его распарсить, очистить от маркдауна или превратить в DTO.

2. Параметры генерации

Температура (temperature), top_p, frequency_penalty, presence_penalty и выбранная модель (model). Случай из практики: один из разработчиков случайно захардкодил температуру 1.5 для агента, занимающегося классификацией строгих юридических терминов. Модель начала придумывать несуществующие законы. Найти эту проблему удалось только благодаря тому, что параметры отправлялись в трейсинг как атрибуты спанов (span attributes).

3. Метаданные ответа (Usage Metrics)

  • Token counts: prompt_tokens, completion_tokens, total_tokens. Без этого вы не сможете строить дашборды юнит-экономики и не заметите, как кривой RAG начал заливать в контекст по 80 тысяч токенов мусора на каждый запрос.
  • Finish reason: Важнейший маркер. stop — всё хорошо. length — уперлись в max_tokens. content_filter — сработала защита провайдера.

4. Цепочки вызовов (Tracing LLM Calls)

Если ваш AI-агент делает несколько шагов (например, LangChain или собственная стейт-машина на базе ReAct), вам нужна распределенная трассировка. Каждый шаг рассуждений модели (Thought), каждое действие (Action) и каждый результат вызова внешней функции (Observation) должны быть связаны единым trace_id. Без этого, когда агент уходит в бесконечный цикл (Agent Loop), вы просто увидите всплеск расходов на балансе провайдера, но не поймете, на каком именно туле (tool) зациклилась модель.

Темная сторона: Хранение, PII и Деньги

Здесь мы сталкиваемся с главным конфликтом AI-observability. Логировать всё подряд — невероятно дорого. Если ваш сервис обрабатывает сотни запросов в секунду с контекстами по 32K токенов, вы генерируете гигабайты текстовых логов в час. Индексировать такие объемы в стандартном ELK-стеке или Datadog влетит в копеечку.

Вторая, еще более острая проблема — приватность (PII - Personally Identifiable Information). Пользователи отправляют в LLM медицинские диагнозы, номера кредитных карт и пароли. Если вы просто дампите всё в логи, вы нарушаете GDPR, HIPAA, PCI-DSS и базовые принципы безопасности.

Как решают эту проблему зрелые команды?

  1. Семплирование (Sampling): Для успешных запросов (где парсинг прошел, а метрика уверенности высока) логируется только 5% полных пейлоадов.
  2. Асинхронное маскирование: Перед отправкой в лог-систему текстовые данные прогоняются через легковесную NER-модель (Named Entity Recognition), которая заменяет чувствительные данные на [REDACTED].
  3. Холодное хранение: Логи LLM-запросов отправляются не в дорогую in-memory систему для полнотекстового поиска, а складываются батчами в S3 или ClickHouse для ретроспективного анализа.

Интеграция: Как RouterAPI снимает боль слепоты

Разработка собственного решения для AI-observability — это месяцы работы выделенной платформенной команды. Нужно поднимать отдельный пайплайн, настраивать OpenTelemetry, писать коллекторы.

Именно поэтому в RouterAPI прозрачность заложена на уровне архитектуры. Выступая в роли интеллектуального шлюза (gateway) между вашим приложением и провайдерами (OpenAI, Anthropic и десятками других), RouterAPI берет на себя самую грязную работу по логированию.

Что дает личный кабинет RouterAPI:

  • История запросов в один клик: Вам не нужно лезть в консоль сервера. В дашборде RouterAPI сохраняется полная история взаимодействия. Вы можете найти конкретный запрос по времени или метаданным и увидеть точный payload, отправленный провайдеру, включая системный промпт и параметры генерации.
  • Точные тайминги и статусы провайдеров: Если Anthropic начал возвращать HTTP 529 (Overloaded), вы увидите это в админке RouterAPI с точной разбивкой по времени задержки, а не просто как таймаут вашего собственного бекенда.
  • Экономика на уровне логов: В истории запросов RouterAPI каждый вызов автоматически пересчитывается в реальные затраты, учитывая сложные тарифные сетки разных моделей. Вы сразу видите, какой именно запрос сожрал бюджет.
  • Изоляция и маршрутизация: Если запрос завершился ошибкой из-за падения конкретного upstream-провайдера, RouterAPI логирует момент автоматического фоллбека на резервного провайдера. В логах вы четко видите: "Попытка 1: Provider A (Error 500) -> Попытка 2: Provider B (Success)".

Использование RouterAPI превращает черный ящик LLM-интеграции в прозрачный процесс. Разработчикам больше не нужно гадать, почему модель "ответила бредом" — достаточно открыть лог запроса в личном кабинете, скопировать промпт, воспроизвести его в песочнице и скорректировать логику. Вы перестаете писать код для инфраструктуры и концентрируетесь на продукте.

Теги

Ещё по теме