Aller au contenu

TechniquePratique

Taxonomie des métriques LLM et instrumentation Python

Pour : ingénieurs et SREPrérequis : Avoir lu la leçon 2 du parcours et savoir lire du Python.

La taxonomie couvre les quatre dimensions de l’observabilité LLM : qualité des réponses, performance et latence, coûts et tokens, infrastructure GPU et CPU.

  • Préfixe llm_ pour toutes les métriques propres aux LLM.
  • Préfixe rag_ pour les métriques propres à la recherche documentaire (retrieval).
  • Noms DCGM standard pour les métriques GPU (DCGM_FI_*).
  • Suffixes standard : _total (compteur), _seconds ou _ms (histogramme de latence), _ratio (valeur sans unité entre 0 et 1, quel que soit le type : jauge ou histogramme, comme rag_context_pressure_ratio), _bytes (jauge).
MétriqueType PrometheusCardinalitéLabels essentiels
llm_requests_totalCompteurMoyennemodel, provider, pipeline_id, status
llm_request_duration_msHistogrammeMoyennemodel, provider, pipeline_id
llm_tokens_total{type}CompteurFaiblemodel, provider, type=input ou output
llm_cost_usd_totalCompteurFaiblemodel, provider, tenant_id, use_case
llm_errors_totalCompteurMoyennemodel, provider, error_type
llm_ttft_msHistogrammeMoyennemodel, provider
rag_retrieval_score_avgJaugeFaibleindex_id, pipeline_id, strategy
rag_context_pressure_ratioHistogrammeFaiblepipeline_id, model
rag_chunks_dropped_totalCompteurFaiblepipeline_id, reason
DCGM_FI_DEV_GPU_UTILJaugeFaiblegpu, UUID, pod, namespace, node
vllm:num_requests_runningJaugeFaiblemodel_name, instance

Module 6 : instrumentation Python, émettre les métriques

Section intitulée « Module 6 : instrumentation Python, émettre les métriques »
  • Bibliothèque cliente Prometheus : l’application expose un point /metrics, collecté par vmagent. Simple et très répandu.
  • OTLP via le SDK OpenTelemetry : métriques et traces dans le même pipeline. Recommandé pour les nouvelles applications.
Fenêtre de terminal
# Option 1 : prometheus_client
pip install prometheus-client
# Option 2 : SDK OTel (métriques et traces unifiées)
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-grpc

6.2 Client Prometheus : les métriques LLM de base

Section intitulée « 6.2 Client Prometheus : les métriques LLM de base »
from prometheus_client import Counter, Histogram, Gauge, start_http_server
import time
LLM_REQUESTS = Counter(
'llm_requests_total', 'Total LLM requests',
['model', 'provider', 'pipeline_id', 'status']
)
LLM_ERRORS = Counter(
'llm_errors_total', 'Total failed LLM requests',
['model', 'provider', 'error_type']
)
LLM_DURATION = Histogram(
'llm_request_duration_ms', 'LLM request duration in ms',
['model', 'provider', 'pipeline_id'],
buckets=[50, 100, 200, 500, 1000, 2000, 5000, 10000, 30000]
)
LLM_TOKENS = Counter(
'llm_tokens_total', 'Total tokens processed',
['model', 'provider', 'type'] # type : input ou output
)
LLM_COST = Counter(
'llm_cost_usd_total', 'Total cost in USD',
['model', 'provider', 'tenant_id', 'use_case']
)
LLM_TTFT = Histogram(
'llm_ttft_ms', 'Time to first token in ms',
['model', 'provider'],
buckets=[50, 100, 200, 500, 1000, 2000, 5000]
)
RAG_RETRIEVAL_SCORE = Gauge(
'rag_retrieval_score_avg', 'Average retrieval similarity score',
['index_id', 'pipeline_id', 'strategy']
)
RAG_CONTEXT_PRESSURE = Histogram(
'rag_context_pressure_ratio', 'Context window pressure ratio (0-1)',
['pipeline_id', 'model'],
buckets=[0.1, 0.3, 0.5, 0.7, 0.8, 0.85, 0.9, 0.95, 1.0]
)
RAG_CHUNKS_DROPPED = Counter(
'rag_chunks_dropped_total', 'Chunks dropped from the context',
['pipeline_id', 'reason']
)
# Expose /metrics sur le port 8000 (9090 est le port habituel de Prometheus)
start_http_server(8000)

6.3 Décorateur d’instrumentation : suivi automatique des coûts

Section intitulée « 6.3 Décorateur d’instrumentation : suivi automatique des coûts »
import functools
import json
import os
# Grille de prix en USD par token, lue depuis un fichier JSON versionné que vous
# fournissez ; aucune valeur ici. À renseigner depuis la grille datée de votre fournisseur :
# {"effective_date": "AAAA-MM-JJ", "currency": "USD",
# "models": {"grand-modele": {"input": null, "output": null}}}
# Sans fichier, TOKEN_PRICES reste vide : le décorateur compte les tokens et n'émet
# pas llm_cost_usd_total.
PRICE_GRID_FILE = os.getenv('LLM_PRICE_GRID_FILE')
PRICE_GRID_EFFECTIVE_DATE = None
TOKEN_PRICES = {}
if PRICE_GRID_FILE:
with open(PRICE_GRID_FILE, encoding='utf-8') as fh:
_grid = json.load(fh)
PRICE_GRID_EFFECTIVE_DATE = _grid['effective_date']
TOKEN_PRICES = {m: p for m, p in _grid['models'].items()
if p.get('input') is not None and p.get('output') is not None}
def track_llm_call(model, provider, pipeline_id, tenant_id='default', use_case='chat'):
def decorator(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
labels = {'model': model, 'provider': provider, 'pipeline_id': pipeline_id}
t0 = time.monotonic()
status = 'success'
try:
result = await func(*args, **kwargs)
if hasattr(result, 'usage'):
in_tok = result.usage.prompt_tokens
out_tok = result.usage.completion_tokens
LLM_TOKENS.labels(model=model, provider=provider, type='input').inc(in_tok)
LLM_TOKENS.labels(model=model, provider=provider, type='output').inc(out_tok)
if model in TOKEN_PRICES:
p = TOKEN_PRICES[model]
cost = in_tok * p['input'] + out_tok * p['output']
LLM_COST.labels(model=model, provider=provider,
tenant_id=tenant_id, use_case=use_case).inc(cost)
return result
except Exception as e:
status = type(e).__name__
LLM_ERRORS.labels(model=model, provider=provider, error_type=status).inc()
raise
finally:
LLM_DURATION.labels(**labels).observe((time.monotonic() - t0) * 1000)
LLM_REQUESTS.labels(**labels, status=status).inc()
return wrapper
return decorator
@track_llm_call(model='grand-modele', provider='fournisseur-a', pipeline_id='support-rag', tenant_id='acme')
async def call_llm(messages: list) -> dict:
# client : le client SDK de votre fournisseur ; utilisez l'identifiant exact du modèle
return await client.chat.completions.create(model='identifiant-du-modele', messages=messages)

Le label status prend le nom de la classe d’exception : limitez le nombre de types d’exception distincts, ou ramenez-les à une courte liste de catégories, pour borner la cardinalité.

Révisé le 2 octobre 2026 : prix retirés ; la grille de prix du décorateur est lue depuis un fichier que vous fournissez, sans valeur dans le guide et le coût n’est émis que pour les modèles dont le prix est renseigné.