Наблюдаемость сервинга LLM: метрики и трассировка

Задача страницы. Дать практический план по наблюдаемости LLM‑сервисов: какие SLI/SLO фиксировать, как мерить TTFT/TBT/TTLT, где смотреть TPS/QPS, как собирать GPU/KV‑кэш и стоимость за 1M токенов, как устроить трейсинг запросов (OpenTelemetry) и построить дашборды/алерты.

TL;DR

  • Разбейте запрос на этапы: queue → prefill → first_token → decode(stream) → post → done. На каждом этапе собирайте время/ошибки.
  • Ключевые метрики: TTFT, TBT, TTLT, QPS, TPS_decode/prefill_rate, KV‑occupancy/evictions, GPU‑util/HBM.
  • Для управления SLA держите пулы short/long, жёсткие лимиты max_tokens/context и стриминг; алертите по p95 и timeouts.
  • Логи — структурные события без PII. Трейсинг — сквозной trace_id от шлюза до сервера модели и инструментов.

Связанные разделы: /solutions/monitoring-logging//solutions/llm-inference/streaming//solutions/llm-inference/guardrails//solutions/llm-inference/vllm//solutions/llm-inference/tgi//solutions/llm-inference/tensorrt-llm//solutions/llm-inference/sglang//solutions/llm-inference/llama-cpp//solutions/llm-inference/multi-model//solutions/llm-inference/costs//solutions/cost-planner//solutions/llm-inference/quantization/

SLI/SLO: что считаем «качеством сервиса»

SLI (показатели):

  • TTFT — время до первого токена.
  • TBT — средняя задержка между токенами (stream decode).
  • TTLT — время до последнего токена (время ответа).
  • QPS — запросов/с, TPS_decode/prefill_rate — токенов/с на декоде/префилле.
  • Error/Timeout rate, Queue wait (время ожидания планировщика).
  • GPU: utilization, HBM usage, power, SM occupancy; KV‑кэш: occupancy, evictions.

SLO (цели, пример):

  • TTFT p95 ≤ 300 мс (short‑pool), TBT p95 ≤ 50 мс/токен, TTLT p95 ≤ 2.5 с при L_out ≤ 200.
  • Ошибки ≤ 0.5%, timeouts ≤ 0.2%.
  • Цена за 1M токенов в short‑pool ≤ X ₽ (см. формулы ниже).

Таймлайн запроса и точки измерения

client → gateway

→ queue_in

→ prefill_start … prefill_end

→ first_token (TTFT)

→ decode_tick (много раз; мерим TBT/TPS)

→ postprocess

→ done (TTLT)

События логируйте с общим trace_id и метаданными: модель, пул, лимиты, длины (L_in/L_out), параметры генерации, GPU‑узел, версия весов.

Метрики: полный базовый набор API/транспорт

  • requests_total{route,model,pool,status} (counter)
  • request_duration_seconds p50/p95/p99 (histogram)
  • inflight_requests{pool} (gauge)
  • SSE/WS: активные соединения, обрывы, heartbeat‑timeouts

Латентность генерации

  • ttft_seconds (histogram)
  • tbt_seconds_per_token (histogram)
  • ttlt_seconds (histogram)
  • queue_wait_seconds (histogram)

Throughput

  • tokens_generated_total{phase=decode} (counter)
  • prefill_tokens_total (counter)
  • decode_batch_size (histogram)
  • scheduler_efficiency (gauge, 0–1)

KV‑кэш

  • kv_cache_bytes_used / kv_cache_evictions_total
  • prefix_cache_hit_ratio (где доступно)

GPU/система

  • gpu_utilization / gpu_memory_bytes / gpu_power_watts
  • nvlink_tx_rx_bytes_total / pcie_tx_rx_bytes_total
  • CPU токенизации, дисковый I/O (веса/логи), сеть на стриме

Модель/вызов

  • Распределения L_in/L_out, temperature, top_p, stop_reasons
  • Ошибки по классам: oom, timeout, over_limit, bad_json
  • Экономика tokens_total → Tokens/hourCost_per_1M (см. §10)

