A própria documentação de policy da Microsoft diz que um score threshold acima de 0.2 pode levar a cache mismatch, o que é uma forma educada de dizer que o gateway vai responder uma pergunta que o chamador não fez. Todos os outros mecanismos desta série falharam fazendo barulho: um 429, um 503, um batch job expirado. Um semantic cache falha devolvendo uma resposta fluente, bem formada e confiante que pertence ao prompt de outra pessoa. A Parte 5 tornou o gasto atribuível. Esta parte o torna menor, e trata o mecanismo que faz isso como uma superfície de correção, não como uma alavanca de custo.

O que o cache realmente precisa antes de funcionar

Semantic caching no API Management tem uma lista de pré-requisitos que decide uma escolha de infraestrutura impossível de revisitar depois, então ela vem antes da policy, não depois.

  • Um segundo model deployment, para embeddings. O deployment de chat completion atende as chamadas dos consumidores; um deployment separado de embeddings é o que o cache usa para transformar um prompt em um vetor. Isso é uma segunda linha de capacidade, uma segunda quota e uma segunda coisa para monitorar.
  • Autenticação por managed identity do API Management para as APIs de modelo, que a policy exige: embeddings-backend-auth deve ser definido como system-assigned. Não existe opção baseada em key aqui, o que é uma boa restrição.
  • Azure Managed Redis com o módulo RediSearch habilitado, configurado como external cache na instância de API Management.

Esse último item carrega a armadilha. O RediSearch só pode ser habilitado quando o cache é criado, e não pode ser adicionado a um cache existente. Uma plataforma que já roda uma instância Redis para sessões ou estado de rate-limit não pode simplesmente ligar o semantic caching: ela precisa de um cache novo, provisionado com o módulo, e o Terraform da Parte 2 é o lugar onde essa decisão fica registrada. Descobrir isso no meio de um sprint que orçou uma tarde para "habilitar caching" é o desfecho comum.

O par de policies

O lookup vai no inbound, o store vai no outbound, e cada um do par só pode aparecer uma vez por seção de policy. Ambos estão disponíveis nos escopos global, de product, de API e de operation, e nos gateways classic, v2, consumption e 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>

Quatro desses atributos são decisões, não boilerplate.

score-threshold define quão próximo um prompt de entrada precisa estar de um prompt em cache, em uma escala de 0.0 a 1.0, onde valores mais baixos exigem similaridade semântica mais alta. Isso inverte o que a maioria assume na primeira leitura, e errar a direção transforma um cache conservador em um promíscuo. A orientação da Microsoft é começar baixo, por volta de 0.05, e ajustar em direção à razão de hit para miss que você quer, com o aviso explícito de que acima de 0.2 os mismatches se tornam prováveis e de que casos de uso sensíveis devem ficar mais baixo.

ignore-system-messages="true" remove as system messages antes de a similaridade ser avaliada, e é recomendado. Importa mais do que parece: duas aplicações com perguntas de usuário idênticas e system prompts diferentes pareceriam diferentes para o cache, e uma única aplicação cujo system prompt fosse editado invalidaria o cache inteiro sem nenhuma razão semântica.

max-message-count pula o caching quando um diálogo passa do número especificado de mensagens restantes. Conversas longas são exatamente onde um match semântico é menos confiável, porque o significado da última mensagem depende de turnos que o embedding nunca viu.

vary-by é a fronteira de isolamento, e a próxima seção explica por que ele é um controle de segurança.

vary-by não é um capricho de particionamento

Sem vary-by, um único cache é compartilhado entre todos os chamadores da API. Um prompt do assistente de atendimento contendo os dados de um cliente pode ser respondido a partir de uma completion em cache, e um prompt semanticamente similar vindo de outro tenant pode ser respondido a partir dessa mesma entrada. A documentação diz sem rodeios: controle o acesso entre usuários às entradas do cache especificando vary-by com identificadores específicos de usuário ou de grupo de usuários.

Para esta plataforma o mínimo é o subscription ID, que alinha a fronteira do cache exatamente com a fronteira de tenancy que a Parte 5 construiu. Onde uma aplicação atende usuários finais cujos dados não podem se misturar, o valor correto é um claim do token validado, não algo que o chamador possa definir em um header, e é por isso que o validate-jwt da Parte 5 escreve seu token em output-token-variable-name. Um vary-by que lê um header não validado é um vazamento de dados entre tenants com um arquivo de configuração como causa raiz.

O custo do particionamento estreito é um hit rate mais baixo, e essa troca é real. Ela também é o default correto: um cache que nunca vaza e economiza 20 por cento vence um que economiza 45 por cento e um dia devolve a resposta do cliente errado.

A dependência que você acabou de adicionar

