Aller au contenu

TechniquePratique

4. La mise en œuvre en sept étapes

Pour : ingénieurs et SRE · architectesPrérequis : Avoir lu les chapitres 1 à 3 du guide ; notions de base sur OpenTelemetry.

Sept étapes ordonnées pour amener un système du niveau 0 à une plateforme de niveau 3 opérationnelle. L’ordre compte. Sauter des étapes produit une pile qui émet des données que personne n’interroge.

Avant toute instrumentation, écrivez les questions auxquelles la plateforme doit répondre. La stratégie d’instrumentation découle des questions et non l’inverse. Un échec courant consiste à tout instrumenter, puis à s’apercevoir que les attributs à forte cardinalité nécessaires pour répondre aux vraies questions métier n’ont jamais été capturés.

  • Quelle est la latence p95 et p99 de chaque étape de la chaîne, par locataire ?
  • Quelle fonctionnalité a consommé le plus de jetons la semaine dernière et comment cela se compare-t-il à la semaine précédente ?
  • À quelle fréquence l’agent entre-t-il dans une boucle de plus de dix étapes et quels outils sont en cause ?
  • Quel est le taux d’erreur de chaque serveur MCP et un serveur domine-t-il à lui seul la latence globale ?
  • Quel est le score de fidélité des réponses ancrées dans le corpus documentaire Z ?
  • Quelle est la tendance des signalements de toxicité sur les 30 derniers jours ?
  • Quelles versions de prompt ont produit des régressions sur le jeu de référence ?
  • Existe-t-il une dérive de la distribution des langues en entrée corrélée à des baisses de qualité ?
  • Montrez-moi le prompt et la réponse complets de la requête soumise par l’utilisateur X à l’instant T (ce qui suppose une capture du contenu activée explicitement, avec un accès contrôlé : voir l’étape 2).
  • Montrez-moi toutes les traces dont la sortie contenait un motif de numéro de carte bancaire.
  • Montrez-moi toutes les traces qui ont appelé l’outil billing.invoice_fetch avec des arguments contenant l’identifiant de locataire Y.

Fixez-vous un objectif interne de délai pour répondre aux questions d’investigation, par exemple moins d’une minute. Aucune réglementation n’impose ce chiffre ; il est illustratif, mais en cas d’incident ou de demande d’un auditeur, la rapidité d’investigation devient une fonctionnalité, pas un effet de bord.

Trois options, par ordre de préférence.

  1. L’instrumentation OpenTelemetry GenAI native lorsqu’elle existe. Les SDK sont encore en cours de maturation, mais leur adoption est, à mon avis, le bon pari à long terme.
  2. Les bibliothèques d’auto-instrumentation comme OpenInference (Arize) ou OpenLLMetry (Traceloop). Elles enveloppent les bibliothèques clientes courantes (SDK OpenAI, SDK Anthropic, LangChain, LlamaIndex) et émettent les spans automatiquement.
  3. La création manuelle de spans autour de la logique métier spécifique qui ne correspond à aucun cadre connu.

Capture minimale par appel LLM : nom du modèle, jetons en entrée, jetons en sortie, latence, statut d’erreur ; le prompt et la complétion suivent la règle de capture du contenu ci-dessous. Attributs complémentaires utiles : température, top-p, empreinte (hash) du prompt système, définitions d’outils, identifiant de requête, identifiant de locataire, version du prompt, version du moteur de recherche.

Conventions sémantiques OpenTelemetry GenAI : le noyau

Section intitulée « Conventions sémantiques OpenTelemetry GenAI : le noyau »
AttributTypeRôle
gen_ai.operation.namestringType d’opération (chat, embeddings, retrieval, execute_tool, invoke_agent, etc.)
gen_ai.provider.namestringIdentité du fournisseur (openai, anthropic, mistral_ai, etc.)
gen_ai.request.modelstringNom du modèle demandé
gen_ai.response.modelstringModèle réellement utilisé (peut différer de la demande)
gen_ai.usage.input_tokensintNombre de jetons en entrée
gen_ai.usage.output_tokensintNombre de jetons en sortie
gen_ai.response.finish_reasonsstring[]Raison de fin de génération (stop, length, content_filter, tool_call, error)
gen_ai.input.messages, gen_ai.output.messagesstructuré, opt-inContenu du prompt et de la complétion (voir la règle de capture ci-dessous)

Ces conventions sont encore au statut « en développement », maintenues depuis juin 2026 dans un dépôt dédié et plusieurs attributs ont déjà été renommés (par exemple gen_ai.system, nom qu’utilisait la première version de ce guide, devenu gen_ai.provider.name). La liste complète des attributs, leurs versions et l’historique des renommages sont tenus à jour dans l’article de mise en production, section 2 : c’est la référence du site sur ce point.

