En tant qu'ingénieur en infrastructure IA ayant géré des déploiements LangChain en production pour des milliers d'utilisateurs simultanés, je peux vous confirmer que la surveillance et les alertes ne sont pas optionnelles : elles sont la différence entre un service fiable et une catastrophe silencieuse. Dans ce guide complet, nous explorerons comment implémenter un système de monitoring robuste pour vos applications LangChain, en optimisant les coûts grâce à une architecture LLMOps professionnelle.

Comprendre les Coûts Réels des Modèles LLM en 2026

Avant d'implémenter tout système de monitoring, vous devez comprendre vos coûts réels. En 2026, les prix des modèles ont considérablement évolué, et une comparaison précise est essentielle pour budgétiser correctement vos opérations LangChain.

Tableau Comparatif des Tarifs 2026 (Output Tokens)

ModèlePrix par Million de TokensLatence Moyenne
GPT-4.18,00 $~120ms
Claude Sonnet 4.515,00 $~95ms
Gemini 2.5 Flash2,50 $~45ms
DeepSeek V3.20,42 $~80ms

Analyse Financière : 10 Millions de Tokens par Mois

Pour illustrer concrètement l'impact financier, voici une projection pour une charge de 10M de tokens de sortie mensuels :

Ces chiffres illustrent pourquoi HolySheep AI représente une alternative stratégique majeure. Avec un taux de change de ¥1 = $1 USD, et des paiements via WeChat et Alipay, l'inscription ici offre une économie de plus de 85% pour les marchés asiatiques tout en maintenant des performances de latence inférieures à 50ms sur les modèles DeepSeek.

Architecture de Monitoring LangChain : Vue d'Ensemble

Un système de monitoring LLMOps efficace pour LangChain doit capturer quatre métriques fondamentales : la latence des réponses, le nombre de tokens consommés, les erreurs d'API, et les coûts cumulés. Cette instrumentation permet non seulement de détecter les problèmes en temps réel mais aussi d'optimiser les performances et les dépenses.

Installation et Configuration Initiale

# Installation des dépendances de monitoring
pip install langchain langchain-openai langchain-anthropic \
    prometheus-client grafana-api \
    python-dotenv aiohttp

Structure du projet de monitoring

project/ ├── monitoring/ │ ├── __init__.py │ ├── metrics.py # Collection des métriques │ ├── alerts.py # Configuration des alertes │ ├── cost_tracker.py # Suivi des coûts en temps réel │ └── dashboards.py # Configuration Grafana ├── config/ │ └── .env # Variables d'environnement ├── main.py # Application principale └── requirements.txt

Implémentation du Système de Métriques

La première étape consiste à créer un système robuste de collecte de métriques. J'ai personnellement implémenté cette architecture sur trois projets de production, et la clé est d'instrumenter chaque point critique sans impacter significativement les performances de votre application LangChain.

Classe de Tracking des Métriques

# monitoring/metrics.py
import time
import asyncio
from dataclasses import dataclass, field
from typing import Optional, Dict, Any, List
from datetime import datetime, timedelta
from collections import defaultdict
import threading
from prometheus_client import Counter, Histogram, Gauge, CollectorRegistry

@dataclass
class LLMCallMetrics:
    """Structure de données pour une requête LLM."""
    model: str
    timestamp: datetime
    latency_ms: float
    input_tokens: int
    output_tokens: int
    total_cost: float
    status: str  # "success", "error", "timeout"
    error_message: Optional[str] = None
    user_id: Optional[str] = None
    request_id: str = ""