O cache fica no caminho de requisição de toda chamada, o que significa que o Redis agora está no caminho crítico do gateway. A recomendação da Microsoft é específica e vale seguir à risca: coloque uma policy rate-limit ou rate-limit-by-key imediatamente depois do cache lookup, para impedir que o backend seja sobrecarregado se o cache não estiver disponível.

Pense nessa falha até o fim. Em um dia normal, uma parcela significativa do tráfego nunca chega a um modelo. Se o Redis ficar indisponível, cada uma dessas requisições vira uma chamada real de completion, instantaneamente, contra uma quota dimensionada para o estado estável com cache. A queda do cache não degrada a plataforma, ela multiplica a carga, e o rate limit é o que converte isso de uma indisponibilidade em throttling. É a mesma lição de qualquer cache na frente de um banco de dados, e é mais fácil esquecer aqui porque o cache foi introduzido como otimização de custo, não como dependência de capacidade.

O deployment de embeddings merece o mesmo raciocínio. Todo lookup gera o embedding do prompt de entrada, então o modelo de embeddings precisa de capacidade suficiente e de tamanho de contexto suficiente para o volume e os comprimentos de prompt em produção. Um deployment de embeddings dimensionado para uma prova de conceito vira o gargalo de todo o tráfego, com cache ou sem, porque o lookup acontece antes de se saber se é hit ou miss.

O que um cache hit faz com os números da Parte 5

Um hit não é gratuito e não é uma completion. Ele custa uma chamada de embeddings, algum tempo de Redis e nenhum token de completion, o que significa que o modelo de chargeback da Parte 5 precisa de três pequenas mudanças, não de uma reescrita.

  • O gasto com embeddings vira uma linha de custo da plataforma. Ele é incorrido em toda requisição, incluindo os misses, e não é atribuível a uma completion. Ou é cobrado dos tenants pro rata pela contagem de requisições, ou é absorvido pela plataforma. Escolha uma opção e documente; não a deixe fora da reconciliação.
  • Hit rate é uma dimensão de métrica legítima. É de baixa cardinalidade e pertence ao dashboard ao lado do gasto, porque um hit rate que cai costuma ser uma mudança de prompt template que ninguém anunciou.
  • A economia deve ser reportada como custo evitado, não como gasto. Um tenant cuja fatura caiu 30 por cento por causa do caching vai assumir que o número menor é a nova baseline. Mostrar tokens evitados ao lado de tokens faturados mantém essa conversa honesta, e sustenta o argumento pela instância Redis que a plataforma está pagando.

Quando não usar cache nenhum

Semantic caching serve a prompts de alto volume, baixa variância e não personalizados. A busca de conhecimento de varejo da empresa sobre documentação de produto está perto do ideal. Os outros casos não estão, e a policy deve ter escopo por API em vez de ser aplicada globalmente, que é exatamente o motivo de ela suportar escopo de product e de API.

Pule o cache onde a resposta depende da hora atual ou de estado vivo, porque um duration de 60 segundos é uma janela de correção, não só uma preferência de frescor. Pule em fluxos de tool-calling, onde a saída do modelo é uma instrução para agir, não um texto para ler, e uma instrução obsoleta é uma ação errada. Pule em qualquer coisa personalizada além do que o vary-by consegue particionar. E trate diálogos longos de múltiplos turnos como fora de escopo definindo max-message-count, em vez de torcer para o score de similaridade perceber.

Modos de falha para observar

  • O threshold definido ao contrário. Mais alto é mais frouxo. Um time otimizando o hit rate o sobe para 0.4, a razão melhora, e os mismatches ficam invisíveis até um usuário reportar uma resposta que não tem nada a ver com a pergunta dele.
  • Um vary-by ausente. Sem erro, sem aviso, e um cache compartilhado entre todos os tenants da API.
  • RediSearch descoberto tarde demais. O módulo não pode ser adicionado a um cache existente, então a resposta é um cache novo e uma migração, no meio de um trabalho que assumia uma mudança de configuração.
  • Redis fora do ar, backend inundado. Sem o rate limit logo depois do lookup, uma queda do cache chega ao modelo como um pico de tráfego.
  • Um deployment de embeddings subdimensionado. Ele é chamado em toda requisição, então estrangula hits e misses igualmente, e o sintoma parece o cache deixando tudo mais lento.
  • Uma mudança de prompt template que esvazia o cache em silêncio. Vale alertar sobre o hit rate em vez de descobrir na fatura.

O que a Parte 7 herda

O cache responde repetições. Ele não faz nada pelo caso muito mais comum em que o modelo precisa de informação com a qual nunca foi treinado, que é o problema de retrieval, e a busca de conhecimento de varejo da empresa já está esperando por ele. A seguir: Azure AI Search contra um vector database dedicado, comparados nos termos que realmente decidem a escolha.

Leia isso a seguir

Para o lado de infraestrutura e plataforma de rodar isso em escala, as notas de campo estão em ercan.cloud, e o hub fica em ercanermis.com.

Referências