ms-ai-architect/skills/ms-ai-engineering/references/api-management/semantic-caching-apim.md
Kjell Tore Guttormsen 544934dc57
refactor(examples): replace sector-specific example material with generic, fictitious examples
Reference files, test fixtures, the playground demo project and one design
document now use generic, fictitious examples (buildings, energy, water,
grants, municipal services). The playground demo (17 fixtures plus the
embedded demo state) tells one consistent story: a municipal customer
chatbot that pre-screens housing-benefit applications, classified under
Annex III point 5(a). The embedded demo copies were edited in place rather
than regenerated, because they already carry newer AI Act dates than the
fixture files.

Legal text is unchanged. Test semantics are unchanged. Four dark-theme
onboarding screenshots with outdated placeholder text are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 14:03:53 +02:00

20 KiB
Raw Blame History

Semantic Caching in APIM

Last updated: 2026-02 Status: GA Category: API Management & AI Gateway Type: reference


Innhold

Introduksjon

Semantic caching i Azure API Management er en teknikk som reduserer kostnader og latens for LLM-baserte applikasjoner ved å gjenbruke tidligere genererte completions. I motsetning til tradisjonell nøkkelbasert caching, bruker semantic caching embeddings og vektorlikhet til å identifisere semantisk like prompts -- selv når ordlyden er forskjellig. "Hva er hovedstaden i Norge?" og "Hvilken by er Norges hovedstad?" gir samme cachede svar.

For norsk offentlig sektor, der mange brukere stiller lignende spørsmål til interne AI-assistenter og chatbots, kan semantic caching gi betydelige kostnadsbesparelser. Typiske kundeservicescenarier med repeterende spørsmål om åpningstider, tjenester og prosedyrer oppnår cache hit rates på 30-60%, noe som tilsvarer tilsvarende reduksjon i token-forbruk og kostnader.

APIM implementerer semantic caching gjennom dedikerte policies som samarbeider med Azure Managed Redis (med RediSearch-modulen) og en Azure OpenAI Embeddings API-deployment. Hele flyten -- fra prompt-inngang til cache-oppslag og lagring -- håndteres av APIM-policies uten egenutviklet kode.


Arkitektur

Dataflyt

1. Bruker sender prompt til APIM
2. APIM → Embeddings API → Vektor [0.23, -0.45, 0.67, ...]
3. Vektor → Azure Managed Redis (RediSearch) → Similarity search
4. IF similarity score < threshold (lavere = mer lik):
     RETURN cached completion (cache HIT)
   ELSE:
     Forward til Azure OpenAI → Generer completion
     Store completion + embedding i Redis (cache STORE)
     RETURN completion til bruker

Komponentoversikt

                    ┌─────────────┐
                    │   Client    │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │    APIM     │
                    │ (AI Gateway)│
                    └──┬───┬──┬──┘
                       │   │  │
              ┌────────┘   │  └────────┐
              ▼            ▼           ▼
     ┌────────────┐ ┌──────────┐ ┌──────────┐
     │ Embeddings │ │  Redis   │ │ Azure    │
     │ API        │ │(RediSearch│ │ OpenAI   │
     │ (vektor)   │ │  cache)  │ │(LLM)     │
     └────────────┘ └──────────┘ └──────────┘
Komponent Rolle Azure-tjeneste
APIM Orkestrerer cache-logikk via policies Azure API Management (Standard v2+)
Embeddings API Konverterer prompts til vektorer Azure OpenAI text-embedding-3-large
Vector Cache Lagrer embeddings + completions, utfører similarity search Azure Managed Redis med RediSearch
LLM Backend Genererer nye completions ved cache miss Azure OpenAI GPT-4o / GPT-4o-mini

Forutsetninger

1. Azure Managed Redis med RediSearch

resource redis 'Microsoft.Cache/redisEnterprise@2024-09-01-preview' = {
  name: 'redis-semantic-cache-${environment}'
  location: location
  sku: {
    name: 'Enterprise_E10'
    capacity: 2
  }
  properties: {}
}

resource database 'Microsoft.Cache/redisEnterprise/databases@2024-09-01-preview' = {
  parent: redis
  name: 'default'
  properties: {
    clientProtocol: 'Encrypted'
    evictionPolicy: 'VolatileLRU'
    modules: [
      {
        name: 'RediSearch'
      }
    ]
  }
}

