← LLMOps and agent observability: debugging the black box
05 / 07 · 50 min

Alerting on quality drift, not just uptime

Why 200 OK says nothing about answer usefulness: online quality signals, LLM-as-judge on a traffic sample, thresholds and burn rate against alert fatigue, and input-distribution drift detection.

Lesson content is currently available in Russian.

What you will learn

  • →Объяснять, какие деградации LLM-системы невидимы для классического APM-мониторинга.
  • →Собирать онлайн-сигналы качества: фидбэк, поведенческие метрики, судья на сэмпле трафика.
  • →Проектировать алёрты с baseline, окнами и burn-rate, не создавая alert fatigue.

Слепота uptime-мониторинга

Классический мониторинг отвечает на вопросы «жив ли сервис» и «быстро ли он отвечает». Для LLM-систем этого мало: система может неделями держать 100% uptime и идеальный p95, выдавая при этом бесполезные ответы. HTTP 200 не означает, что ответ правильный, полезный или безопасный.

Типовые тихие деградации. Провайдер обновил модель за тем же алиасом — стиль и качество ответов поменялись без единого изменения в вашем коде; в трейсах улика: gen_ai.response.model изменился при неизменном gen_ai.request.model. Переиндексация базы знаний прошла с ошибкой — retrieval возвращает нерелевантные документы, агент отвечает «мимо», но все статусы зелёные. Деплой «косметической» правки системного промпта сломал инструкцию про формат — доля отказов тихо выросла втрое. Изменился сам трафик: маркетинг привёл новую аудиторию с вопросами, под которые промпт не писался.

Общее у этих сценариев: ни один не виден в метриках доступности, все видны в метриках качества — если те существуют. Отсюда рамка урока: качество — это отдельное измерение мониторинга со своими сигналами, своей базовой линией и своими алёртами. APM-слой остаётся обязательным (таймауты и 429 никуда не делись), но поверх него нужен контур, который следит за тем, что система отвечает, а не только за тем, что она отвечает быстро.

Онлайн-сигналы качества

Качество в проде измеряют слоями, каждый сигнал пишется скором к трейсу (scores в Langfuse, feedback в LangSmith):

  • Явный фидбэк — 👍/👎, оценка, жалоба. Честный, но редкий (единицы процентов) и смещённый: жалуются недовольные, довольные молчат. Тренд информативен, абсолютное значение — нет;
  • Неявный фидбэк — поведение: пользователь сразу переспросил то же самое (ответ не помог), скопировал ответ (помог), нажал «позвать человека», оборвал сессию. Сигналов на порядки больше, чем явных, но каждый требует аккуратной интерпретации;
  • Guardrail-метрики — доля отказов модели (refusal rate), доля fallback-ответов, срабатывания фильтров безопасности, доля «не знаю». Дёшевы, считаются по каждому запросу;
  • Online LLM-as-judge — судья с бинарной рубрикой («опирается ли ответ на предоставленный контекст?») оценивает случайные 1–5% трафика. Это главный тонкий сигнал, но у него есть цена в токенах — поэтому сэмпл, а не весь трафик, и дешёвая модель в роли судьи с периодической калибровкой по человеческой разметке.

Практика: начните с двух guardrail-метрик (refusal rate, fallback rate) и одного судьи на сэмпле — этого достаточно, чтобы поймать все сценарии из предыдущей секции. Каждому сигналу — базовая линия за 7–14 дней, без неё алёртить не на что.

Проектирование алёртов: пороги, окна, burn-rate

Плохой алёртинг хуже его отсутствия: после десятка ложных срабатываний канал мьютят, и настоящий инцидент проходит незамеченным. Принципы:

Порог от базовой линии, а не абсолют. «Refusal rate > 5%» ложно сработает в ночь низкого трафика и промолчит при росте с 1% до 4%. Правильнее: «> 2× медианы того же часа за последние 7 дней» или «> baseline + 3σ».

Окно вместо точки. Мгновенные значения шумны; агрегируйте за 15–60 минут и требуйте удержания условия. Для метрик с малым объёмом (judge на сэмпле) окна длиннее — иначе три неудачных кейса из тридцати дадут ложный «всплеск».

