LLM Observability в production: метрики, трейсы и алерты
Что такое LLM observability?
LLM observability — практика сквозного восстановления AI-запроса и измерения того, решил ли он задачу пользователя безопасно, быстро и в заданном бюджете. Она объединяет traces, metadata моделей и tools, версии промптов, quality scores, продуктовые outcomes и alerts.
TL;DR
- -Model latency и HTTP status недостаточны: LLM-запрос может технически пройти и всё равно не решить задачу пользователя
- -Один root observation должен представлять пользовательскую задачу, а дочерние — retrieval, tools, model calls, parsing и guardrails
- -Измеряйте contract failures, качество задачи, latency, cost per successful task и продуктовый outcome, а не только токены и model IDs
- -Привязывайте версии промпта и релиза к generation, чтобы регрессию можно было связать с точным изменением
- -Маскируйте чувствительные данные до export, ограничивайте доступ, осознанно задавайте retention и проверяйте отказ telemetry без отказа продукта
LLM-endpoint может вернуть 200 OK, уложиться в latency target и выдать уверенный
неверный ответ. В этом и состоит observability gap: инфраструктурная telemetry видит
завершённый запрос, но не видит решённую задачу.
Production LLM observability связывает весь путь:
request → retrieval → tool calls → model generations → parser → guardrail → outcome
Для каждой задачи нужно уметь ответить:
- что произошло и в каком порядке;
- какие prompt, model, tools и retrieval release использовались;
- где потрачены время и деньги;
- прошёл ли output контракт и помог ли пользователю;
- изменил ли новый release любой из этих сигналов.
Langfuse — одна из практических реализаций. Это open-source платформа с OpenTelemetry-based SDK, tracing, prompt management, evaluation и metrics. Здесь речь об эксплуатации этого контура. Установка вынесена в отдельный пошаговый гайд по Langfuse. Контракты, роутинг, отказоустойчивость и релизы вокруг этого контура разобраны в общем руководстве по production LLM stack.
Начинайте с задачи, а не с model call
Полезная граница trace — одна пользовательская задача: классифицировать тикет, ответить в support, собрать маршрут или проверить pull request. Trace одного provider call теряет retrieval и tool steps, которые часто и вызвали ошибку.
Рабочая иерархия выглядит так:
root: answer-support-question
├── span: load-account-policy
├── span: retrieve-help-center
├── generation: draft-answer
├── tool: check-subscription-status
├── generation: revise-answer
└── span: validate-citations
В Langfuse observations являются OpenTelemetry spans. Generation — специализированный observation с model, parameters, token usage, cost и timing. Tool и retrieval observations сохраняют немодельную работу в одном причинном дереве. Актуальное SDK overview описывает mapping на OpenTelemetry и context propagation.
Не создавайте плоский список generations в надежде восстановить parentage позже. Передавайте trace context через async jobs и границы сервисов с самого начала.
Минимальный trace на актуальном Python SDK
Текущий Python SDK использует OpenTelemetry-based observation API:
from langfuse import get_client
langfuse = get_client()
with langfuse.start_as_current_observation(
as_type="span",
name="answer-support-question",
input={"ticket_id": ticket_id},
) as root:
with langfuse.start_as_current_observation(
as_type="generation",
name="draft-answer",
model=model_id,
input=messages,
prompt=prompt,
) as generation:
response = call_model(model_id=model_id, messages=messages)
generation.update(output=response)
root.update(output={"status": "completed"})
Credentials задаются через environment variables, а не через source code. В long-running service SDK экспортирует данные асинхронно. В short-lived job вызовите flush до завершения процесса.
Связь prompt=prompt принципиальна: она прикрепляет точную managed prompt version к
generation и позволяет строить prompt-level metrics и разбирать rollback. Полный
release workflow описан в
гайде по prompt engineering в production.
Спроектируйте telemetry contract
Instrumentation быстро расползается, если каждая команда придумывает names и metadata. До дашборда задайте небольшую общую схему.
Names
Используйте стабильные имена задач, а не route paths и model names:
support.answer
travel.itinerary.create
code_review.pull_request
Model — dimension, а не identity задачи. После смены модели time series должен продолжиться.
Dimensions
Добавляйте только dimensions, по которым будете фильтровать или группировать:
| Dimension | Пример | На какой вопрос отвечает |
|---|---|---|
environment | production | Проблема только в одной среде? |
release | Git SHA или app version | Какой release изменил поведение? |
feature | itinerary | Какой product surface создаёт cost? |
| prompt version | связанный prompt object | Регрессию вызвал prompt release? |
model | runtime model ID | Изменился routing или provider? |
session_id | стабильный internal ID | Multi-turn flow сломался посередине? |
| tags | canary, paid | Проблема только у одной когорты? |
Не используйте email и внешние account IDs в tags. High-cardinality dimensions полезны для debugging, но неудобны и дороги в dashboards. Стабильные внутренние ID нужны там, где требуется восстановление сессии или deletion workflow.
Inputs и outputs
«Логировать всё» не должно быть default. Отдельно решите, сохранять ли:
- production input и output;
- retrieved documents;
- tool arguments и results;
- model configuration;
- error bodies;
- evaluation reasoning.
Operational metrics можно сохранить без исходного content. Относитесь к traces как к production dataset с теми же правилами доступа и retention, что у исходных данных.
Измеряйте пять слоёв, а не один dashboard
1. Contract correctness
Эти сигналы детерминированы и должны быть дешёвыми:
- ошибки JSON или schema validation;
- отсутствие обязательных fields;
- невалидные или неразрешённые tool calls;
- ссылки на несуществующие citations;
- retry exhaustion и включение fallback;
- пустой или обрезанный output.
Contract error — не «низкое качество», а поломанный интерфейс. Обычно он должен останавливать rollout раньше субъективного score.
2. Качество задачи
Langfuse хранит результаты evaluation как scores. Score может прийти из user feedback, human annotation, deterministic code, LLM judge или experiment. Актуальная модель scores поддерживает numeric, categorical, boolean и text values.
Выбирайте score под задачу:
| Задача | Сигнал лучше абстрактного «helpfulness» |
|---|---|
| Classification | корректность label по class и failure slice |
| Extraction | schema validity и field-level precision/recall |
| RAG answer | citation validity, groundedness, полнота ответа |
| Agent workflow | task completion, tool errors, лишние steps |
| Customer support | accepted answer, correction, reopen, escalation |
Для output, который нельзя оценить детерминированно, используйте LLM-as-a-Judge, но калибруйте judge по решениям людей. Judge — измерительный инструмент, а не ground truth.
3. Operations
Измеряйте end-to-end и per-observation значения:
- request volume и error rate;
- p50, p95 и p99 task latency;
- provider latency и time to first token, если доступен;
- retries, rate limits, timeouts и fallback usage;
- queue delay и evaluator lag.
Медленный tool call и медленный model call требуют разных владельцев. Для этого и нужно дерево trace.
4. Economics
Tokens — вход для расчёта стоимости, но не продуктовая метрика. Считайте:
- cost per task;
- cost per successful task;
- cost по feature, release, prompt version и model route;
- retry и fallback cost;
- стоимость evaluators отдельно.
Дешёвый ответ, после которого растут reopens и ручные исправления, может оказаться дороже на уровне workflow.
5. Product outcome
Свяжите trace ID с downstream event, который означает успех: accepted suggestion, completed booking, resolved ticket, merged pull request или retained user. Без этой связи observability оптимизирует proxy, пока продукт становится хуже.
Sampling: сохраняйте редкие failures, семплируйте обычный success
Одинаковое сохранение 10% всех traces просто, но часто неверно. Сохраняйте:
- каждый contract failure и provider error;
- весь canary traffic на маленьком rollout;
- sessions с явным negative feedback;
- high-cost и high-latency outliers;
- репрезентативный sample обычного success.
Дорогие evaluators запускайте на контролируемой выборке. Langfuse использует детерминированный evaluator sampling: evaluators с одинаковыми filters и rate могут получить один и тот же subset. Сравнение scores будет чище, чем на независимых random samples.
Запишите sampling policy рядом с dashboard. Quality rate по flagged failures нельзя сравнивать с rate по случайному трафику.
Alerts: связывайте симптом с действием
Не алертите по каждой raw metric. У alert должны быть owner, comparison window, minimum sample и понятная реакция.
| Alert | С чем сравнивать | Первое действие |
|---|---|---|
| Contract failures | текущий release против recent baseline | остановить rollout; проверить parser/tool schema |
| Quality regression | prompt/model version и failure slice | поставить canary на паузу; открыть scored examples |
| Cost per success | feature и model route | проверить retries, context size, routing |
| p95 task latency | trace и child observation | найти slow span до настройки модели |
| Provider errors | provider, region, error class | включить или проверить fallback policy |
| Telemetry delay | ingestion timestamp против event time | проверить exporter, queue, worker, storage |
Thresholds задаются по наблюдаемой дисперсии и business impact. Универсальное правило «10% regression» шумит на низком трафике и пропускает абсолютные ошибки в high-risk workflow.
Dashboard должен показывать моменты смены версий. Alert без application release, prompt version, model route и environment отправляет on-call инженера в ручные раскопки.
Privacy и retention — часть instrumentation
Промпты и tool results часто содержат персональные или конфиденциальные данные.
Маскируйте их до выхода trace из процесса: post-ingestion cleanup слишком поздний для
строгих data boundaries. Для новых Python SDK setup Langfuse рекомендует
mask_otel_spans в masking guide.
Минимум:
- классифицируйте fields, которые разрешено экспортировать;
- маскируйте secrets, tokens, email, телефоны и document content по требованиям;
- разделите production и non-production projects и keys;
- ограничьте project access и export permissions;
- задайте deletion и retention procedure;
- тестируйте observability на репрезентативных redacted fixtures.
Self-hosted Langfuse по умолчанию не удаляет event data автоматически. Retention — осознанная настройка продукта или инфраструктуры. В retention documentation описаны доступность по планам, nightly deletion и влияние blob storage.
Cloud или self-hosted: решайте по стоимости эксплуатации
Self-hosted Langfuse больше не является двухконтейнерным PostgreSQL stack. Актуальный Langfuse v4 использует web и worker services, Postgres, ClickHouse, Redis или Valkey и S3-compatible blob storage. Docker Compose поддерживается для local и low-scale развёртывания; официальный self-hosting guide рекомендует managed или orchestrated варианты для production scale и high availability.
Self-hosting оправдан, когда организации нужны data locality, network isolation или контроль инфраструктуры и она готова отвечать за:
- backups и restore drills;
- schema и version upgrades;
- ClickHouse capacity и retention;
- queue и worker health;
- object storage lifecycle;
- authentication, TLS и secrets;
- alerting самой observability-системы.
Cloud выбирайте, когда managed operations ценнее самостоятельной эксплуатации этого stack. Не называйте self-hosting более дешёвым, пока не посчитано инженерное время и восстановление после сбоев.
Миграция старой Langfuse instrumentation
Если код всё ещё использует langfuse.trace(...), trace.generation(...) или legacy
batch ingestion, не копируйте старые примеры в новые сервисы. Langfuse v4 работает по
observations-first модели на OpenTelemetry. Python SDK v4 и JS/TS SDK v5 по умолчанию
используют актуальные data APIs.
Compatibility guide перечисляет требования к server и SDK, deprecated endpoints и migration deadlines. Там же указано, что старые SDK или OTLP exporter без актуального ingestion header могут показывать данные в v2 API с задержкой. Проверяйте freshness, прежде чем считать пустой dashboard признаком спокойной системы.
Практический rollout
День 1: одна задача. Проследите один пользовательский path целиком: retrieval, tools, model calls, parsing и финальный status.
День 2: contract signals. Добавьте schema failures, retries, fallback usage, latency и cost. Проверьте, что отказ telemetry не ломает запрос пользователя.
День 3: один quality score. Выберите сигнал, связанный с задачей, прикрепите его к тому же trace и посмотрите примеры с обоих концов distribution.
День 4: versions и privacy. Добавьте release и prompt versions. Настройте masking, access и retention до расширения coverage.
День 5: один actionable alert. Начните с failure, у которого понятны owner и response. В безопасной среде намеренно создайте условие и проверьте alert.
Затем повторите для следующего важного workflow. Цель — не максимальное количество traces. Цель — минимальный путь от «пользователи говорят, что AI стал хуже» до точного release, prompt, model route, tool call и failed example, которые объясняют почему.