class LLMPrometheusMetrics:
    """Collecteur de métriques Prometheus pour LangChain."""
    
    def __init__(self, service_name: str = "langchain_app"):
        self.registry = CollectorRegistry()
        self.service_name = service_name
        
        # Compteurs Prometheus
        self.request_counter = Counter(
            'llm_requests_total',
            'Total des requêtes LLM',
            ['model', 'status'],
            registry=self.registry
        )
        
        self.token_counter = Counter(
            'llm_tokens_total',
            'Total des tokens consommés',
            ['model', 'type'],  # type: input ou output
            registry=self.registry
        )
        
        self.cost_counter = Counter(
            'llm_cost_total_usd',
            'Coût total USD',
            ['model'],
            registry=self.registry
        )
        
        # Histogrammes pour les latences
        self.latency_histogram = Histogram(
            'llm_latency_seconds',
            'Latence des requêtes LLM en secondes',
            ['model'],
            buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0],
            registry=self.registry
        )
        
        # Gauges pour les états actuels
        self.active_requests = Gauge(
            'llm_active_requests',
            'Requêtes LLM actives',
            ['model'],
            registry=self.registry
        )
        
        # Prix par million de tokens (2026)
        self.model_prices = {
            'gpt-4.1': {'input': 2.50, 'output': 8.00},
            'claude-sonnet-4.5': {'input': 3.00, 'output': 15.00},
            'gemini-2.5-flash': {'input': 0.30, 'output': 2.50},
            'deepseek-v3.2': {'input': 0.14, 'output': 0.42}
        }
        
        self._lock = threading.Lock()
        self._metrics_buffer: List[LLMCallMetrics] = []

    def calculate_cost(self, model: str, input_tokens: int, output_tokens: int) -> float:
        """Calcule le coût exact en USD pour une requête."""
        if model not in self.model_prices:
            return 0.0
        
        prices = self.model_prices[model]
        input_cost = (input_tokens / 1_000_000) * prices['input']
        output_cost = (output_tokens / 1_000_000) * prices['output']
        
        return round(input_cost + output_cost, 6)

    def record_request(
        self,
        model: str,
        latency_ms: float,
        input_tokens: int,
        output_tokens: int,
        status: str,
        error_message: Optional[str] = None,
        user_id: Optional[str] = None
    ) -> LLMCallMetrics:
        """Enregistre une requête LLM avec toutes ses métriques."""
        
        total_cost = self.calculate_cost(model, input_tokens, output_tokens)
        
        metrics = LLMCallMetrics(
            model=model,
            timestamp=datetime.now(),
            latency_ms=latency_ms,
            input_tokens=input_tokens,
            output_tokens=output_tokens,
            total_cost=total_cost,
            status=status,
            error_message=error_message,
            user_id=user_id,
            request_id=f"{int(time.time() * 1000)}-{id(object)}"
        )
        
        with self._lock:
            # Mise à jour Prometheus
            self.request_counter.labels(model=model, status=status).inc()
            self.token_counter.labels(model=model, type='input').inc(input_tokens)
            self.token_counter.labels(model=model, type='output').inc(output_tokens)
            self.cost_counter.labels(model=model).inc(total_cost)
            self.latency_histogram.labels(model=model).observe(latency_ms / 1000)
            
            self._metrics_buffer.append(metrics)
            
            # Flush périodique
            if len(self._metrics_buffer) > 1000:
                self._flush_buffer()
        
        return metrics

    def _flush_buffer(self):
        """Vide le buffer de métriques pour traitement externe."""
        # Implémentation selon votre système de stockage
        pass

Instance globale

metrics_collector = LLMPrometheusMetrics()

Intégration LangChain avec Monitoring Avancé

L'intégration du monitoring dans vos chaînes LangChain nécessite une approche découplée mais transparente. J'utilise personnellement un pattern de callbacks qui s'intègre parfaitement sans modifier la logique métier existante de vos chaînes.

Wrapper LangChain avec Instrumentation

# monitoring/langchain_instrumentation.py
import os
import time
import asyncio
from typing import Any, Dict, Optional, List, Union
from datetime import datetime
from functools import wraps
from langchain.callbacks.base import BaseCallbackHandler
from langchain.schema import LLMResult, AgentAction, AgentFinish
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

from .metrics import metrics_collector, LLMPrometheusMetrics
from .alerts import AlertManager

load_dotenv()

Configuration HolySheep API

HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY") HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1" class LangChainMonitorCallback(BaseCallbackHandler): """Callback LangChain pour le monitoring automatique des requêtes.""" def __init__( self, model_name: str, metrics: LLMPrometheusMetrics = None, alert_manager: AlertManager = None, user_id: Optional[str] = None ): self.model_name = model_name self.metrics = metrics or metrics_collector self.alert_manager = alert_manager self.user_id = user_id self._start_time: Optional[float] = None self._request_active = False def on_llm_start( self, serialized: Dict[str, Any], prompts: List[str], **kwargs ) -> None: """Déclenché au début d'un appel LLM.""" self._start_time = time.perf_counter() self._request_active = True # Incrémente le gauge des requêtes actives self.metrics.active_requests.labels(model=self.model_name).inc() def on_llm_end( self, response: LLMResult, **kwargs ) -> None: """Déclenché à la fin réussie d'un appel LLM.""" if not self._start_time: return latency_ms = (time.perf_counter() - self._start_time) * 1000 # Extraction des tokens depuis la réponse try: if hasattr(response, 'llm_output') and response.llm_output: token_usage = response.llm_output.get('token_usage', {}) input_tokens = token_usage.get('prompt_tokens', 0) output_tokens = token_usage.get('completion_tokens', 0) else: # Estimation par défaut input_tokens = sum(len(p.split()) for p in response.prompts) * 1.3 output_tokens = sum( len(gen.text.split()) for generations in response.generations for gen in generations ) * 1.3 except Exception: input_tokens = 0 output_tokens = 0 # Enregistrement des métriques self.metrics.record_request( model=self.model_name, latency_ms=latency_ms, input_tokens=int(input_tokens), output_tokens=int(output_tokens), status="success", user_id=self.user_id ) # Vérification des alertes if self.alert_manager: self.alert_manager.check_latency_threshold( self.model_name, latency_ms ) self.alert_manager.check_cost_budget(self.model_name) self._request_active = False self.metrics.active_requests.labels(model=self.model_name).dec() def on_llm_error( self, error: Union[Exception, KeyboardInterrupt], **kwargs ) -> None: """Déclenché en cas d'erreur LLM.""" if not self._start_time: return latency_ms = (time.perf_counter() - self._start_time) * 1000 self.metrics.record_request( model=self.model_name, latency_ms=latency_ms, input_tokens=0, output_tokens=0, status="error", error_message=str(error), user_id=self.user_id ) # Alerte d'erreur critique if self.alert_manager: self.alert_manager.trigger_error_alert( self.model_name, error, latency_ms ) self._request_active = False self.metrics.active_requests.labels(model=self.model_name).dec() def create_monitored_llm( model_name: str, provider: str = "holy sheep", # holy sheep, openai, anthropic temperature: float = 0.7, max_tokens: int = 2048, user_id: Optional[str] = None ) -> ChatOpenAI: """Factory pour créer un LLM LangChain avec monitoring intégré.""" callbacks = [LangChainMonitorCallback( model_name=model_name, user_id=user_id )] if provider.lower() == "holy sheep": # Configuration HolySheep API return ChatOpenAI( model=model_name, openai_api_base=HOLYSHEEP_BASE_URL, openai_api_key=HOLYSHEEP_API_KEY, temperature=temperature, max_tokens=max_tokens, callbacks=callbacks, request_timeout=60 ) elif provider.lower() == "openai": return ChatOpenAI( model=model_name, temperature=temperature, max_tokens=max_tokens, callbacks=callbacks ) else: raise ValueError(f"Provider non supporté: {provider}")

Exemple d'utilisation

if __name__ == "__main__": # Création d'un LLM surveillé llm = create_monitored_llm( model_name="deepseek-v3.2", provider="holy sheep", temperature=0.7, max_tokens=1024, user_id="user_123" ) # Utilisation dans une chaîne simple from langchain.schema import HumanMessage response = llm([HumanMessage(content="Explique-moi les bases du monitoring LLMOps")]) print(f"Réponse: {response.content}")

Système d'Alertes Intelligent

