ktg-plugin-marketplace/plugins/ms-ai-architect/skills/ms-ai-engineering/references/api-management/semantic-caching-apim.md
Kjell Tore Guttormsen 6a7632146e feat(ms-ai-architect): add plugin to open marketplace (v1.5.0 baseline)
Initial addition of ms-ai-architect plugin to the open-source marketplace.
Private content excluded: orchestrator/ (Linear tooling), docs/utredning/
(client investigation), generated test reports and PDF export script.
skill-gen tooling moved from orchestrator/ to scripts/skill-gen/.

Security scan: WARNING (risk 20/100) — no secrets, no injection found.
False positive fixed: added gitleaks:allow to Python variable reference
in output-validation-grounding-verification.md line 109.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 17:17:17 +02:00

20 KiB
Raw Blame History

Semantic Caching in APIM

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


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 veibygging?" "Hvordan bygger man en bro?" ~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 Cosmo

  • 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).