Aller au contenu

TechniquePratique

Lab 1 : instrumenter une application LLM

Pour : ingénieurs et SREPrérequis : Avoir lu les modules 1 et 2 du parcours, savoir utiliser Docker et lire du Python.

Durée : 1 h 45. Prérequis : modules 1 et 2, Docker, Python 3.10 ou plus récent, 16 Go de mémoire conseillés.

Fenêtre de terminal
cd labs/llm-observability
docker compose up -d
# Modèles locaux (5 à 10 minutes au premier lancement)
docker exec -it mttl-ollama ollama pull mistral:7b
docker exec -it mttl-ollama ollama pull nomic-embed-text
cd app && pip install -r requirements.txt
ServiceAdresseNature
Grafanahttp://localhost:3000interface (admin / admin)
VictoriaMetricshttp://localhost:8428/vmuiinterface de requête
Tempohttp://localhost:3200API HTTP, sans interface : on explore les traces depuis Grafana
Phoenixhttp://localhost:6006interface

Versions des images du kit : Collector contrib 0.108.0, VictoriaMetrics v1.110.0, Tempo 2.6.0, Grafana 11.2.0, Phoenix version-5.9.1, Qdrant v1.12.0, Ollama 0.3.12. Elles sont épinglées dans docker-compose.yml ; les dépendances Python le sont dans app/requirements.txt.

Ouvrez app/lab1_llm_app.py et repérez :

  • init_telemetry(), qui crée les fournisseurs de traces et de métriques et les exportateurs OTLP ;
  • le span manuel autour de l’appel au modèle ;
  • les attributs gen_ai.* posés sur ce span ;
  • les instruments créés par create_genai_metrics() : jetons et durée (standard), coût et erreurs (extensions mttl.*). Le lab 1 n’émet pas de coût : le coût en euros n’est calculé qu’au lab 4, à partir d’une grille de prix que vous fournissez.

Le cœur de l’instrumentation tient en quelques lignes :

labels = {
GEN_AI_PROVIDER_NAME: Provider.OLLAMA, # gen_ai.provider.name
GEN_AI_OPERATION_NAME: Operation.CHAT,
GEN_AI_REQUEST_MODEL: MODEL,
MTTL_TENANT_ID: tenant, # extension maison : mttl.tenant.id
MTTL_FEATURE: feature, # extension maison : mttl.feature
}
with tracer.start_as_current_span(f"chat {MODEL}", attributes={**labels, ...}) as span:
try:
response = get_ollama().chat(model=MODEL, messages=[...], options={...})
input_tokens = response.get("prompt_eval_count", 0)
output_tokens = response.get("eval_count", 0)
span.set_attribute(GEN_AI_USAGE_INPUT_TOKENS, input_tokens)
span.set_attribute(GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens)
metrics["duration"].record(time.time() - start, labels)
metrics["tokens"].record(input_tokens, {**labels, GEN_AI_TOKEN_TYPE: "input"})
metrics["tokens"].record(output_tokens, {**labels, GEN_AI_TOKEN_TYPE: "output"})
except Exception as e:
err = {**labels, ERROR_TYPE: type(e).__name__}
span.record_exception(e)
span.set_status(trace.Status(trace.StatusCode.ERROR, str(e)))
metrics["duration"].record(time.time() - start, err)
metrics["errors"].add(1, err)
raise

Les constantes (GEN_AI_PROVIDER_NAME, MTTL_TENANT_ID…) sont définies dans app/shared/conventions.py : un seul fichier à modifier quand la convention évolue. Les noms standard restent dans gen_ai.*, les extensions du kit vont dans mttl.*.

Dans VictoriaMetrics, l’exportateur du Collector ajoute les suffixes d’unité et de type : la durée devient gen_ai_client_operation_duration_seconds_bucket, le compteur d’erreurs mttl_client_errors_total et l’attribut mttl.tenant.id le label mttl_tenant_id. Ce sont ces noms qu’interrogent les tableaux de bord et les alertes.

Fenêtre de terminal
python lab1_llm_app.py

Dans Grafana :

  1. Explore, source Tempo : cherchez le service lab1-llm-app et ouvrez une trace.
  2. Vérifiez la présence des attributs de fournisseur, de modèle et d’usage.
  3. Tableau de bord LLM Overview : jetons et latence doivent apparaître.
  1. Ajoutez l’attribut gen_ai.request.top_p sur le span.
  2. Faites varier la température de 0.0 à 1.0 et observez la dispersion de la latence.
  3. Ajoutez un événement de span pour le prompt : span.add_event("prompt", {...}). Cette capture est acceptable dans ce lab local ; en production, elle reste désactivée par défaut, ne s’active que par un choix explicite (opt-in) et s’accompagne d’un masquage des données sensibles (voir le module 2 et le §2 de l’article Observer un système LLM).

Étape 4 : plusieurs clients et fonctionnalités (25 min)

Section intitulée « Étape 4 : plusieurs clients et fonctionnalités (25 min) »

Le script envoie cinq requêtes réparties entre deux clients (acme-corp, client-test) et trois fonctionnalités (support, search, summary). Exercice à faire vous-même : ajoutez dans la liste PROMPTS un troisième client (globex-energy) et une quatrième fonctionnalité (translation), relancez, puis vérifiez sur le tableau de bord LLM Overview que les métriques se découpent par client et par fonctionnalité (panneaux « Jetons par client » et « Jetons par fonctionnalité », variables model et tenant en haut du tableau de bord).

Fenêtre de terminal
docker stop mttl-ollama
python lab1_llm_app.py

Observez les spans en erreur dans Tempo (avec l’attribut error.type), l’incrément du compteur mttl_client_errors_total et l’absence de jetons consommés. Relancez ensuite docker start mttl-ollama.

NotionCe que le lab montre
Tracesdans ce lab, un appel LLM produit un span ; les spans enfants par étape arrivent au lab 2
Métriquestoujours labellisées par modèle, client et fonctionnalité
Erreursspan.set_status(StatusCode.ERROR) plus un compteur d’erreurs dédié
Coûtsuivi ici en jetons ; en euros au lab 4 seulement, avec votre grille de prix versionnée et datée
  • mettre les prompts complets dans les attributs ;
  • laisser un identifiant client libre si vous en avez des milliers : la cardinalité explose ;
  • oublier que l’auto-instrumentation existe pour les cas standard : le paquet officiel du projet OpenTelemetry pour le client OpenAI est opentelemetry-instrumentation-openai-v2 ; opentelemetry-instrumentation-openai, sans suffixe, est celui du projet OpenLLMetry de Traceloop.

Révisé le 2 octobre 2026 : encadré sur l’état du kit, gen_ai.system remplacé par gen_ai.provider.name, extensions du kit déplacées de gen_ai.* vers mttl.*, noms de métriques avec les suffixes ajoutés par l’exportateur, Tempo présenté comme une API, exercice aligné sur le code (deux clients, trois fonctionnalités), paquet officiel opentelemetry-instrumentation-openai-v2 précisé ; plus de coût nul émis pour le modèle local, le coût en euros n’étant calculé qu’avec une grille de prix fournie.