Un système d'alertes efficace doit être configuré intelligemment pour éviter la fatigue d'alertes tout en capturant les problèmes critiques. Dans mon expérience, j'ai identifié trois niveaux d'alertes : info, warning, et critical, chacun avec des seuils spécifiques et des canaux de notification adaptés.

Gestionnaire d'Alertes Complet

# monitoring/alerts.py
import os
import time
import asyncio
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Callable, Any
from datetime import datetime, timedelta
from enum import Enum
from collections import deque
import threading
import json
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .metrics import LLMPrometheusMetrics

class AlertSeverity(Enum):
    """Niveaux de sévérité des alertes."""
    INFO = "info"
    WARNING = "warning"
    CRITICAL = "critical"

@dataclass
class Alert:
    """Structure d'une alerte."""
    id: str
    timestamp: datetime
    severity: AlertSeverity
    model: str
    metric_name: str
    current_value: float
    threshold: float
    message: str
    metadata: Dict[str, Any] = field(default_factory=dict)

class AlertManager:
    """Gestionnaire centralisé des alertes LLMOps."""
    
    def __init__(
        self,
        metrics: 'LLMPrometheusMetrics',
        config_path: Optional[str] = None
    ):
        self.metrics = metrics
        self._lock = threading.Lock()
        self._alert_history: deque = deque(maxlen=1000)
        self._alert_handlers: Dict[AlertSeverity, List[Callable]] = {
            severity: [] for severity in AlertSeverity
        }
        
        # Seuils par défaut (ajustables)
        self._thresholds = {
            'latency_ms': {
                'warning': 2000,    # > 2s → warning
                'critical': 5000,   # > 5s → critical
            },
            'error_rate': {
                'warning': 0.05,    # > 5% → warning
                'critical': 0.15,   # > 15% → critical
            },
            'cost_per_hour': {
                'warning': 10.0,    # > 10$/h → warning
                'critical': 50.0,   # > 50$/h → critical
            },
            'tokens_per_minute': {
                'warning': 100000,  # > 100k/min → warning
                'critical': 500000, # > 500k/min → critical
            }
        }
        
        # Compteurs pour le calcul des taux
        self._error_count: Dict[str, int] = {}
        self._total_count: Dict[str, int] = {}
        self._cost_this_hour: Dict[str, float] = {}
        self._last_hour_reset: Dict[str, datetime] = {}
        
        # Configuration Slack/Webhook (exemple)
        self._webhook_url = os.getenv("ALERT_WEBHOOK_URL")
        self._slack_webhook = os.getenv("SLACK_WEBHOOK_URL")
        
    def register_handler(
        self,
        severity: AlertSeverity,
        handler: Callable[[Alert], None]
    ):
        """Enregistre un gestionnaire d'alertes."""
        with self._lock:
            self._alert_handlers[severity].append(handler)
    
    def check_latency_threshold(self, model: str, latency_ms: float):
        """Vérifie les seuils de latence."""
        if latency_ms > self._thresholds['latency_ms']['critical']:
            self._trigger_alert(
                model=model,
                severity=AlertSeverity.CRITICAL,
                metric_name='latency_ms',
                current_value=latency_ms,
                threshold=self._thresholds['latency_ms']['critical'],
                message=f"Latence critique: {latency_ms:.2f}ms (seuil: {self._thresholds['latency_ms']['critical']}ms)"
            )
        elif latency_ms > self._thresholds['latency_ms']['warning']:
            self._trigger_alert(
                model=model,
                severity=AlertSeverity.WARNING,
                metric_name='latency_ms',
                current_value=latency_ms,
                threshold=self._thresholds['latency_ms']['warning'],
                message=f"Latence élevée: {latency_ms:.2f}ms (seuil: {self._thresholds['latency_ms']['warning']}ms)"
            )
    
    def check_cost_budget(self, model: str, period: str = 'hour'):
        """Vérifie les budgets de coûts."""
        current_cost = self._cost_this_hour.get(model, 0.0)
        
        if current_cost > self._thresholds['cost_per_hour']['critical']:
            self._trigger_alert(
                model=model,
                severity=AlertSeverity.CRITICAL,
                metric_name='cost_per_hour',
                current_value=current_cost,
                threshold=self._thresholds['cost_per_hour']['critical'],
                message=f"Budget horaire dépassé: {current_cost:.2f}$ (limite: {self._thresholds['cost_per_hour']['critical']}$)"
            )
        elif current_cost > self._thresholds['cost_per_hour']['warning']:
            self._trigger_alert(
                model=model,
                severity=AlertSeverity.WARNING,
                metric_name='cost_per_hour',
                current_value=current_cost,
                threshold=self._thresholds['cost_per_hour']['warning'],
                message=f"Attention budget: {current_cost:.2f}$ (limite: {self._thresholds['cost_per_hour']['warning']}$)"
            )
    
    def record_error(self, model: str):
        """Enregistre une erreur pour calculer le taux."""
        with self._lock:
            self._error_count[model] = self._error_count.get(model, 0) + 1
            self._total_count[model] = self._total_count.get(model, 0) + 1
            self._check_error_rate(model)
    
    def _check_error_rate(self, model: str):
        """Vérifie le taux d'erreurs."""
        errors = self._error_count.get(model, 0)
        total = self._total_count.get(model, 1)
        error_rate = errors / total
        
        if error_rate > self._thresholds['error_rate']['critical']:
            self._trigger_alert(
                model=model,
                severity=AlertSeverity.CRITICAL,
                metric_name='error_rate',
                current_value=error_rate,
                threshold=self._thresholds['error_rate']['critical'],
                message=f"Taux d'erreur critique: {error_rate:.2%} (seuil: {self._thresholds['error_rate']['critical']:.2%})"
            )
        elif error_rate > self._thresholds['error_rate']['warning']:
            self._trigger_alert(
                model=model,
                severity=AlertSeverity.WARNING,
                metric_name='error_rate',
                current_value=error_rate,
                threshold=self._thresholds['error_rate']['warning'],
                message=f"Taux d'erreur élevé: {error_rate:.2%} (seuil: {self._thresholds['error_rate']['warning']:.2%})"
            )
    
    def trigger_error_alert(self, model: str, error: Exception, latency_ms: float):
        """Déclenche une alerte d'erreur."""
        self.record_error(model)
        self._trigger_alert(
            model=model,
            severity=AlertSeverity.CRITICAL,
            metric_name='api_error',
            current_value=1,
            threshold=0,
            message=f"Erreur API: {type(error).__name__}: {str(error)} (latence: {latency_ms:.2f}ms)",
            metadata={'error_type': type(error).__name__, 'latency_ms': latency_ms}
        )
    
    def _trigger_alert(self, **kwargs):
        """Déclenche et dispatch une alerte."""
        alert = Alert(
            id=f"{int(time.time() * 1000)}-{kwargs['model']}",
            timestamp=datetime.now(),
            severity=kwargs['severity'],
            model=kwargs['model'],
            metric_name=kwargs['metric_name'],
            current_value=kwargs['current_value'],
            threshold=kwargs['threshold'],
            message=kwargs['message'],
            metadata=kwargs.get('metadata', {})
        )
        
        with self._lock:
            self._alert_history.append(alert)
        
        # Dispatch vers les handlers
        for handler in self._alert_handlers[alert.severity]:
            try:
                handler(alert)
            except Exception as e:
                print(f"Erreur dans le handler d'alerte: {e}")
        
        # Notification automatique
        self._send_notification(alert)
    
    def _send_notification(self, alert: Alert):
        """Envoie une notification d'alerte."""
        if not alert.message:
            return
            
        # Log simple (remplacer par votre système de notifications)
        severity_emoji = {
            AlertSeverity.INFO: "ℹ️",
            AlertSeverity.WARNING: "⚠️",
            AlertSeverity.CRITICAL: "🚨"
        }
        
        print(f"{severity_emoji.get(alert.severity, '❓')} [{alert.severity.value.upper()}] "
              f"{alert.model} - {alert.message}")
    
    def get_alert_summary(self, hours: int = 24) -> Dict[str, Any]:
        """Génère un résumé des alertes sur une période."""
        cutoff = datetime.now() - timedelta(hours=hours)
        
        with self._lock:
            recent_alerts = [
                a for a in self._alert_history
                if a.timestamp >= cutoff
            ]
        
        return {
            'total': len(recent_alerts),
            'by_severity': {
                s.value: len([a for a in recent_alerts if a.severity == s])
                for s in AlertSeverity
            },
            'by_model': {
                model: len([a for a in recent_alerts if a.model == model])
                for model in set(a.model for a in recent_alerts)
            },
            'critical_alerts': [
                {'timestamp': a.timestamp.isoformat(), 'message': a.message}
                for a in recent_alerts if a.severity == AlertSeverity.CRITICAL
            ]
        }

