La documentation de politique de Microsoft dit elle-même qu'un score threshold au-dessus de 0.2 peut conduire à un cache mismatch, ce qui est une manière polie de dire que la gateway répondra à une question que l'appelant n'a pas posée. Tous les autres mécanismes de cette série ont échoué bruyamment : un 429, un 503, un batch job expiré. Un cache sémantique échoue en renvoyant une réponse fluide, bien formée et sûre d'elle qui appartient au prompt de quelqu'un d'autre. La partie 5 a rendu la dépense attribuable. Cette partie la réduit, et traite le mécanisme qui s'en charge comme une surface de justesse plutôt que comme un levier de coût.

Ce dont le cache a réellement besoin avant de fonctionner

Le semantic caching dans API Management a une liste de prérequis qui engage un choix d'infrastructure impossible à revisiter plus tard, et elle a donc sa place avant la politique plutôt qu'après.

  • Un second déploiement de modèle, pour les embeddings. Le déploiement de chat completion sert les appels des consommateurs ; un déploiement d'embeddings séparé est ce que le cache utilise pour transformer un prompt en vecteur. C'est une seconde ligne de capacité, un second quota, et une seconde chose à monitorer.
  • Une authentification par managed identity d'API Management vers les APIs de modèles, que la politique exige : embeddings-backend-auth doit valoir system-assigned. Il n'y a pas d'option par key ici, ce qui est une bonne contrainte.
  • Azure Managed Redis avec le module RediSearch activé, configuré comme external cache sur l'instance API Management.

C'est le dernier point qui porte le piège. RediSearch ne peut être activé qu'à la création du cache, et ne peut pas être ajouté à un cache existant. Une plateforme qui exploite déjà une instance Redis pour les sessions ou l'état de rate-limit ne peut pas simplement activer le semantic caching : il lui faut un nouveau cache, provisionné avec le module, et le Terraform de la partie 2 est l'endroit où cette décision se consigne. La découvrir au milieu d'un sprint qui avait budgété un après-midi pour « activer le cache » est l'issue ordinaire.

La paire de politiques

Le lookup va dans l'inbound, le store dans l'outbound, et chacune des deux ne peut apparaître qu'une seule fois par section de politique. Les deux sont disponibles aux portées globale, product, API et operation, et sur les gateways classic, v2, consumption et self-hosted.

<inbound>
  <base />
  <llm-semantic-cache-lookup
      score-threshold="0.05"
      embeddings-backend-id="embeddings-backend"
      embeddings-backend-auth="system-assigned"
      ignore-system-messages="true"
      max-message-count="6">
    <vary-by>@(context.Subscription.Id)</vary-by>
  </llm-semantic-cache-lookup>
  <rate-limit calls="10" renewal-period="60" />
</inbound>
<outbound>
  <llm-semantic-cache-store duration="60" />
  <base />
</outbound>

Quatre de ces attributs sont des décisions plutôt que du boilerplate.

score-threshold définit à quel point un prompt entrant doit correspondre à un prompt en cache, sur une échelle de 0.0 à 1.0, où les valeurs basses exigent une similarité sémantique plus élevée. C'est l'inverse de ce que la plupart des gens supposent à la première lecture, et se tromper de sens transforme un cache conservateur en cache trop permissif. La recommandation de Microsoft est de commencer bas, autour de 0.05, et d'ajuster vers le ratio hit-to-miss voulu, avec l'avertissement explicite qu'au-dessus de 0.2 les mismatches deviennent probables et que les cas d'usage sensibles doivent rester plus bas.

ignore-system-messages="true" retire les messages système avant l'évaluation de la similarité, et est recommandé. Cela compte plus qu'il n'y paraît : deux applications avec des questions utilisateur identiques et des prompts système différents paraîtraient sinon différentes au cache, et une seule application dont le prompt système est modifié invaliderait tout son cache sans aucune raison sémantique.

max-message-count saute la mise en cache dès qu'un dialogue compte plus que le nombre spécifié de messages restants. Les longues conversations sont exactement l'endroit où un match sémantique est le moins fiable, parce que le sens du dernier message dépend de tours que l'embedding n'a jamais vus.

vary-by est la frontière d'isolation, et la section suivante explique pourquoi c'est un contrôle de sécurité.

vary-by n'est pas un raffinement de partitionnement

Sans vary-by, un seul cache est partagé entre tous les appelants de l'API. Un prompt de l'assistant du service client contenant les détails d'un client peut être servi depuis une completion en cache, et un prompt sémantiquement similaire venant d'un autre tenant peut être servi depuis cette même entrée. La documentation le dit sans détour : contrôlez l'accès inter-utilisateurs aux entrées du cache en spécifiant vary-by avec des identifiants précis d'utilisateur ou de groupe d'utilisateurs.

Pour cette plateforme, le minimum est l'ID de subscription, ce qui aligne la frontière du cache exactement sur la frontière de tenancy construite par la partie 5. Là où une application sert des utilisateurs finaux dont les données ne doivent pas se mélanger, la bonne valeur est un claim du token validé plutôt que quoi que ce soit que l'appelant peut poser dans un en-tête, et c'est la raison pour laquelle le validate-jwt de la partie 5 écrit son token dans output-token-variable-name. Un vary-by qui lit un en-tête non validé est une fuite de données inter-tenants avec un fichier de configuration pour cause racine.

Le coût d'un partitionnement étroit est un hit rate plus bas, et ce compromis est réel. C'est aussi le bon défaut : un cache qui ne fuit jamais et économise 20 pour cent bat celui qui économise 45 pour cent et finit par renvoyer la réponse du mauvais client.