Une seule règle sur le site : la capture du contenu est désactivée par défaut et ne s’active qu’explicitement (opt-in). C’est aussi ce que prévoient les conventions : gen_ai.input.messages, gen_ai.output.messages et gen_ai.system_instructions sont au niveau d’exigence « Opt-In » et les instrumentations ne doivent pas les capturer par défaut. Pour la production, la spécification recommande de stocker le contenu hors de la télémétrie, avec ses propres contrôles d’accès et de ne garder que des références sur les spans ; l’enregistrement dans les attributs des spans convient plutôt à la préproduction ou aux cas où le stockage de télémétrie respecte les règles de confidentialité. Quand le contenu transite malgré tout par la télémétrie, il est masqué dans le Collector avant stockage (étape 3). Les drapeaux d’activation, les événements et les règles du Collector sont détaillés dans l’article de mise en production (spécification).

Les attributs personnalisés doivent porter un préfixe propre à votre organisation pour éviter toute collision avec les conventions futures. Exemple : acme.tenant_id, acme.prompt_version, acme.retriever.index_name.

# Pseudo-code Python avec opentelemetry.trace et hashlib
# CAPTURE_CONTENT : drapeau de configuration, False par défaut
def search(rewritten_query, tenant_id):
with tracer.start_as_current_span("retriever.search") as span:
span.set_attribute("acme.retriever.store", "qdrant")
span.set_attribute("acme.retriever.top_k", 8)
# Par défaut : longueur et condensat de la requête, jamais le texte
span.set_attribute("acme.retriever.query_length", len(rewritten_query))
span.set_attribute("acme.retriever.query_sha256",
hashlib.sha256(rewritten_query.encode()).hexdigest())
if CAPTURE_CONTENT: # opt-in explicite, désactivé par défaut
# texte en événement, hors des attributs indexés, masqué dans le Collector
span.add_event("acme.retriever.query", {"text": rewritten_query})
span.set_attribute("acme.tenant_id", tenant_id)
span.set_attribute("acme.retriever.index_name", "kb-prod-v3")
chunks = vector_store.search(rewritten_query, k=8)
span.set_attribute("acme.retriever.hits", len(chunks))
if chunks:
span.set_attribute("acme.retriever.top_score", chunks[0].score)
span.add_event("chunks", {"ids": [c.id for c in chunks]})
return chunks

Placez un OpenTelemetry Collector entre l’application et le dorsal. Le collecteur prend en charge ce qui n’a pas sa place dans l’application : mise en lots, nouvelles tentatives, contre-pression, enrichissement d’attributs, filtrage, échantillonnage, caviardage (redaction) et diffusion vers plusieurs dorsaux.

Pipeline de l'OpenTelemetry Collector : récepteurs, processeurs, exportateurs, trois pipelines parallèles pour les traces, les métriques et les journaux

Figure 7. Pipeline de l’OpenTelemetry Collector. Les récepteurs ingèrent les données, les processeurs les transforment, les exportateurs les acheminent vers les dorsaux. Trois pipelines tournent en parallèle pour les traces, les métriques et les journaux.

  • memory_limiter : protège le collecteur d’un dépassement mémoire lors des pics de trafic.
  • redaction : nettoyage des attributs avant stockage. Il supprime les attributs absents d’une liste de clés autorisées (allowed_keys), puis masque ou hache les valeurs qui correspondent à des expressions régulières bloquées (blocked_values). Il ne fait pas de reconnaissance d’entités nommées (NER) : celle-ci, comme le caviardage par LLM, exige un composant externe ou un processeur personnalisé (documentation du processeur).
  • attributes : injecte de façon uniforme l’environnement, la région, le locataire et la version de déploiement.
  • tail_sampling : décision fondée sur des règles, prise une fois la trace terminée.
  • batch : regroupe les spans pour une transmission efficace. Se place en dernier dans la chaîne des processeurs.
  • resource : injecte l’identité et la version du service dans chaque span.

Le stockage est réparti par type de signal. Une pile cohérente choisit un dorsal par signal et garantit qu’ils partagent l’indexation par identifiant de trace pour les requêtes croisées.