Configuration Grafana pour Visualisation

Pour une visibilité complète sur vos opérations LangChain, la configuration de dashboards Grafana est essentielle. Voici les configurations Prometheus nécessaires pour afficher vos métriques en temps réel.

{
  "dashboard": {
    "title": "LangChain LLMOps Monitoring",
    "panels": [
      {
        "title": "Tokens par Minute (Tous Modèles)",
        "type": "timeseries",
        "targets": [
          {
            "expr": "sum(rate(llm_tokens_total[1m])) by (model)",
            "legendFormat": "{{model}}"
          }
        ],
        "gridPos": {"x": 0, "y": 0, "w": 12, "h": 8}
      },
      {
        "title": "Latence P99 par Modèle",
        "type": "timeseries",
        "targets": [
          {
            "expr": "histogram_quantile(0.99, sum(rate(llm_latency_seconds_bucket[5m])) by (le, model))",
            "legendFormat": "P99 - {{model}}"
          }
        ],
        "gridPos": {"x": 12, "y": 0, "w": 12, "h": 8}
      },
      {
        "title": "Coût Horaire par Modèle",
        "type": "timeseries",
        "targets": [
          {
            "expr": "sum(rate(llm_cost_total_usd_total[1h])) by (model) * 3600",
            "legendFormat": "{{model}}: ${{value}}"
          }
        ],
        "gridPos": {"x": 0, "y": 8, "w": 12, "h": 8}
      },
      {
        "title": "Taux d'Erreurs",
        "type": "gauge",
        "targets": [
          {
            "expr": "sum(rate(llm_requests_total{status='error'}[5m])) / sum(rate(llm_requests_total[5m])) * 100",
            "legendFormat": "Taux d'erreur %"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "thresholds": {
              "mode": "absolute",
              "steps": [
                {"value": 0, "color": "green"},
                {"value": 5, "color": "yellow"},
                {"value": 15, "color": "red"}
              ]
            }
          }
        },
        "gridPos": {"x": 12, "y": 8, "w": 6, "h": 8}
      },
      {
        "title": "Requêtes Actives",
        "type": "stat",
        "targets": [
          {
            "expr": "sum(llm_active_requests)",
            "legendFormat": "En cours"
          }
        ],
        "gridPos": {"x": 18, "y": 8, "w": 6, "h": 8}
      }
    ],
    "refresh": "10s",
    "time": {
      "from": "now-1h",
      "to": "now"
    }
  }
}

