Aller au contenu
NDNicolas Desjardins, retour à l'aperçu
CV
Démo en ligne, code ouvert04 / Phase 4 · Livré

Contrôle qualité de l'IA

Passerelle LLMOps de production : évaluation CI/CD et optimisation de la latence

Se place devant chaque appel à l'IA. Une question à laquelle il a déjà répondu revient de sa mémoire en quelques millisecondes, une question simple va à un modèle économique et une question difficile à un modèle puissant, et chaque réponse est consignée avec ce qu'elle a coûté. Avant qu'un changement soit livré, une série fixe de questions est reposée, et le changement est bloqué si les réponses se sont dégradées.

Le problème réglé

La facture de modèles grimpe à chaque requête, la latence de queue dépend de la file d'attente du fournisseur ce jour-là, et personne ne peut démontrer que la modification de prompt de la semaine dernière n'a pas dégradé les réponses. Une cache sémantique ne fait économiser que si elle ne sert jamais la réponse d'une autre question, et un seuil de qualité n'est réel que si un changement fautif ne peut pas être fusionné.

Ce qui a été mesuré

  • Mesuré
  • Cible
  • 0 / 78

    questions semblables servies avec une mauvaise réponse en cache à 0,96, 23 octobre 2026

    mesuré
  • 79,8 %

    de la dépense de modèles au prix courant évitée sur une relecture de 250 questions

    mesuré
  • 264

    tests hermétiques, sans réseau, sans Redis, sans clé

    mesuré
  • 0,00 $

    de coût marginal par requête sur la pile gratuite

    mesuré
Comment ça marchepour les équipes techniques

En termes techniques

Une passerelle FastAPI placée devant chaque appel de modèle, avec une route compatible OpenAI vers laquelle n'importe quel client peut pointer. Les questions répétées reviennent d'une cache vectorielle Redis 8 sans appel de modèle, les nouvelles sont évaluées selon leur complexité et envoyées à une chaîne de modèles légère ou lourde avec relève automatique, et chaque étape est un segment d'une même trace, avec jetons et dollars, reproduite dans Langfuse. Un flux GitHub Actions fait répondre le vrai pipeline à un jeu de questions de référence, fait juger les réponses par DeepEval et Ragas, et fait échouer la pull request quand le taux d'hallucination dépasse 5 % ou que la fidélité passe sous 0,95. Fonctionne à coût nul sur des modèles gratuits.

Architecture

Une garde normalise chaque requête, neutralise les jetons de gabarit de clavardage et les balises usurpées, signale les formulations d'injection et applique des plafonds de jetons en entrée et en sortie par palier. La cache sémantique vérifie d'abord une clé exacte, puis plonge la question avec bge-small sur le processeur et demande à Redis les trois questions en cache les plus proches dans le même espace de noms (version du prompt, empreinte du corpus, modèle de plongement), au moyen d'un index HNSW en distance cosinus. Un voisin au-dessus du seuil de 0,96 n'est servi que si une garde de contraste ne trouve aucune contradiction entre les deux questions sur un nombre, un code ou un terme opposé : mesuré, c'est ce qui empêche de servir la limite de repas au pays pour la question sur l'étranger. Un échec de cache passe par une recherche hybride (plongements et BM25 fusionnés par rang réciproque) et un routeur de complexité fait de signaux nommés, qui ne coûte aucun appel de modèle et s'explique dans la trace. La chaîne du palier choisi répond, et un 429, un délai dépassé, un modèle retiré, un refus ou une réponse sans citation passe la requête au modèle suivant, puis à l'autre palier; une clé invalide l'arrête. Les réponses sont diffusées en Server-Sent Events, et chaque appel de modèle est chiffré deux fois : ce qu'il a coûté, et ce que les mêmes jetons coûteraient au prix courant de Claude Haiku 5.5 ou de Claude Opus 5.5. Chaque étape est un segment reproduit dans Langfuse comme une observation typée, les générations avec le détail des jetons et du coût. La barrière de qualité exécute une vérification déterministe de la recherche à chaque pull request et, quand les réponses ont pu changer, DeepEval et Ragas comme juges LLM sur 37 cas de référence, puis fait échouer la compilation selon les seuils et dès qu'une métrique passe sous la référence acceptée.

Pile technique

  • Python 3.12
  • FastAPI
  • Pydantic v2
  • Server-Sent Events
  • Redis 8 vector search
  • fastembed (bge-small)
  • Langfuse
  • DeepEval
  • Ragas
  • GitHub Actions
  • OpenRouter
  • Docker
  • GitHub Pages

Éléments de preuve

  • Compromis du seuil mesuré le 23 octobre 2026 avec le vrai modèle de plongement : deux reformulations d'une même question ont une similarité médiane de 0,889, tandis que deux questions semblables aux réponses différentes atteignent 0,961. Au seuil de 0,96, la cache seule servirait la mauvaise réponse à 1 question semblable sur 78 ; avec la garde de contraste, à aucune, et la garde n'a bloqué aucune des 78 vraies reformulations.
  • Sur une relecture de 250 questions de centre d'assistance passées dans le vrai pipeline, la cache a répondu à 199 requêtes (172 répétitions exactes, 27 correspondances sémantiques) sans aucune mauvaise réponse, évitant 79,8 % de la dépense de modèles au prix courant. La même relecture sur Redis 8 a donné les mêmes réponses en cache, avec des recherches de 2,4 ms (exacte) et de 3,6 ms (sémantique, plongement compris) en médiane.
  • Vérification de la recherche sans modèle, exécutée à chaque pull request : le passage qui contient la réponse figurait parmi les quatre premiers pour les 30 questions de référence auxquelles on peut répondre, avec une précision de contexte de 0,975.
  • L'image de production tourne dans un conteneur de 512 Mo à 348 Mio, modèle de plongement intégré, se connecte à Redis 8, crée son index vectoriel et a servi une réponse sémantique en cache en 15 ms de bout en bout.
  • 264 tests hermétiques : ni réseau, ni Redis, ni clé. Les vrais adaptateurs SDK OpenAI et Anthropic tournent contre un transport simulé, le SDK Langfuse exporte en mémoire, les métriques de DeepEval passent par le propre juge de la passerelle, et la page du projet est tenue à ses enregistrements par des tests. Un test de plus fait tourner la cache sur un vrai Redis 8.

Pipeline de requête

  1. 01

    Garde

    Unicode replié, jetons de gabarit et balises usurpées neutralisés, injections signalées, plafonds de jetons

  2. 02

    Cache

    Clé exacte, puis KNN Redis au-dessus de 0,96 en cosinus, refusé quand les questions se contredisent

  3. 03

    Routage

    Des signaux nommés et l'étendue de la recherche choisissent la chaîne légère ou lourde, sans appel de modèle

  4. 04branche

    Repli

    429, délai dépassé, modèle retiré ou réponse sans citation : modèle suivant, puis l'autre palier

  5. 05

    Trace

    Un segment par étape vers Langfuse : jetons, coût réel, coût au prix courant, p50/p95/p99

  6. 06

    Barrière

    DeepEval et Ragas sur un jeu de référence; une pull request échoue au-delà de 5 % d'hallucination