Viktig: RediSearch-modulen kan KUN aktiveres ved opprettelse av Redis-instansen. Du kan ikke legge den til i etterkant. Planlegg for dette fra starten.

2. Embeddings API Deployment

resource embeddingsDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = {
  parent: openaiAccount
  name: 'text-embedding-3-large'
  sku: {
    name: 'Standard'
    capacity: 120  // 120K TPM for embeddings
  }
  properties: {
    model: {
      format: 'OpenAI'
      name: 'text-embedding-3-large'
      version: '1'
    }
  }
}

Valg av embeddings-modell:

Modell Dimensjoner Pris (per 1M tokens) Anbefaling
text-embedding-3-large 3072 ~$0.13 Høyest kvalitet, anbefalt
text-embedding-3-small 1536 ~$0.02 Kostnadseffektiv, god nok for de fleste
text-embedding-ada-002 1536 ~$0.10 Legacy, ikke anbefalt for nye prosjekter

3. APIM External Cache-konfigurasjon

Koble Redis som ekstern cache i APIM:

resource externalCache 'Microsoft.ApiManagement/service/caches@2023-09-01-preview' = {
  parent: apim
  name: 'redis-semantic'
  properties: {
    connectionString: '${redis.properties.hostName}:10000,password=${listKeys(redis.id, redis.apiVersion).keys[0].value},ssl=True,abortConnect=False'
    useFromLocation: 'default'
    description: 'Azure Managed Redis for semantic caching'
    resourceId: redis.id
  }
}

4. Embeddings Backend i APIM

resource embeddingsBackend 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'embeddings-backend'
  properties: {
    url: 'https://aoai-norwayeast.openai.azure.com/openai/deployments/text-embedding-3-large/embeddings'
    protocol: 'http'
  }
}

Cache-lookup og Cache-store Policies

Azure OpenAI-spesifikke policies

For Azure OpenAI APIs, bruk de spesialbestemte policies:

Inbound (cache lookup):

<azure-openai-semantic-cache-lookup
    score-threshold="0.15"
    embeddings-backend-id="embeddings-backend"
    embeddings-backend-auth="system-assigned"
    ignore-system-messages="true"
    max-message-count="10">
    <vary-by>@(context.Subscription.Id)</vary-by>
</azure-openai-semantic-cache-lookup>

Outbound (cache store):

<azure-openai-semantic-cache-store duration="3600" />

Generelle LLM-policies

For tredjeparts LLM-er eller OpenAI-kompatible endepunkter:

Inbound:

<llm-semantic-cache-lookup
    score-threshold="0.15"
    embeddings-backend-id="embeddings-backend"
    embeddings-backend-auth="system-assigned"
    max-message-count="10">
    <vary-by>@(context.Subscription.Id)</vary-by>
</llm-semantic-cache-lookup>

Outbound:

<llm-semantic-cache-store duration="3600" />

Policy-attributter

Attributt Type Beskrivelse Anbefalt verdi
score-threshold float Maks avstand for cache hit (lavere = strengere) 0.10-0.20
embeddings-backend-id string Backend-ID for Embeddings API embeddings-backend
embeddings-backend-auth string Autentiseringsmetode system-assigned
ignore-system-messages bool Ignorer system message i cache-nøkkel true (oftest)
max-message-count int Maks antall meldinger i konversasjonshistorikk å cache 10
duration int Cache TTL i sekunder 3600 (1 time)

vary-by-element

<vary-by> sikrer at cache er isolert per konsument/kontekst:

<!-- Isoler cache per subscription -->
<vary-by>@(context.Subscription.Id)</vary-by>

<!-- Isoler per subscription OG modell -->
<vary-by>@(context.Subscription.Id + "-" + context.Request.MatchedParameters["deployment-id"])</vary-by>

<!-- Isoler per etat -->
<vary-by>@(context.Request.Headers.GetValueOrDefault("x-etat-id", "shared"))</vary-by>

Embedding-Based Similarity

Hvordan score-threshold fungerer

APIM bruker cosine distance (ikke cosine similarity) for å sammenligne embeddings:

Cosine Distance = 1 - Cosine Similarity

Distance 0.0  = Identiske prompts (perfekt match)
Distance 0.15 = Svært like prompts
Distance 0.30 = Noe like prompts
Distance 1.0  = Helt ulike prompts

Threshold-valg:

score-threshold Matchstrenghet Cache hit rate Presisjon Anbefalt for
0.05 Ekstremt streng Lav (5-15%) Svært høy Faktabaserte spørsmål
0.10 Streng Moderat (15-30%) Høy Standard anbefaling
0.15 Balansert God (25-45%) God De fleste use cases
0.20 Liberal Høy (35-60%) Moderat FAQ/kundeservice
0.30 Aggressiv Svært høy (50-70%) Lavere Generelle spørsmål

Kalibreringsprosess:

  1. Start med score-threshold="0.15" (balansert)
  2. Kjør produksjonstrafikk i 1-2 uker
  3. Analyser cache hit rate og brukertilfredshet
  4. Juster ned (strengere) hvis brukere rapporterer irrelevante svar
  5. Juster opp (mer liberal) hvis cache hit rate er under 20%

Eksempler på semantisk matching

Prompt A Prompt B Typisk distance Match ved 0.15?
"Hva er hovedstaden i Norge?" "Hvilken by er Norges hovedstad?" ~0.05 Ja
"Forklar maskinlæring" "Hva er machine learning?" ~0.10 Ja
"Hvordan søker jeg om byggetillatelse?" "Prosessen for å få byggetillatelse" ~0.12 Ja
"Hva er fotosyntese?" "Hvordan fungerer en solcelle?" ~0.25 Nei
"Fortell om AI" "Hva er kvantemekanikk?" ~0.45 Nei

Cache Invalidation Strategies

TTL-basert invalidation (standard)

<!-- Cache entries utløper etter 1 time -->
<azure-openai-semantic-cache-store duration="3600" />

Anbefalte TTL-verdier:

Innholdstype TTL Begrunnelse
Statisk fakta (hovedsteder, lover) 86400 (24t) Endres sjelden
Generell kunnskap 3600 (1t) God balanse
Dynamisk innhold (priser, status) 300 (5min) Endres ofte
Real-time data 0 (ingen cache) Må alltid være oppdatert

Manuell cache-invalidation

APIM har ingen innebygd policy for selektiv cache-invalidation av semantic cache. Alternativa tilnærminger:

1. Redis CLI flush:

# Flush all cached entries (krever Redis-tilgang)
redis-cli -h redis-cache.norwayeast.redis.cache.windows.net -p 10000 --tls FLUSHDB

2. TTL-basert rotasjon: Bruk kort TTL og la entries utløpe naturlig.

3. vary-by med versjonsnøkkel:

<vary-by>@("v2-" + context.Subscription.Id)</vary-by>

Endre "v2" til "v3" i policy for å effektivt invalidere all cache (nye nøkler gir cache miss).


Cost Savings Analysis

Beregningsmodell

Kostnad UTEN caching:
  Totale requests × gjennomsnittlig tokens per request × pris per token

Kostnad MED caching:
  (Cache misses × tokens per request × pris per token)
  + (Alle requests × embedding tokens × embedding pris)
  + Redis-kostnad

Besparelse = Kostnad UTEN - Kostnad MED

Eksempelberegning for norsk offentlig sektor

Scenario: Intern AI-assistent for 500 ansatte, 10 000 requests/dag.

Parameter Verdi
Requests per dag 10 000
Gjennomsnittlig prompt tokens 200
Gjennomsnittlig completion tokens 500
GPT-4o pris (input) $2.50 / 1M tokens
GPT-4o pris (output) $10.00 / 1M tokens
Embedding pris $0.13 / 1M tokens
Cache hit rate 40%

Beregning:

Kostnadspost Uten caching Med caching (40% hit rate)
LLM input tokens 10K × 200 = 2M → $5.00/dag 6K × 200 = 1.2M → $3.00/dag
LLM output tokens 10K × 500 = 5M → $50.00/dag 6K × 500 = 3M → $30.00/dag
Embedding tokens $0/dag 10K × 200 = 2M → $0.26/dag
Redis (E10) $0/dag ~$6.00/dag ($182/mnd)
Total per dag $55.00 $39.26
Total per måned $1 650 $1 178
Besparelse - $472/mnd (29%)