Burn-rate для SLO-метрик. Подход из Google SRE Workbook: алёртить на скорость сжигания бюджета ошибок в двух окнах сразу (например, быстрое 1 час и медленное 6 часов). Быстрое окно ловит резкие инциденты, медленное — ползучую деградацию, а комбинация режет ложные срабатывания.

Стартовый набор для LLM-системы: refusal rate > 2× baseline в течение часа; judge-fail rate на критичной фиче выше порога за 4 часа; cost/hour > +50% к тому же часу прошлой недели; TTFT p95 > 5 с дольше 15 минут; расхождение response.model c request.model — немедленно.

И дисциплина: у каждого алёрта — владелец, runbook и ссылка на сохранённый фильтр трейсов, чтобы дежурный за минуту перешёл от «что-то красное» к конкретным деревьям спанов. Алёрт без runbook — это будущий мьют.

Дрейф входного распределения

Система может деградировать без единого изменения с вашей стороны — если изменились сами входы. Новый сегмент аудитории, сезонные темы, другой язык, вирусный сценарий использования, о котором вы не думали, наконец, волна промпт-инъекций — всё это сдвигает распределение запросов относительно того, под которое писался промпт и собирался eval-сет.

Как ловить. Для структурированных признаков (длина запроса, язык, категория из роутера) работают классические тесты стабильности распределений — PSI (population stability index) или KL-дивергенция между текущим окном и базовым: PSI выше ~0.2 принято считать значимым сдвигом. Для свободного текста признаков нет — работают эмбеддинги: считаете векторы входных запросов, сравниваете текущее окно с базовым через дистанцию центроидов, долю точек дальше порога от ближайшего базового кластера и появление новых плотных кластеров. Инструменты класса Evidently делают это из коробки; на платформах трейсинга то же собирается поверх выгрузки входов.

Что делать с находками — двухтактный процесс. Еженедельный отчёт «новые кластеры входов» смотрит человек: каждый устойчивый новый кластер — это либо кандидат в новые eval-кейсы и правку промпта, либо сегмент для явного «вне области» ответа. Алёрт — только на резкие сдвиги: доля out-of-distribution запросов выросла кратно за часы, что чаще означает атаку или интеграцию нового клиента, о которой вам забыли сказать.

Связка с предыдущими уроками прямая: дрейф объясняет, почему качество упало при неизменном коде, а его детект подсказывает, какими кейсами пора пополнять eval-сет — об этом следующий урок.

Practice

Контур качества с алёртами

Для своего агента добавьте контур качества: refusal/fallback rate как скоры на каждом трейсе, online-судью с бинарной рубрикой на 10% трафика (учебный объём позволяет). Соберите базовую линию на 50+ прогонах, затем «сломайте» систему (испортите системный промпт или подмените retrieval) и покажите, что метрики сдвинулись. Опишите три алёрта с порогами от baseline, окнами и runbook.

Deliverable

Скриншоты скоров до и после поломки плюс документ с тремя алёртами: метрика, порог относительно baseline, окно, владелец, runbook со ссылкой на фильтр трейсов.

Checkpoint

Докажите на своём примере, что uptime-мониторинг не поймал бы вашу «поломку», а контур качества — поймал.

  • ✓ Приведён сценарий деградации с зелёным uptime и назван сигнал качества, который его вскрывает.
  • ✓ Алёрты сформулированы через baseline и окно (или burn-rate), а не голым абсолютным порогом.
  • ✓ Объяснён выбор доли трафика для online-судьи как баланс цены и чувствительности.

Check yourself

Провайдер тихо обновил модель за тем же алиасом. Как это заметить в трейсах?

01Провайдер тихо обновил модель за тем же алиасом. Как это заметить в трейсах?

Почему online LLM-as-judge запускают на сэмпле 1–5% трафика, а не на всём?

02Почему online LLM-as-judge запускают на сэмпле 1–5% трафика, а не на всём?

Чем порог «2× baseline того же часа недели» лучше абсолютного «refusal rate > 5%»?

03Чем порог «2× baseline того же часа недели» лучше абсолютного «refusal rate > 5%»?

В чём идея multiwindow burn-rate алёртинга из SRE Workbook?

04В чём идея multiwindow burn-rate алёртинга из SRE Workbook?

Как детектируют дрейф распределения для свободного текста входов?

05Как детектируют дрейф распределения для свободного текста входов?

Sources

Alerting on quality drift, not just uptime · LLMOps and agent observability: debugging the black box · VORCL