SignalLogiciels auto-hébergeablesServices gérés (SaaS)
MétriquesGrafana Mimir, Prometheus, Thanos, VictoriaMetricsChronosphere, Datadog, Grafana Cloud, New Relic
TracesGrafana Tempo, Jaeger, Phoenix (licence ELv2, source disponible), SigNozArize AX, Datadog APM, Grafana Cloud, Honeycomb
JournauxElasticsearch, Grafana Loki, OpenObserve, VictoriaLogsDatadog, Elastic Cloud, Grafana Cloud, Splunk
ÉvaluationsRattachées aux traces, agrégées en métriquesIdem : c’est une convention, pas un produit
Retours utilisateursMagasin d’événements indexé par identifiant de traceIdem : développement spécifique
Archive froideStockage objet compatible S3S3, Azure Blob, GCS

Les options sont listées par ordre alphabétique ; le chapitre 6 précise les licences et les critères de choix. Trois contraintes guident les choix de stockage : la politique de rétention, la latence des requêtes sur des prompts volumineux et les exigences de localisation des données. Quand une exigence de localisation s’applique (politique interne, régulateur sectoriel ou contrat), c’est souvent elle qui détermine la pile.

Deux modes de déclenchement pour l’évaluation en ligne.

  • En ligne directe (inline) : chaque requête déclenche une évaluation synchrone. Ajoute de la latence. À réserver aux cas où le résultat de l’évaluation doit conditionner la réponse (par exemple un filtre de toxicité ou un classifieur de refus).
  • Asynchrone : une tâche de fond échantillonne les traces et exécute l’évaluation hors du chemin critique. C’est le mode par défaut.

Évaluation en ligne sur les traces de production contre évaluation hors ligne sur des jeux de données figés

Figure 8. L’évaluation en ligne s’exécute sur les traces de production en direct. L’évaluation hors ligne s’exécute sur des jeux de données figés, en intégration continue ou en lots planifiés. Les deux sont nécessaires : l’une pour détecter la dérive, l’autre pour bloquer les régressions.

Le chapitre 5 détaille la conception des évaluateurs, notamment les modèles de prompts pour LLM juge, la calibration et l’agrégation des scores.

Construisez des tableaux de bord qui répondent aux questions définies à l’étape 1. Regroupez-les par public et par préoccupation.

  • Coût et usage : jetons par fonctionnalité, par locataire, par modèle, avec bandes d’anomalie.
  • Latence et fiabilité : p50, p95, p99 par étape de la chaîne, taux d’erreur par mode de défaillance.
  • Qualité et sûreté : scores des évaluateurs dans le temps, avec alertes de régression sur les baisses de qualité.
  • Dérive et distribution : longueur des entrées, langue, distribution des sujets et dérive des plongements.
  • Comportement des agents : histogrammes d’itérations, fréquence de sélection des outils, indicateurs de boucle.
  • Régression de qualité au-delà d’un seuil (par exemple, une baisse de la fidélité de plus de deux écarts-types).
  • Pic de coût au-delà d’un multiple de référence (par exemple, des jetons par minute supérieurs à 3 fois la médiane glissante sur sept jours).
  • Dégradation de latence (p95 au-dessus du SLO pendant plus de cinq minutes).
  • Détection de données personnelles ou d’identifiants secrets dans les sorties (tout compte non nul sur une fenêtre d’une minute).
  • Indicateur de boucle d’agent (plus de dix itérations dans une trace, seuil cohérent avec la question de l’étape 1, ou un nombre d’itérations supérieur au plafond défini pour la fonctionnalité).

Sans processus de revue explicite, les données s’accumulent et rien ne s’améliore. Instaurez des rituels hebdomadaires.

  • Revue des traces en échec et des traces à faible score, avec promotion vers un jeu de non-régression.
  • Revue des traces à score élevé, avec promotion vers un ensemble d’exemples few-shot.
  • Conditionnement des changements de modèle et de prompt au jeu de non-régression (aucune fusion si un score d’évaluateur régresse).
  • Revue des tendances des retours utilisateurs, avec des actions sur la recherche documentaire, les prompts ou le choix du modèle.

La boucle fermée est ce qui distingue une plateforme d’observabilité d’un système de journalisation. Sans elle, vous disposez d’une archive coûteuse en lecture seule.

Suite : 5. Conception des évaluateurs.

Révisé le 2 octobre 2026 : conventions GenAI déplacées dans le dépôt semantic-conventions-genai (v1.42.0), historique des attributs de contenu et valeurs de finish_reasons corrigés, fonctionnement réel du processeur redaction, place du processeur batch et batching côté exportateur, colonnes du tableau de stockage rendues neutres.

Révisé le 4 octobre 2026 : tableau des conventions réduit au noyau, historique des renommages remplacé par un renvoi à l’article de mise en production, règle de capture du contenu alignée sur la spécification (désactivée par défaut, activation explicite, stockage à part recommandé en production).