Инструментирование по стеку

  • vLLM. Снимайте: prefill_rate, decode_tps, размер текущего батча, hit‑rate prefix‑кэша, paged‑KV занятость. См. /solutions/llm-inference/vllm/.
  • TGI. Очереди планировщика, max_input_length, max_total_tokens, фактический батч, p95 на short/long пулах. См. /solutions/llm-inference/tgi/.
  • TensorRT‑LLM. Время билда/прогрева движка, профиль ядёр, p95 при разных max seq len. См. /solutions/llm-inference/tensorrt-llm/.
  • SGLang/LightLLM. decode_tps, конкуренция, context-length распределения. См. /solutions/llm-inference/sglang/.
  • llama.cpp. tokens/s, -ngl, -c, CPU threads; p95 на hybrid CPU↔GPU. См. /solutions/llm-inference/llama-cpp/.

Пример метрик (Prometheus) и SSE‑хуки

Python (FastAPI + prometheus_client)

from prometheus_client import Counter, Gauge, Histogram
REQS = Counter("requests_total","",["route","model","pool","status"])
INF = Gauge("inflight_requests","",["pool"])
TTFT = Histogram("ttft_seconds","",buckets=[.05,.1,.2,.3,.5,1,2,5])
TBT = Histogram("tbt_seconds_per_token","",buckets=[.005,.01,.02,.03,.05,.1])
TTLT = Histogram("ttlt_seconds","",buckets=[.5,1,2,3,5,8,13])
TOK = Counter("tokens_generated_total","",["phase","model","pool"])
KV = Gauge("kv_cache_bytes_used","",["model","pool"])
# Пример стрим‑обработчика (события start/first_token/token/done)
async def sse_stream(gen):
 import time, json
 t0, first = time.time(), None
 yield "event: startndata: {}nn"
 async for delta in gen: # delta = {"content": "..."} от сервера модели
 NOW = time.time()
 if first is None:
 TTFT.observe(NOW - t0); first = NOW
 else:
 TBT.observe(NOW - prev)
 prev = NOW
 TOK.labels("decode","llama3-8b","short").inc(len(delta["content"])) # грубо: 1 символ = 1 токен? замените на реальный счетчик токенайзера
 yield f"data: {json.dumps({'choices':[{'delta':delta}]}, ensure_ascii=False)}nn"
 TTLT.observe(time.time() - t0)
 yield "event: donendata: [DONE]nn"

PromQL (примеры)

