Le Black Friday qui m'a fait comprendre l'importance du contexte distribué

Le 11 novembre 2024, à 02h47 du matin, mon téléphone vibre : alerte critique sur notre passerelle LLM d'e-commerce. Un pic de 14× le trafic normal — chatbot de service client dopé à GPT-4.1 qui sert simultanément 12 000 utilisateurs francophones. Les logs sont noyés, le rate-limit du fournisseur upstream se déclenche, et les 429 Too Many Requests se propagent en cascade vers le front, le worker de retry, et le système de tickets. Sans contexte de trace, impossible de savoir si l'erreur vient du quota par utilisateur, du token-per-minute, ou d'une panne régionale. Cette nuit-là, j'ai compris que OpenTelemetry n'était plus optionnel dans une architecture LLM.

Dans ce tutoriel, je vous montre comment instrumenter une passerelle d'API LLM compatible avec HolySheep AI pour que chaque erreur 429 — et chaque retry — conserve son trace_id de bout en bout, avec une latence P50 inférieure à 50 ms.

Pourquoi les erreurs 429 sont un cas particulier dans les chaînes LLM

Contrairement à un microservice REST classique, une requête LLM traverse plusieurs couches de rate-limiting invisibles :

Sans propagation de contexte, le ticket d'incident ressemble à : « 429 sur le pod B à 03h12 ». Avec OpenTelemetry + W3C Trace Context + Baggage, il devient : « 429 déclenché par le quota upstream de l'utilisateur tier=free user_id=8421, retry_after=60s, propagation OK vers le worker d'embeddings ».

Architecture cible : passerelle Flask + collector OTLP + Tempo

Notre stack de référence :

Code 1 — Passerelle LLM instrumentée avec span, baggage et capture des 429

# passerelle_llm.py

Passerelle API LLM avec traçage OpenTelemetry distribué.

from flask import Flask, request, jsonify from opentelemetry import trace, context, baggage from opentelemetry.sdk.trace import TracerProvider, Resource from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.propagate import inject, extract from opentelemetry.semconv.resource import ResourceAttributes from openai import OpenAI, RateLimitError import time, logging

── 1. Configuration du tracer global ──────────────────────────────────

resource = Resource.create({ ResourceAttributes.SERVICE_NAME: "llm-api-gateway", ResourceAttributes.SERVICE_VERSION: "1.4.2", ResourceAttributes.DEPLOYMENT_ENVIRONMENT: "production", }) provider = TracerProvider(resource=resource) provider.add_span_processor( BatchSpanProcessor(