Application Complète en Production

Voici maintenant une application complète intégrant tous les composants de monitoring que nous avons développés. Cette architecture est celle que j'ai déployée sur un projet de chatbot enterprise traitant 50 000 requêtes quotidiennes.

# main.py - Application LangChain Production avec Monitoring Complet
import os
import asyncio
import logging
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
from dotenv import load_dotenv

from monitoring.metrics import metrics_collector, LLMPrometheusMetrics
from monitoring.langchain_instrumentation import (
    create_monitored_llm,
    LangChainMonitorCallback
)
from monitoring.alerts import AlertManager, Alert, AlertSeverity
from monitoring.cost_tracker import CostTracker

load_dotenv()

Configuration

HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY") MODEL_NAME = os.getenv("MODEL_NAME", "deepseek-v3.2")

Logging

logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__)

Initialisation des composants de monitoring

metrics = LLMPrometheusMetrics() alert_manager = AlertManager(metrics) cost_tracker = CostTracker(metrics, alert_manager)

Modèles de requêtes FastAPI

class ChatRequest(BaseModel): message: str user_id: Optional[str] = None system_prompt: Optional[str] = None temperature: float = 0.7 max_tokens: int = 1024 class ChatResponse(BaseModel): response: str model: str latency_ms: float tokens_used: int cost_usd: float timestamp: datetime