La dépendance que vous venez d'ajouter

Le cache est dans le chemin de requête de chaque appel, ce qui met Redis sur le chemin critique de la gateway. La recommandation de Microsoft est précise et vaut d'être suivie à la lettre : placer une politique rate-limit ou rate-limit-by-key immédiatement après le cache lookup, pour éviter que le backend ne soit submergé si le cache n'est pas disponible.

Déroulez cette défaillance jusqu'au bout. Un jour normal, une part significative du trafic n'atteint jamais un modèle. Si Redis devient indisponible, chacune de ces requêtes devient un vrai appel de completion, instantanément, contre un quota dimensionné pour le régime permanent avec cache. La panne du cache ne dégrade pas la plateforme, elle multiplie sa charge, et le rate-limit est ce qui convertit cela d'une panne en throttling. C'est la même leçon qu'avec n'importe quel cache devant une base de données, et elle est plus facile à oublier ici parce que le cache a été introduit comme une optimisation de coût plutôt que comme une dépendance de capacité.

Le déploiement d'embeddings mérite la même réflexion. Chaque lookup transforme le prompt entrant en vecteur, donc le modèle d'embeddings a besoin d'assez de capacité et d'une taille de contexte suffisante pour le volume et la longueur des prompts en production. Un déploiement d'embeddings dimensionné pour un proof of concept devient le goulot d'étranglement de tout le trafic, en cache ou non, parce que le lookup se produit avant que le hit ou le miss ne soit connu.

Ce qu'un cache hit fait aux chiffres de la partie 5

Un hit n'est pas gratuit et n'est pas une completion. Il coûte un appel d'embeddings, un peu de temps Redis, et aucun token de completion, ce qui veut dire que le modèle de chargeback de la partie 5 a besoin de trois petits changements plutôt que d'une réécriture.

  • La dépense d'embeddings devient une ligne de coût plateforme. Elle est engagée sur chaque requête, misses compris, et elle n'est pas attribuable à une completion. Soit elle est facturée aux tenants au prorata du nombre de requêtes, soit elle est absorbée par la plateforme. Choisissez et écrivez-le ; ne la laissez pas hors de la réconciliation.
  • Le hit rate est une dimension de métrique légitime. Il est à faible cardinalité et il a sa place sur le dashboard à côté de la dépense, parce qu'un hit rate qui chute est le plus souvent un changement de template de prompt que personne n'a annoncé.
  • Les économies doivent être rapportées comme du coût évité, pas comme de la dépense. Un tenant dont la facture a baissé de 30 pour cent grâce au cache supposera que le chiffre bas est la nouvelle baseline. Montrer les tokens évités à côté des tokens facturés garde cette conversation honnête, et justifie l'instance Redis que la plateforme paie.

Quand ne pas mettre en cache du tout

Le semantic caching convient aux prompts à fort volume, à faible variance et non personnalisés. La recherche de connaissances retail de l'entreprise sur la documentation produit en est presque l'idéal. Les autres cas non, et la politique doit être portée par API plutôt qu'appliquée globalement, ce qui est exactement la raison pour laquelle elle supporte les portées product et API.

Passez-vous-en là où la réponse dépend de l'heure courante ou d'un état vivant, parce qu'une duration de 60 secondes est une fenêtre de justesse, pas seulement une préférence de fraîcheur. Passez-vous-en pour les flux de tool-calling, où la sortie du modèle est une instruction d'agir plutôt qu'un texte à lire, et où une instruction périmée est une action fausse. Passez-vous-en pour tout ce qui est personnalisé au-delà de ce que vary-by peut partitionner. Et traitez les longs dialogues multi-tours comme hors périmètre en posant max-message-count plutôt qu'en espérant que le score de similarité s'en aperçoive.

Modes de défaillance à surveiller

  • Le threshold réglé à l'envers. Plus haut veut dire plus lâche. Une équipe qui optimise le hit rate le monte à 0.4, le ratio s'améliore, et les mismatches restent invisibles jusqu'à ce qu'un utilisateur signale une réponse sans aucun rapport avec sa question.
  • Un vary-by manquant. Pas d'erreur, pas d'avertissement, et un cache partagé entre tous les tenants de l'API.
  • RediSearch découvert tard. Le module ne peut pas être ajouté à un cache existant, donc la réponse est un nouveau cache et une migration, au milieu du travail qui supposait un changement de configuration.
  • Redis à terre, backend inondé. Sans le rate-limit juste après le lookup, une panne de cache arrive au modèle sous forme de pic de trafic.
  • Un déploiement d'embeddings sous-dimensionné. Il est appelé sur chaque requête, donc il throttle les hits comme les misses, et le symptôme ressemble à un cache qui ralentit tout.
  • Un changement de template de prompt qui vide le cache en silence. Cela vaut une alerte sur le hit rate plutôt qu'une découverte sur la facture.

Ce dont hérite la partie 7

Le cache répond aux répétitions. Il ne fait rien pour le cas bien plus fréquent où le modèle a besoin d'une information sur laquelle il n'a jamais été entraîné, ce qui est le problème de retrieval, et la recherche de connaissances retail de l'entreprise l'attend déjà. Ensuite : Azure AI Search face à une base vectorielle dédiée, comparés sur les termes qui décident vraiment.

À lire ensuite

Pour le volet infrastructure et plateforme de l'exploitation de tout cela à grande échelle, les notes de terrain sont sur ercan.cloud, et le hub se trouve sur ercanermis.com.

Références