Monitoring et Observabilité IA : LangSmith, Phoenix, Helicone
Votre chatbot RAG fonctionne en dev. Super ! Mais en production :
- 💸 Combien ça coûte réellement par utilisateur ?
- ⏱️ Quelle est la latence p95 ? Certains utilisateurs attendent 10 secondes ?
- 🎯 La qualité se dégrade-t-elle au fil du temps ?
- 🐛 Pourquoi cette requête a échoué à 3h du matin ?
- 🔄 Quelle version du prompt performe le mieux ?
Sans monitoring, vous pilotez à l’aveugle. Les problèmes apparaissent en prod, vous ne savez pas quand ni pourquoi.
La solution : Monitoring et observabilité IA.
Dans ce guide :
- Métriques clés à tracker en production
- LangSmith : Solution officielle LangChain (payant)
- Phoenix : Open source par Arize AI
- Helicone : Proxy API simple et gratuit
- Comparaison et recommandations
- Alerting et détection d’anomalies
- A/B testing de prompts

Pourquoi le monitoring IA est différent
Monitoring classique vs IA
| Aspect | Monitoring Classique | Monitoring IA |
|---|---|---|
| Métriques | CPU, RAM, requêtes/sec | Latence LLM, tokens, coûts, qualité |
| Traces | HTTP requests | Prompts, retrievals, chains complets |
| Erreurs | 500, timeouts | Hallucinations, mauvais retrieval, rate limits |
| Coûts | Infra fixe | Variable (par token), imprévisible |
| Qualité | Uptime | Pertinence, exactitude, toxicité |
| Debug | Logs, stack traces | Prompts, contexte, réponses LLM |
Les défis spécifiques
Non-déterminisme
# Même prompt, réponses différentes
response1 = llm("Résume ce texte") # "Le texte parle de..."
response2 = llm("Résume ce texte") # "Ce document traite de..."
# → Comment comparer la qualité ?
Coûts variables
# Coût par requête varie selon input + output
query1 = "Bonjour" # $0.0001
query2 = "Analyse ce livre..." # $0.50 (avec long contexte RAG)
# → Besoin de tracking granulaire
Latence multi-composants
Total: 3.2s
├─ Embedding: 0.1s
├─ Vector search: 0.3s
├─ LLM: 2.5s (où est le bottleneck ?)
└─ Post-processing: 0.3s
Qualité subjective
Comment mesurer si "Réponse A" est meilleure que "Réponse B" ?
→ Besoin de métriques proxy + human eval
Métriques clés à tracker
Latence et performance
Métriques essentielles :
| Métrique | Description | Objectif |
|---|---|---|
| TTFT (Time to First Token) | Temps avant premier token en streaming | <500ms |
| Latence totale | Temps total de réponse | <3s (p95) |
| Latence par composant | Embedding, retrieval, LLM séparés | Identifier bottlenecks |
| Tokens/seconde | Vitesse génération | >50 t/s |
| Throughput | Requêtes/seconde traitées | Selon charge |
Exemple de tracking :
import time
from functools import wraps
def track_latency(component_name):
"""Decorator pour tracker latence"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
duration = time.time() - start
# Log vers monitoring tool
logger.info({
"component": component_name,
"duration_ms": duration * 1000,
"timestamp": time.time()
})
return result
return wrapper
return decorator
# Usage
@track_latency("embedding")
def create_embedding(text):
return embeddings.embed_query(text)
@track_latency("vector_search")
def search_docs(query_vector):
return vectorstore.similarity_search(query_vector, k=5)
@track_latency("llm_generation")
def generate_response(prompt):
return llm.invoke(prompt)
Coûts et tokens
Métriques financières :
| Métrique | Description | Utilité |
|---|---|---|
| Coût par requête | $ par appel API | ROI, pricing |
| Coût par utilisateur | $ par user/mois | LTV vs CAC |
| Tokens input/output | Comptage par type | Optimisation prompts |
| Coût par composant | Embedding vs LLM vs tools | Identifier coûts cachés |
| Burn rate | $/jour, $/mois | Budget tracking |
Calcul de coûts :
from tiktoken import get_encoding
class CostTracker:
"""Track coûts OpenAI en temps réel"""
PRICES = {
"gpt-4o": {"input": 2.50, "output": 10.00}, # $/1M tokens
"gpt-4o-mini": {"input": 0.15, "output": 0.60},
"text-embedding-3-small": {"input": 0.02, "output": 0}
}
def __init__(self):
self.enc = get_encoding("cl100k_base")
self.daily_cost = 0
def calculate_cost(self, model: str, input_text: str, output_text: str) -> float:
"""Calcule coût d'un appel"""
input_tokens = len(self.enc.encode(input_text))
output_tokens = len(self.enc.encode(output_text))
prices = self.PRICES.get(model, {"input": 0, "output": 0})
cost = (
(input_tokens * prices["input"] / 1_000_000) +
(output_tokens * prices["output"] / 1_000_000)
)
self.daily_cost += cost
return cost
def log_cost(self, model: str, input_text: str, output_text: str, user_id: str):
"""Log coût vers monitoring"""
cost = self.calculate_cost(model, input_text, output_text)
logger.info({
"event": "llm_call",
"model": model,
"user_id": user_id,
"cost_usd": cost,
"daily_total": self.daily_cost,
"timestamp": time.time()
})
# Alerter si dépassement
if self.daily_cost > 100: # $100/jour
send_alert(f"⚠️ Daily cost: ${self.daily_cost:.2f}")
# Usage
tracker = CostTracker()
tracker.log_cost("gpt-4o", prompt, response, user_id="user_123")
Qualité et exactitude
Métriques de qualité :
| Métrique | Description | Méthode |
|---|---|---|
| Relevance | Réponse pertinente à la question | LLM-as-judge, human eval |
| Faithfulness | Réponse basée sur contexte fourni | Citation checking |
| Groundedness | Pas d’hallucinations | Fact verification |
| Toxicity | Contenu inapproprié | Perspective API |
| User satisfaction | 👍/👎 utilisateurs | Feedback buttons |
Exemple d’évaluation automatique :
from langchain.evaluation import load_evaluator
# Évaluateur de pertinence
relevance_evaluator = load_evaluator("qa")
def evaluate_response(question: str, answer: str, context: str):
"""Évalue qualité d'une réponse RAG"""
# 1. Pertinence (LLM-as-judge)
relevance_score = relevance_evaluator.evaluate_strings(
prediction=answer,
input=question,
reference=context
)
# 2. Groundedness (réponse basée sur contexte)
grounding_prompt = f"""
Contexte: {context}
Réponse: {answer}
La réponse est-elle entièrement basée sur le contexte ?
Réponds uniquement par "oui" ou "non".
"""
grounding = llm.invoke(grounding_prompt).content.lower()
grounded = "oui" in grounding
# 3. Log métriques
logger.info({
"question": question,
"relevance_score": relevance_score["score"],
"grounded": grounded,
"answer_length": len(answer),
"timestamp": time.time()
})
return {
"relevance": relevance_score["score"],
"grounded": grounded
}
Erreurs et fiabilité
Métriques de fiabilité :
| Métrique | Description | Objectif |
|---|---|---|
| Error rate | % requêtes en erreur | <1% |
| Rate limit hits | Fréquence 429 errors | 0 |
| Timeout rate | % timeouts | <0.5% |
| Fallback usage | % utilisation backup provider | <5% |
| Retry attempts | Nombre de retries moyen | <1.2 |
⚠️ Un taux d’erreur >5% indique un problème sérieux : rate limits dépassés, provider instable, ou prompts mal formés. Alertez immédiatement.
LangSmith : solution officielle LangChain
Présentation
LangSmith est la plateforme de monitoring officielle de LangChain, par les créateurs de LangChain.
Points forts :
- ✅ Intégration native LangChain (1 ligne de config)
- ✅ Tracing complet des chains/agents
- ✅ Playground pour tester prompts
- ✅ Datasets et évaluation
- ✅ Production monitoring
Lien : https://smith.langchain.com/ & notre article dédié LangSmith
Alternative open source : Découvrez aussi Langfuse, une alternative open source à LangSmith avec self-hosting possible et fonctionnalités similaires.
Configuration
1. Inscription et API key :
# Créer compte sur smith.langchain.com
# Générer API key
# .env
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=ls__...
LANGCHAIN_PROJECT=my-chatbot-prod
2. C’est tout ! Toutes vos chains LangChain sont automatiquement tracées.
Interface et features
Traces :
Run: user_query_abc123
├─ Retriever
│ ├─ Embedding (100ms, $0.00002)
│ └─ Vector Search (250ms)
│ → 5 documents retrieved
├─ Prompt Template (5ms)
│ → Rendered prompt (2,341 tokens)
└─ LLM (ChatOpenAI)
├─ Input: 2,341 tokens ($0.0059)
├─ Output: 287 tokens ($0.0029)
├─ Duration: 2,150ms
└─ Response: "Voici la réponse..."
Total: 2.5s, $0.0088
Dashboard :
- 📊 Latency p50/p95/p99 over time
- 💰 Costs par jour/semaine/mois
- 🔢 Tokens input/output trends
- ❌ Error rate et types d’erreurs
- 👥 Usage par utilisateur
Playground :
- Tester prompts interactivement
- Comparer versions côte à côte
- Voir traces en temps réel
Évaluation avec datasets
Créer un dataset :
from langsmith import Client
client = Client()
# Créer dataset d'évaluation
dataset = client.create_dataset("rag-eval-v1")
# Ajouter exemples
client.create_examples(
dataset_id=dataset.id,
inputs=[
{"question": "Qu'est-ce que le RAG ?"},
{"question": "Comment fonctionne l'embedding ?"}
],
outputs=[
{"answer": "Le RAG combine recherche et génération..."},
{"answer": "L'embedding transforme texte en vecteurs..."}
]
)
Évaluer automatiquement :
from langsmith.evaluation import evaluate
def my_evaluator(run, example):
"""Évaluateur custom"""
# run.outputs["answer"] = réponse du modèle
# example.outputs["answer"] = réponse attendue
# Calculer similarité
from difflib import SequenceMatcher
similarity = SequenceMatcher(
None,
run.outputs["answer"],
example.outputs["answer"]
).ratio()
return {"score": similarity}
# Lancer évaluation
results = evaluate(
lambda inputs: my_chain.invoke(inputs),
data=dataset.name,
evaluators=[my_evaluator]
)
# Résultats disponibles dans dashboard
Alerting
Configurer alertes :
# Via UI LangSmith :
# 1. Settings → Automations
# 2. Create Alert
# - Condition: error_rate > 5%
# - Notification: Email, Slack, Webhook
# Ou via code
client.create_rule(
project="my-chatbot-prod",
rule_type="threshold",
conditions={
"metric": "error_rate",
"operator": ">",
"value": 0.05
},
actions=[
{"type": "email", "recipients": ["[email protected]"]},
{"type": "slack", "webhook": "https://hooks.slack.com/..."}
]
)
Tarification
| Plan | Prix | Limites | Use Case |
|---|---|---|---|
| Developer | Gratuit | 5K traces/mois | POC, dev |
| Plus | $39/mois | 100K traces/mois | Petite prod |
| Enterprise | Sur devis | Illimité, SSO, SLA | Grande prod |
Calcul : 1 requête RAG = ~4 traces (embedding, retrieval, prompt, LLM) → 100K traces = ~25K requêtes utilisateurs
Phoenix : Open source par Arize AI
Présentation
Phoenix est une plateforme de monitoring IA open source par Arize AI.
Points forts :
- ✅ Complètement gratuit et open source
- ✅ Self-hosted (contrôle total des données)
- ✅ Supporte LangChain, LlamaIndex, OpenAI SDK
- ✅ Tracing, évaluation, embedding analysis
- ✅ Interface moderne
Lien : https://github.com/Arize-ai/phoenix
Installation
Docker (recommandé) :
docker run -p 6006:6006 arizephoenix/phoenix:latest
Python :
pip install arize-phoenix
# Lancer serveur
python -m phoenix.server.main serve
Interface disponible sur http://localhost:6006
Configuration avec LangChain
from phoenix.trace.langchain import LangChainInstrumentor
# Instrumenter LangChain
LangChainInstrumentor().instrument()
# Vos chains fonctionnent normalement, traces automatiques !
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(...)
response = qa_chain.invoke({"query": "Qu'est-ce que le RAG ?"})
# → Tracé automatiquement dans Phoenix
Configuration avec OpenAI SDK
from openinference.instrumentation.openai import OpenAIInstrumentor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
# Setup OpenTelemetry
tracer_provider = TracerProvider()
tracer_provider.add_span_processor(
SimpleSpanProcessor(OTLPSpanExporter("http://localhost:6006/v1/traces"))
)
# Instrumenter OpenAI
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)
# Utiliser OpenAI normalement
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}]
)
# → Tracé dans Phoenix
Features clés
1. Tracing :
- Visualisation tree des appels
- Latence par span
- Inputs/outputs capturés
- Métadonnées et tags
2. Embedding Analysis :
import phoenix as px
# Analyser embeddings
df = px.load_dataset("your_embeddings.parquet")
# Visualiser clusters
px.launch_app(
primary=px.Dataset(df, "embeddings_column"),
schema=px.Schema(
embedding_feature_column_names=["embedding"],
prediction_label_column_name="category"
)
)
# → Interface interactive pour explorer embeddings
3. Évaluation :
from phoenix.experimental.evals import (
HallucinationEvaluator,
RelevanceEvaluator,
run_evals
)
# Évaluer hallucinations
hallucination_eval = HallucinationEvaluator()
relevance_eval = RelevanceEvaluator()
results = run_evals(
dataframe=traces_df,
evaluators=[hallucination_eval, relevance_eval],
provide_explanation=True
)
# Résultats : scores + explications
print(results[["hallucination_score", "hallucination_explanation"]])
Avantages vs LangSmith
| Aspect | Phoenix | LangSmith |
|---|---|---|
| Prix | Gratuit | $39+/mois |
| Self-hosted | ✅ Oui | ❌ Cloud only |
| Data privacy | ✅ Total | ⚠️ Données chez LangChain |
| Setup | ⚠️ Plus complexe | ✅ 1 ligne |
| Embedding viz | ✅✅ Excellent | ❌ Non |
| LangChain integration | ✅ Bon | ✅✅ Natif |
| UI | ✅ Moderne | ✅✅ Excellent |
Helicone : Proxy API simple
Présentation
Helicone est un proxy pour APIs LLM (OpenAI, Anthropic, etc.) avec monitoring intégré.
Concept :
Votre app → Helicone Proxy → OpenAI
↓
Monitoring
Points forts :
- ✅ Setup ultra-simple (changer URL)
- ✅ Agnostique du framework (pas besoin LangChain)
- ✅ Gratuit jusqu’à 100K requêtes/mois
- ✅ Caching intégré
- ✅ Rate limiting
Lien : https://www.helicone.ai/
Configuration
1. Créer compte sur helicone.ai et obtenir API key
2. Modifier code (changer base URL) :
from openai import OpenAI
client = OpenAI(
api_key="sk-...", # Votre clé OpenAI
base_url="https://oai.helicone.ai/v1", # Proxy Helicone
default_headers={
"Helicone-Auth": "Bearer <HELICONE_API_KEY>"
}
)
# Utiliser normalement
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}]
)
# → Requête passe par Helicone → loggée → envoyée à OpenAI
Avec LangChain :
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o",
openai_api_key="sk-...",
openai_api_base="https://oai.helicone.ai/v1",
model_kwargs={
"extra_headers": {
"Helicone-Auth": "Bearer <HELICONE_API_KEY>"
}
}
)
Features
Dashboard :
- 📊 Requests/day, coûts totaux
- ⏱️ Latence moyenne, p95
- 💰 Coût par requête, par user
- 📈 Trends over time
- 🔍 Recherche dans prompts/réponses
Caching :
# Activer cache (économiser coûts)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={
"Helicone-Auth": "Bearer ...",
"Helicone-Cache-Enabled": "true"
}
)
# Si même prompt déjà vu → réponse depuis cache (gratuit, <50ms)
User tracking :
# Associer requêtes à utilisateurs
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={
"Helicone-Auth": "Bearer ...",
"Helicone-User-Id": "user_12345"
}
)
# → Dashboard : coûts par user, usage patterns
Custom properties :
# Ajouter métadonnées custom
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={
"Helicone-Auth": "Bearer ...",
"Helicone-Property-Environment": "production",
"Helicone-Property-Feature": "chatbot",
"Helicone-Property-Version": "v2.1"
}
)
# → Filtrer dashboard par propriétés
Tarification
| Plan | Prix | Limites | Features |
|---|---|---|---|
| Free | $0 | 100K requests/mois | Dashboard, basic analytics |
| Pro | $20/mois | 1M requests | Caching, alerting, webhooks |
| Enterprise | Sur devis | Illimité | Self-hosted, SLA |
Comparaison des solutions
Tableau récapitulatif
| Critère | LangSmith | Phoenix | Helicone |
|---|---|---|---|
| Prix | $39/mois (100K traces) | Gratuit (open source) | Gratuit (100K req) |
| Setup | ⭐⭐⭐⭐⭐ 1 ligne | ⭐⭐⭐ Docker required | ⭐⭐⭐⭐⭐ Change URL |
| LangChain | ⭐⭐⭐⭐⭐ Natif | ⭐⭐⭐⭐ Bon | ⭐⭐⭐ Basique |
| Non-LangChain | ⭐⭐⭐ OpenAI SDK | ⭐⭐⭐⭐ Multi-framework | ⭐⭐⭐⭐⭐ Agnostic |
| Tracing | ⭐⭐⭐⭐⭐ Complet | ⭐⭐⭐⭐⭐ Excellent | ⭐⭐⭐ Basique |
| Évaluation | ⭐⭐⭐⭐⭐ Datasets | ⭐⭐⭐⭐ Evals auto | ⭐⭐ Limité |
| Embedding viz | ❌ Non | ⭐⭐⭐⭐⭐ Excellent | ❌ Non |
| Caching | ❌ Non | ❌ Non | ⭐⭐⭐⭐ Intégré |
| Self-hosted | ❌ Non | ⭐⭐⭐⭐⭐ Oui | ⭐⭐⭐ Enterprise |
| Data privacy | ⚠️ Cloud | ⭐⭐⭐⭐⭐ Total | ⚠️ Cloud |
Recommandations
Choisir LangSmith si :
- ✅ Vous utilisez LangChain massivement
- ✅ Besoin de playground interactif
- ✅ Budget OK ($39+/mois)
- ✅ Datasets d’évaluation importants
Choisir Phoenix si :
- ✅ Budget limité (gratuit)
- ✅ Besoin self-hosted (compliance, privacy)
- ✅ Analyse d’embeddings importante
- ✅ OK avec self-manage infrastructure
Choisir Helicone si :
- ✅ Pas de framework (OpenAI SDK direct)
- ✅ Setup ultra-simple requis
- ✅ Besoin de caching pour économiser
- ✅ Tracking par user essentiel
Combiner plusieurs :
# LangSmith pour LangChain chains
LANGCHAIN_TRACING_V2=true
# Helicone pour appels OpenAI directs
openai_client = OpenAI(base_url="https://oai.helicone.ai/v1")
Alerting et détection d’anomalies
Configurer des alertes critiques
Alertes recommandées :
# 1. Coûts excessifs
if daily_cost > budget_daily * 1.5:
send_alert(f"🚨 Daily cost: ${daily_cost:.2f} (budget: ${budget_daily})")
# 2. Latence dégradée
if latency_p95 > 5000: # 5s
send_alert(f"⚠️ Latency p95: {latency_p95}ms (SLA: 3000ms)")
# 3. Taux d'erreur élevé
if error_rate > 0.05: # 5%
send_alert(f"❌ Error rate: {error_rate:.1%}")
# 4. Rate limits atteints
if rate_limit_errors > 10:
send_alert(f"⏱️ Rate limit hit {rate_limit_errors} times in 1h")
# 5. Qualité dégradée
if avg_relevance_score < 0.7:
send_alert(f"📉 Relevance score dropped to {avg_relevance_score:.2f}")
Intégration Slack
import requests
def send_slack_alert(message: str, channel: str = "#alerts"):
"""Envoie alerte Slack"""
webhook_url = os.getenv("SLACK_WEBHOOK_URL")
payload = {
"channel": channel,
"username": "AI Monitoring",
"icon_emoji": ":robot_face:",
"text": message
}
response = requests.post(webhook_url, json=payload)
return response.status_code == 200
# Usage
send_slack_alert("🚨 Daily AI cost exceeded $100: $127.50")
Détection d’anomalies
Méthode simple : écarts types :
import numpy as np
from collections import deque
class AnomalyDetector:
"""Détecte anomalies par écart à la moyenne"""
def __init__(self, window_size=100, threshold=3):
self.window = deque(maxlen=window_size)
self.threshold = threshold # Nombre d'écarts types
def is_anomaly(self, value: float) -> bool:
"""Retourne True si anomalie détectée"""
if len(self.window) < 10:
self.window.append(value)
return False
mean = np.mean(self.window)
std = np.std(self.window)
# Z-score
z_score = abs((value - mean) / std) if std > 0 else 0
self.window.append(value)
return z_score > self.threshold
# Usage
latency_detector = AnomalyDetector()
cost_detector = AnomalyDetector()
# Pour chaque requête
if latency_detector.is_anomaly(current_latency):
send_alert(f"⚠️ Latency spike: {current_latency}ms")
if cost_detector.is_anomaly(current_cost):
send_alert(f"💸 Cost anomaly: ${current_cost:.4f}")
🔔 Configurez au minimum 3 alertes : coûts quotidiens, latence p95, et taux d’erreur. Slack/email pour notifs temps réel. PagerDuty pour incidents critiques.
A/B testing de prompts
Pourquoi A/B tester ?
# Version A
prompt_a = "Résume ce texte en 2 phrases."
# Version B
prompt_b = "Tu es un expert en résumés. Résume ce texte en 2 phrases concises."
# Laquelle performe mieux ?
# → A/B test pour le savoir
Implémentation simple
import random
class PromptABTest:
"""A/B testing de prompts"""
def __init__(self):
self.variants = {}
self.results = {}
def add_variant(self, name: str, prompt_template: str, weight: float = 0.5):
"""Ajoute variant"""
self.variants[name] = {
"template": prompt_template,
"weight": weight
}
self.results[name] = {
"count": 0,
"total_latency": 0,
"total_cost": 0,
"thumbs_up": 0,
"thumbs_down": 0
}
def get_variant(self):
"""Sélectionne variant (weighted random)"""
variants = list(self.variants.keys())
weights = [self.variants[v]["weight"] for v in variants]
return random.choices(variants, weights=weights)[0]
def log_result(self, variant: str, latency: float, cost: float, user_feedback: int):
"""Log résultat (user_feedback: 1 = 👍, -1 = 👎, 0 = none)"""
self.results[variant]["count"] += 1
self.results[variant]["total_latency"] += latency
self.results[variant]["total_cost"] += cost
if user_feedback == 1:
self.results[variant]["thumbs_up"] += 1
elif user_feedback == -1:
self.results[variant]["thumbs_down"] += 1
def get_stats(self):
"""Retourne statistiques"""
stats = {}
for variant, data in self.results.items():
if data["count"] == 0:
continue
stats[variant] = {
"requests": data["count"],
"avg_latency": data["total_latency"] / data["count"],
"avg_cost": data["total_cost"] / data["count"],
"satisfaction": (
data["thumbs_up"] /
(data["thumbs_up"] + data["thumbs_down"])
if (data["thumbs_up"] + data["thumbs_down"]) > 0
else 0
)
}
return stats
# Usage
ab_test = PromptABTest()
ab_test.add_variant(
"control",
"Résume ce texte en 2 phrases: {text}",
weight=0.5
)
ab_test.add_variant(
"expert",
"Tu es un expert en résumés. Résume ce texte en 2 phrases concises: {text}",
weight=0.5
)
# Pour chaque requête
variant = ab_test.get_variant()
prompt = ab_test.variants[variant]["template"].format(text=user_text)
# Appeler LLM, mesurer latence/coût
response, latency, cost = call_llm(prompt)
# Logger (user_feedback ajouté plus tard via thumbs up/down)
ab_test.log_result(variant, latency, cost, user_feedback=0)
# Après 1000 requêtes, analyser
print(ab_test.get_stats())
# {
# "control": {"requests": 497, "avg_latency": 2.3s, "satisfaction": 0.72},
# "expert": {"requests": 503, "avg_latency": 2.5s, "satisfaction": 0.81}
# }
# → "expert" gagne en satisfaction, déployer à 100%
A/B testing avec LangSmith
from langsmith import Client
client = Client()
# Tag chaque run avec variant
def run_with_variant(variant: str, input_data: dict):
# Ajouter tag
with client.trace(
name="rag_chain",
tags=[f"variant:{variant}"],
metadata={"variant": variant}
):
response = chain.invoke(input_data)
return response
# Analyser dans LangSmith UI :
# Filter runs by tag "variant:A" vs "variant:B"
# Comparer latency, cost, user feedback
Checklist de production
Setup initial
Monitoring :
- Outil de monitoring choisi et configuré
- Tracing activé (LangSmith, Phoenix, ou custom)
- Dashboard créé avec métriques clés
- Coûts trackés par requête et par user
Métriques :
- Latence (p50, p95, p99) loggée
- Taux d’erreur tracké
- Tokens input/output comptés
- Qualité évaluée (relevance, groundedness)
Alerting :
- Alerte coûts quotidiens (>$X)
- Alerte latence (p95 >Xms)
- Alerte taux d’erreur (>X%)
- Alerte rate limits
- Canal de notification configuré (Slack/email)
En production
Quotidien :
- Vérifier dashboard (5 min)
- Checker alertes
- Review erreurs récentes
Hebdomadaire :
- Analyser trends latence
- Revoir coûts (budget vs réel)
- Identifier requêtes coûteuses
- Review feedback utilisateurs
Mensuel :
- A/B test nouveau prompt
- Optimiser prompts (réduire tokens)
- Évaluation qualité sur dataset
- Audit sécurité (PII leaked ?)
- ROI analysis
Conclusion
Le monitoring d’applications IA est fondamentalement différent du monitoring classique. Les métriques clés sont : latence par composant, coûts par requête, tokens utilisés, et qualité des réponses.
Points clés à retenir :
- Trois piliers : Latence, Coûts, Qualité (+ fiabilité)
- Outils existants : LangSmith (LangChain natif), Phoenix (open source), Helicone (proxy simple)
- Alerting essentiel : Budget, latence p95, taux d’erreur
- A/B testing : Optimiser prompts/params en continu
- Tracing complet : Capturer contexte (prompts, retrievals, réponses) pour debug
Stratégie recommandée :
- Démarrer : Helicone (gratuit, 2 min setup) ou LangSmith si LangChain
- Tracker : Latence, coûts, erreurs dès jour 1
- Alerter : Configurer alertes budget et latence
- Itérer : A/B test prompts, analyser top requêtes coûteuses
- Scaler : Migrer vers Phoenix (self-hosted) ou LangSmith Enterprise si besoin
Le monitoring transforme une application IA d’une boîte noire imprévisible en un système observable, optimisable et fiable. Investissez dans le monitoring dès le début - c’est 10x plus dur de l’ajouter après coup.
Tendances 2025 :
- 📊 LLM-as-judge pour évaluation automatique qualité
- 🤖 Anomaly detection IA-powered
- 🔍 Embedding visualization pour debug retrieval
- 💰 FinOps IA : optimisation coûts proactive
Pour aller plus loin
Articles connexes :
Ressources externes :
Outils mentionnés :
- LangSmith
- Phoenix
- Helicone
- Weights & Biases - Alternative pour ML ops