ROI-beregning

Cache hit rate Månedlig besparelse (LLM) Redis-kostnad Netto besparelse ROI
20% $330 $182 $148 Positiv
40% $660 $182 $478 Sterk
60% $990 $182 $808 Svært sterk
80% $1 320 $182 $1 138 Eksepsjonell

Break-even punkt: Semantic caching er kostnadseffektivt ved cache hit rates over ~15% for typiske workloads.


Privacy Considerations

Datalagrings-hensyn

Hensyn Risiko Mitigering
PII i cache Persondata caches i Redis Bruk vary-by per bruker, kort TTL, eller ekskluder PII-requests
Cross-tenant data En brukers svar vises for annen bruker vary-by per subscription/bruker isolerer cache
Cache i feil region Data lagres utenfor tillatt geografi Deploy Redis i samme region som APIM og OpenAI
Langvarig lagring Sensitive svar lagret for lenge Sett passende TTL, minimum mulig
Logging av prompts Prompts logges via APIM diagnostics Konfigurer masking i diagnostic settings

Anbefalinger for offentlig sektor

  1. Isoler cache per etat/avdeling med vary-by element
  2. Sett TTL til maksimalt 1 time for generelle spørsmål, kortere for sensitive
  3. Ekskluder sensitive APIer fra semantic caching (fjern policies for spesifikke operasjoner)
  4. Deploy Redis i Norway East eller Sweden Central for datasuverenitet
  5. Aktiver TLS (ssl=True) for all Redis-kommunikasjon
  6. Bruk private endpoints for Redis og APIM
  7. Vurder DPIA (Data Protection Impact Assessment) for cache av brukerdata

Ekskludering av sensitive requests

<policies>
    <inbound>
        <!-- Skip cache for requests med PII-flag -->
        <choose>
            <when condition="@(context.Request.Headers.GetValueOrDefault("x-contains-pii", "false") == "true")">
                <!-- Ingen cache lookup, gå direkte til backend -->
            </when>
            <otherwise>
                <azure-openai-semantic-cache-lookup
                    score-threshold="0.15"
                    embeddings-backend-id="embeddings-backend"
                    embeddings-backend-auth="system-assigned">
                    <vary-by>@(context.Subscription.Id)</vary-by>
                </azure-openai-semantic-cache-lookup>
            </otherwise>
        </choose>
    </inbound>

    <outbound>
        <choose>
            <when condition="@(context.Request.Headers.GetValueOrDefault("x-contains-pii", "false") != "true")">
                <azure-openai-semantic-cache-store duration="3600" />
            </when>
        </choose>
    </outbound>
</policies>

Rate Limiting etter Cache Lookup

Beskyttelse mot cache-utilgjengelighet

Legg alltid til en rate limit ETTER cache lookup for å beskytte backend hvis Redis er nede:

<policies>
    <inbound>
        <!-- 1. Semantic cache lookup -->
        <azure-openai-semantic-cache-lookup
            score-threshold="0.15"
            embeddings-backend-id="embeddings-backend"
            embeddings-backend-auth="system-assigned">
            <vary-by>@(context.Subscription.Id)</vary-by>
        </azure-openai-semantic-cache-lookup>

        <!-- 2. Rate limit for cache misses (beskytter backend) -->
        <rate-limit-by-key
            calls="100"
            renewal-period="60"
            counter-key="@(context.Subscription.Id)" />

        <!-- 3. Token limit -->
        <llm-token-limit
            counter-key="@(context.Subscription.Id)"
            tokens-per-minute="50000"
            estimate-prompt-tokens="true" />
    </inbound>
</policies>

Verifisering og feilsøking

Bekrefte at caching fungerer

Bruk APIM Test Console med tracing aktivert:

  1. Send en request via Test Console med tracing
  2. Inspiser trace-output:
    • Cache HIT: azure-openai-semantic-cache-lookup viser "Cache lookup resulted in a hit"
    • Cache MISS: Viser "Cache lookup resulted in a miss" + backend-kall

KQL for cache-metrikk

