Technique
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.
Préparer l’environnement
Section intitulée « Préparer l’environnement »cd labs/llm-observabilitydocker compose up -d
# Modèles locaux (5 à 10 minutes au premier lancement)docker exec -it mttl-ollama ollama pull mistral:7bdocker exec -it mttl-ollama ollama pull nomic-embed-text
cd app && pip install -r requirements.txt| Service | Adresse | Nature |
|---|---|---|
| Grafana | http://localhost:3000 | interface (admin / admin) |
| VictoriaMetrics | http://localhost:8428/vmui | interface de requête |
| Tempo | http://localhost:3200 | API HTTP, sans interface : on explore les traces depuis Grafana |
| Phoenix | http://localhost:6006 | interface |
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.
Étape 1 : lire le code (15 min)
Section intitulée « Étape 1 : lire le code (15 min) »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 (extensionsmttl.*). 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) raiseLes 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.
Étape 2 : premier lancement (20 min)
Section intitulée « Étape 2 : premier lancement (20 min) »python lab1_llm_app.pyDans Grafana :
- Explore, source Tempo : cherchez le service
lab1-llm-appet ouvrez une trace. - Vérifiez la présence des attributs de fournisseur, de modèle et d’usage.
- Tableau de bord LLM Overview : jetons et latence doivent apparaître.
Étape 3 : modifications guidées (25 min)
Section intitulée « Étape 3 : modifications guidées (25 min) »- Ajoutez l’attribut
gen_ai.request.top_psur le span. - Faites varier la température de 0.0 à 1.0 et observez la dispersion de la latence.
- 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).
Étape 5 : panne volontaire (20 min)
Section intitulée « Étape 5 : panne volontaire (20 min) »docker stop mttl-ollamapython lab1_llm_app.pyObservez 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.
Récapitulatif du lab
Section intitulée « Récapitulatif du lab »| Notion | Ce que le lab montre |
|---|---|
| Traces | dans ce lab, un appel LLM produit un span ; les spans enfants par étape arrivent au lab 2 |
| Métriques | toujours labellisées par modèle, client et fonctionnalité |
| Erreurs | span.set_status(StatusCode.ERROR) plus un compteur d’erreurs dédié |
| Coût | suivi ici en jetons ; en euros au lab 4 seulement, avec votre grille de prix versionnée et datée |
Pièges à éviter
Section intitulée « Pièges à éviter »- 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.