# p95 TTFT по short-пулу
histogram_quantile(0.95, sum(rate(ttft_seconds_bucket{pool="short"}[5m])) by (le))
# QPS
sum(rate(requests_total[1m])) by (route, pool)
# TPS decode
sum(rate(tokens_generated_total{phase="decode"}[1m])) by (model,pool)
# Загруженность KV-кэша
avg(kv_cache_bytes_used) by (model,pool) / on(model,pool) avg(gpu_memory_bytes) by (model,pool)
``` **Трейсинг (OpenTelemetry): сквозные спаны**

**Дерево спанов (минимум):**

Serve.Chat (span)

 ├─ Pre.Filter

 ├─ Queue.Wait

 ├─ Prefill

 ├─ FirstToken

 ├─ Decode (stream, многократно)

 ├─ Postprocess

 └─ Store.Log/Audit

Добавляйте атрибуты: model, pool, L\_in/L\_out, kv\_used, batch\_size, gpu\_id, node, tenant, quant (int4/int8/fp8), finish\_reason.

**Идентификаторы**

- Пробрасывайте **trace\_id/span\_id** с gateway до сервера модели (заголовок traceparent).
- Для SSE/WS — передавайте trace\_id в первом чанке и в конце (done) для корреляции.

**Сэмплинг**

- 100% для ошибок/тайм‑аутов, 10% — для нормальных запросов; _burst sampling_ для всплесков.

**Дашборды: роли и панели**

**SRE‑дашборд**

- TTFT/TBT/TTLT p50/p95/p99 (short/long).
- Ошибки/тайм‑ауты, queue\_wait, inflight, распред. длин.
- GPU util/HBM, KV‑occupancy/evictions, сеть/CPU/диск.

**ML‑инженер**

- Prefill vs Decode доли, TPS, распределение параметров (temperature/top\_p), контекст/выход, stop\_reasons.
- Влияние квантизации на TPS/латентность. См. /solutions/llm-inference/quantization/.

**Продакт/стоимость**

- Tokens/hour, цена за 1M, доля short/long, пер‑тенантные графики. См. /solutions/llm-inference/costs/, /solutions/cost-planner/.

**Логи: события вместо «сырого текста»**

**Событие (рекомендация)**

{

 "ts":"2025-08-22T12:34:56Z",

 "trace\_id":"tr\_abc123",

 "tenant":"acme",

 "model":"llama3-8b@int8",

 "pool":"short",

 "len\_in":512, "len\_out":180,

 "ttft\_ms":180, "ttlt\_ms":1200,

 "kv\_bytes": 268435456,

 "finish\_reason":"stop",

 "error": null

}

Без PII/секретов; для PII — маски и хэши. См. /solutions/llm-inference/guardrails/ и /solutions/security/.

**Стоимость: как вывести из метрик**

Обозначим: TPS\_decode — токены/с на декоде, Num\_GPU, GPU\_hour\_price.

Tokens\_per\_hour ≈ TPS\_decode × 3600

Cost\_per\_1M ≈ (GPU\_hour\_price × Num\_GPU) / (Tokens\_per\_hour / 1e6)Добавьте в экспортер счётчик tokens\_generated\_total и агрегируйте по модели/пулу/тенанту.
 Сводные графики и планирование: /solutions/llm-inference/costs/, /solutions/cost-planner/.

**Нагрузочное тестирование и ёмкость**

**Типы тестов:** ступенчатая нагрузка, всплеск, длительное замачивание (soak), деградация.
 **Набор сценариев:** short (L\_in≤1K, L\_out≤200), balanced, long (≥8K контекста).
 **Фиксируйте:** p95 TTFT/TBT/TTLT, Timeouts/Errors, Queue wait, KV‑occupancy, GPU HBM/util, Tokens/hour.
 **Цель:** найти «колено» — при каком QPS растут p95/тайм‑ауты; обновить лимиты и размеры пулов.

**Интеграция со стримингом/агентами**

- Для SSE/WS измеряйте **TTFT/TBT/TTLT** в самих потоках; отправляйте trace\_id и finish\_reason. См. /solutions/llm-inference/streaming/.
- Для агентов логируйте **шаги инструментов**: tool\_start, tool\_result, tool\_error, длительность и объём контента. См. /solutions/llm-inference/agents/.

**Чек‑лист перед продом**

- Зафиксированы **SLO** (TTFT/TBT/TTLT p95, ошибки/тайм‑ауты, цена/1M).
- Включены метрики API/латентности/throughput/KV/GPU/стоимости.
- Настроен **трейсинг** с trace\_id от шлюза до сервера модели/инструментов.
- Дашборды: **SRE / ML / Стоимость**; алерты по p95/тайм‑аутам/KV/GPU.
- Логи — **событийные**, без PII; аудит и ретеншн.
- План деградации: пулы **short/long**, лимиты max\_tokens/context, квантизация/масштабирование.

**Навигация по разделу «Инференс LLM»**[/solutions/llm-inference/](/solutions/llm-inference/) • [/solutions/llm-inference/vllm/](/solutions/llm-inference/vllm/) • [/solutions/llm-inference/tgi/](/solutions/llm-inference/tgi/) • [/solutions/llm-inference/tensorrt-llm/](/solutions/llm-inference/tensorrt-llm/) • [/solutions/llm-inference/sglang/](/solutions/llm-inference/sglang/) • [/solutions/llm-inference/llama-cpp/](/solutions/llm-inference/llama-cpp/) • [/solutions/llm-inference/multi-model/](/solutions/llm-inference/multi-model/) • [/solutions/llm-inference/quantization/](/solutions/llm-inference/quantization/) • [/solutions/llm-inference/streaming/](/solutions/llm-inference/streaming/) • [/solutions/llm-inference/guardrails/](/solutions/llm-inference/guardrails/) • [/solutions/llm-inference/costs/](/solutions/llm-inference/costs/) • [/solutions/cost-planner/](/solutions/cost-planner/) • [/solutions/monitoring-logging/](/solutions/monitoring-logging/)

Готовы запустить?

Запустить GPU-сервер