// Cache hit rate over tid
ApiManagementGatewayLogs
| where OperationId contains "chat"
| extend cacheHit = ResponseHeaders contains "x-cache: HIT"
| summarize
    TotalRequests = count(),
    CacheHits = countif(cacheHit),
    CacheMisses = countif(not(cacheHit)),
    HitRate = round(100.0 * countif(cacheHit) / count(), 2)
    by bin(TimeGenerated, 1h)
| render timechart
// Kostnadsbesparelse estimat
ApiManagementGatewayLogs
| where OperationId contains "chat"
| extend cacheHit = ResponseHeaders contains "x-cache: HIT"
| extend estimatedTokensSaved = iff(cacheHit, 700, 0)  // avg tokens per request
| summarize
    TokensSaved = sum(estimatedTokensSaved),
    EstimatedCostSavedUSD = round(sum(estimatedTokensSaved) * 0.000010, 2)
    by bin(TimeGenerated, 1d)

Komplett policy for semantic caching

<policies>
    <inbound>
        <base />

        <!-- Autentisering -->
        <authentication-managed-identity
            resource="https://cognitiveservices.azure.com/" />

        <!-- Token rate limit -->
        <llm-token-limit
            counter-key="@(context.Subscription.Id)"
            tokens-per-minute="50000"
            estimate-prompt-tokens="true"
            remaining-tokens-variable-name="remainingTokens" />

        <!-- Semantic cache lookup -->
        <azure-openai-semantic-cache-lookup
            score-threshold="0.15"
            embeddings-backend-id="embeddings-backend"
            embeddings-backend-auth="system-assigned"
            ignore-system-messages="true"
            max-message-count="10">
            <vary-by>@(context.Subscription.Id)</vary-by>
        </azure-openai-semantic-cache-lookup>

        <!-- Fallback rate limit hvis cache er nede -->
        <rate-limit-by-key
            calls="100"
            renewal-period="60"
            counter-key="@(context.Subscription.Id)" />

        <!-- Backend pool -->
        <set-backend-service backend-id="openai-pool" />
    </inbound>

    <outbound>
        <base />

        <!-- Cache store -->
        <azure-openai-semantic-cache-store duration="3600" />

        <!-- Token metrikk -->
        <llm-emit-token-metric namespace="ai-gateway">
            <dimension name="Subscription"
                       value="@(context.Subscription.Id)" />
            <dimension name="CacheHit"
                       value="@(context.Response.Headers.GetValueOrDefault("x-cache", "MISS"))" />
        </llm-emit-token-metric>
    </outbound>

    <on-error>
        <base />
    </on-error>
</policies>

Tier-kompatibilitet

Policy Classic V2 Consumption Self-hosted Workspace
azure-openai-semantic-cache-lookup Ja Ja Ja Nei Nei
azure-openai-semantic-cache-store Ja Ja Ja Nei Nei
llm-semantic-cache-lookup Ja Ja Ja Nei Nei
llm-semantic-cache-store Ja Ja Ja Nei Nei

Merk: Semantic caching krever ekstern cache (Azure Managed Redis) og er IKKE tilgjengelig i self-hosted gateway eller workspace gateway.


For arkitekten

  • Semantic caching er den mest kostnadseffektive optimaliseringen for AI-workloads med repeterende spørsmål. Start med score-threshold="0.15" og juster basert på cache hit rate og brukerfeedback. For FAQ/kundeservice-scenarier, vurder 0.20 for høyere hit rate.
  • Krav: Azure Managed Redis med RediSearch-modul (MÅ aktiveres ved opprettelse, kan ikke legges til etterpå) + Azure OpenAI Embeddings deployment. Planlegg disse ressursene fra starten.
  • Bruk vary-by per subscription/bruker for å isolere cache og forhindre data-lekkasje mellom konsumenter. For offentlig sektor er dette en forutsetning for compliance.
  • Legg alltid til en rate-limit policy ETTER cache lookup som beskyttelse mot situasjoner der Redis er utilgjengelig -- uten dette vil alle requests gå direkte til backend uten throttling.
  • Kostnadsbesparelse ved 40% cache hit rate er typisk 25-35% for standard AI-assistenter. Break-even punkt er ca. 15% hit rate (under dette er Redis-kostnaden høyere enn besparelsen).