Régler le cache sémantique d'un proxy LLM
9 septembre 2026
Un cache exact (même prompt octet pour octet) ne sert à rien face à un agent conversationnel : la même intention s'exprime rarement deux fois avec les mêmes mots. Un cache sémantique compare le sens plutôt que le texte, au prix d'un risque nouveau — répondre à la mauvaise question.
Le principe : comparer des vecteurs, pas des chaînes
Chaque dernier message utilisateur est transformé en embedding — un vecteur de quelques centaines à quelques milliers de dimensions qui représente son sens. La question entrante est comparée aux vecteurs déjà en cache via une similarité cosinus : un score entre -1 et 1, où 1 signifie « sens identique ».
Si le meilleur score dépasse un seuil configuré, le cache renvoie la réponse stockée sans jamais appeler le provider LLM. En dessous, c'est un miss : la requête part normalement, et sa réponse alimente le cache pour la prochaine fois.
Le seuil est un arbitrage, pas un détail
Un seuil trop bas (permissif) fait remonter des réponses à des questions différentes — un faux hit, invisible pour l'utilisateur qui reçoit une réponse plausible mais fausse. Un seuil trop haut (strict) ne déclenche presque jamais, et le cache ne rapporte rien.
Il n'existe pas de seuil universel : il dépend de la variance du trafic. Un agent de support qui répond à des questions très répétitives (« quel est le statut de ma commande ? ») tolère un seuil permissif. Un agent qui traite des requêtes ouvertes et nuancées a besoin d'un seuil strict, sous peine de mélanger des intentions voisines mais distinctes.
Trois profils plutôt qu'un curseur
Exposer un seuil brut (« 0.93 ») à un utilisateur qui ne connaît pas la distribution de ses embeddings revient à lui demander de deviner. Une meilleure interface propose des profils nommés — strict, permissif, agressif — chacun mappé à un seuil calibré en amont sur un modèle d'embedding donné.
Le point à retenir : ces seuils ne sont pas portables d'un modèle d'embedding à l'autre. 0.95 sur `text-embedding-3-small` ne veut rien dire pour un autre modèle — la géométrie de l'espace vectoriel change. Toute calibration doit être refaite si le modèle d'embedding change.
Ce qui ne doit jamais être caché
Une réponse portant des appels d'outils (tool calls) ne devrait jamais être mise en cache telle quelle : elle correspond à une action effectuée dans un contexte précis, et la rejouer pour une question sémantiquement proche mais différente peut déclencher la mauvaise action.
Un flux streamé interrompu par le client (déconnexion en cours de génération) ne doit pas non plus être caché : ce serait resservir une réponse tronquée comme si elle était complète à la prochaine requête correspondante.
Un détail d'implémentation qui compte : le budget candidat
Comparer une requête à des milliers d'entrées en cache coûte cher si c'est fait naïvement. Deux leviers pratiques : plafonner le nombre de candidats comparés à chaque lookup (les plus récents d'abord — la localité temporelle du trafic conversationnel rend les vieilles entrées de moins en moins pertinentes), et vectoriser la comparaison en un seul produit matriciel plutôt qu'une boucle.