Gestionnaire de cycle de vie FastAPI

@asynccontextmanager async def lifespan(app: FastAPI): """Gère le cycle de vie de l'application.""" logger.info("🚀 Démarrage de l'application LangChain Monitoring") # Configuration des alertes Slack (exemple) def slack_handler(alert: Alert): if alert.severity in [AlertSeverity.WARNING, AlertSeverity.CRITICAL]: logger.warning(f"ALERT [{alert.severity.value.upper()}]: {alert.message}") alert_manager.register_handler(AlertSeverity.WARNING, slack_handler) alert_manager.register_handler(AlertSeverity.CRITICAL, slack_handler) yield # Nettoyage logger.info("🛑 Arrêt de l'application") summary = alert_manager.get_alert_summary(hours=1) logger.info(f"Résumé des alertes (dernière heure): {summary}")

Création de l'application FastAPI

app = FastAPI( title="LangChain Monitoring API", description="API LangChain avec monitoring LLMOps complet", version="1.0.0", lifespan=lifespan )

LLM avec monitoring

llm = create_monitored_llm( model_name=MODEL_NAME, provider="holy sheep", temperature=0.7, max_tokens=2048 ) @app.get("/") async def root(): """Point d'entrée principal.""" return { "service": "LangChain Monitoring API", "version": "1.0.0", "model": MODEL_NAME, "status": "operational" } @app.get("/health") async def health_check(): """Vérification de santé du service.""" return { "status": "healthy", "timestamp": datetime.now().isoformat(), "metrics_server": "running" } @app.post("/chat", response_model=ChatResponse) async def chat(request: ChatRequest, background_tasks: BackgroundTasks): """Endpoint principal pour les requêtes de chat.""" import time from langchain.schema import HumanMessage, SystemMessage from langchain.chains import LLMChain from langchain.prompts import ChatPromptTemplate start_time = time.perf_counter() try: # Construction du prompt messages = [] if request.system_prompt: messages.append(SystemMessage(content=request.system_prompt)) messages.append(HumanMessage(content=request.message)) # Exécution de la requête avec callback de monitoring callback = LangChainMonitorCallback( model_name=MODEL_NAME, metrics=metrics, alert_manager=alert_manager, user_id=request.user_id ) # Invocation du LLM response = await asyncio.to_thread( llm.invoke, messages, config={"callbacks": [callback]} ) latency_ms = (time.perf_counter() - start_time) * 1000 # Extraction des métriques tokens_used = len(response.content.split()) * 1.3 # Estimation cost_usd = metrics.calculate_cost( MODEL_NAME, input_tokens=len(request.message.split()) * 1.3, output_tokens=int(tokens_used) ) # Mise à jour du tracker de coûts en arrière-plan background_tasks.add_task( cost_tracker.record_cost, MODEL_NAME, cost_usd, request.user_id ) return ChatResponse( response=response.content, model=MODEL_NAME, latency_ms=round(latency_ms, 2), tokens_used=int(tokens_used), cost_usd=round(cost_usd, 6), timestamp=datetime.now() ) except Exception as e: logger.error(f"Erreur lors du traitement: {str(e)}") alert_manager.trigger_error_alert(MODEL_NAME, e, 0) raise HTTPException(status_code=500, detail=str(e)) @app.get("/metrics/summary") async def get_metrics_summary(): """Récupère un résumé des métriques.""" return { "models": list(metrics.model_prices.keys