ktg-plugin-marketplace/plugins/ms-ai-architect/skills/ms-ai-engineering/references/api-management/caching-strategies-apim-ai.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

15 KiB

Caching Strategies for AI Responses in APIM

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


Introduksjon

Caching er en av de mest effektive strategiene for a redusere kostnader og forbedre ytelse i AI-applikasjoner. Azure API Management tilbyr bade tradisjonell HTTP-caching og semantisk caching spesielt designet for LLM-API-er. Semantisk caching bruker embedding-vektorer for a identifisere prompts som er semantisk like -- ikke bare identiske -- og returnere cachede svar uten a kalle backend-modellen.

For norsk offentlig sektor kan caching-strategier gi vesentlige besparelser. En typisk offentlig virksomhet som bruker Azure OpenAI for chatbot-tjenester, intern dokumentanalyse eller innbyggerveiledning vil ofte motta mange lignende sporsmol. Semantisk caching kan redusere token-forbruket med 20-40% for slike workloads, med tilsvarende kostnadsbesparelse og forbedret responstid.

APIM stotter to hovedtyper caching: intern (innebygd) og ekstern (Redis-basert). For semantisk caching av AI-svar er ekstern cache via Azure Managed Redis med RediSearch-modulen pakrevd. Denne referansen dekker bade tradisjonell og semantisk caching, med fokus pa praktisk implementering for AI-workloads.


Prompt-baserte caching-nokler

Tradisjonell caching med eksakte matcher

For identiske prompts kan standard cache-lookup / cache-store policies brukes:

<policies>
    <inbound>
        <base />
        <!-- Cache lookup based on exact request body hash -->
        <cache-lookup vary-by-developer="false" vary-by-developer-groups="false">
            <vary-by-header>x-tenant-id</vary-by-header>
            <vary-by-query-parameter>model</vary-by-query-parameter>
        </cache-lookup>
    </inbound>
    <outbound>
        <base />
        <!-- Store response in cache for 5 minutes -->
        <cache-store duration="300" />
    </outbound>
</policies>

Custom cache-nokler for AI-foresporsler

Bygg tilpassede cache-nokler basert pa prompt-innhold:

<policies>
    <inbound>
        <base />
        <!-- Generate cache key from normalized prompt content -->
        <set-variable name="cacheKey" value="@{
            var body = context.Request.Body.As<JObject>(preserveContent: true);
            var messages = (JArray)body?["messages"];
            if (messages == null) return "";

            // Build key from role+content pairs, normalized
            var keyParts = new System.Collections.Generic.List<string>();
            foreach (var msg in messages)
            {
                var role = msg["role"]?.ToString() ?? "";
                var content = msg["content"]?.ToString()?.Trim().ToLower() ?? "";
                keyParts.Add($"{role}:{content}");
            }

            var model = body["model"]?.ToString() ?? "default";
            var combined = model + "|" + string.Join("|", keyParts);

            // Generate SHA256 hash
            using (var sha = System.Security.Cryptography.SHA256.Create())
            {
                var bytes = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(combined));
                return BitConverter.ToString(bytes).Replace("-", "").ToLower();
            }
        }" />

        <!-- Lookup in cache -->
        <cache-lookup-value key="@((string)context.Variables["cacheKey"])"
            variable-name="cachedResponse" />

        <choose>
            <when condition="@(context.Variables.ContainsKey("cachedResponse"))">
                <return-response>
                    <set-status code="200" reason="OK" />
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-header name="x-cache-hit" exists-action="override">
                        <value>true</value>
                    </set-header>
                    <set-body>@((string)context.Variables["cachedResponse"])</set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <outbound>
        <base />
        <!-- Store successful responses in cache -->
        <choose>
            <when condition="@(context.Response.StatusCode == 200)">
                <cache-store-value key="@((string)context.Variables["cacheKey"])"
                    value="@(context.Response.Body.As<string>(preserveContent: true))"
                    duration="600" />
                <set-header name="x-cache-hit" exists-action="override">
                    <value>false</value>
                </set-header>
            </when>
        </choose>
    </outbound>
</policies>

Semantisk deduplisering

Oversikt over semantisk caching i APIM

Semantisk caching bruker embeddings for a matche prompts basert pa meningsbetydning, ikke bare eksakt tekst. To prompts som "Hva er Microsoft Azure?" og "Kan du forklare hva Azure er?" vil ga i cache-treff selv om ordlyden er ulik.

Arkitektur

Klient --> APIM --> [Semantic Cache Lookup] --> Azure Managed Redis (RediSearch)
                          |                          |
                          | (cache miss)             | (cache hit)
                          v                          v
                   [Embeddings API]          [Returner cached svar]
                          |
                          v
                   [AI Backend (Chat)]
                          |
                          v
                   [Semantic Cache Store] --> Azure Managed Redis

Forutsetninger

Komponent Krav
Azure Managed Redis RediSearch-modul aktivert (velges ved opprettelse)
Embeddings deployment text-embedding-ada-002 eller nyere modell
APIM Alle tiers stotter semantisk caching med ekstern cache
Autentisering Managed Identity til bade OpenAI og Redis

Konfigurering av semantisk caching

1. Opprett embeddings-backend

<!-- Backend for embeddings API -->
<set-backend-service backend-id="embeddings-backend" />

I Azure Portal:

  • Type: Custom URL
  • Runtime URL: https://{aoai-name}.openai.azure.com/openai/deployments/{embedding-deployment}/embeddings
  • Managed Identity: System-assigned, Resource ID: https://cognitiveservices.azure.com/

2. Konfigurer semantic cache lookup (inbound)

For Azure OpenAI API-er:

<policies>
    <inbound>
        <base />
        <!-- 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>

        <!-- Rate limit as fallback if cache is unavailable -->
        <rate-limit calls="20" renewal-period="60" />
    </inbound>
</policies>

For andre LLM-API-er (ikke Azure OpenAI):

<policies>
    <inbound>
        <base />
        <llm-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>
        </llm-semantic-cache-lookup>
    </inbound>
</policies>

3. Konfigurer semantic cache store (outbound)

<policies>
    <outbound>
        <base />
        <!-- Store response for 60 seconds -->
        <azure-openai-semantic-cache-store duration="60" />
    </outbound>
</policies>

TTL-konfigurasjon

Strategier for Time-to-Live

Riktig TTL-konfigurasjon balanserer mellom kostnadsbesparelse og datakvalitet:

Bruksscenario Anbefalt TTL Begrunnelse
FAQ/statisk veiledning 3600s (1 time) Innholdet endres sjelden
Generell chatbot 300s (5 min) Balanse mellom friskhet og kostnad
Dokumentanalyse 600s (10 min) Dokumenter endres sjelden innen sesjon
Sanntidsdata-sporring 30-60s Data kan endres raskt
Kodegenerering 120s (2 min) Brukere itererer raskt
Intern kunnskapssok 1800s (30 min) Intern kunnskap er relativt stabil

Dynamisk TTL basert pa kontekst

<policies>
    <outbound>
        <base />
        <!-- Dynamic TTL based on request type -->
        <set-variable name="cacheDuration" value="@{
            var body = context.Request.Body.As<JObject>(preserveContent: true);
            var messages = (JArray)body?["messages"];
            var lastMessage = messages?.Last?["content"]?.ToString() ?? "";

            // Longer TTL for FAQ-like questions
            if (lastMessage.Contains("hva er") || lastMessage.Contains("forklar"))
                return 3600;

            // Shorter TTL for data queries
            if (lastMessage.Contains("status") || lastMessage.Contains("siste"))
                return 60;

            // Default TTL
            return 300;
        }" />

        <azure-openai-semantic-cache-store
            duration="@((int)context.Variables["cacheDuration"])" />
    </outbound>
</policies>

Cache-invalidering

Manuell invalidering med cache-remove-value

<!-- Remove specific cached value -->
<cache-remove-value key="specific-cache-key" />

Automatisk invalidering ved modellbytte

<policies>
    <inbound>
        <base />
        <!-- Include model version in cache key to auto-invalidate on model change -->
        <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 model deployment to invalidate cache on model update -->
            <vary-by>@(context.Subscription.Id)</vary-by>
            <vary-by>@(context.Request.Headers.GetValueOrDefault("x-model-version", "default"))</vary-by>
        </azure-openai-semantic-cache-lookup>
    </inbound>
</policies>

Cache-invalidering via API-kall

Opprett en dedikert operasjon for administratorer:

<!-- Cache purge operation -->
<policies>
    <inbound>
        <base />
        <!-- Verify admin access -->
        <validate-jwt header-name="Authorization"
            failed-validation-httpcode="401"
            failed-validation-error-message="Unauthorized">
            <required-claims>
                <claim name="roles" match="any">
                    <value>CacheAdmin</value>
                </claim>
            </required-claims>
        </validate-jwt>

        <!-- Purge cache - requires external cache API call -->
        <send-request mode="new" response-variable-name="purgeResult" timeout="10">
            <set-url>@($"https://{cacheHost}:10000/FLUSHDB")</set-url>
            <set-method>POST</set-method>
        </send-request>

        <return-response>
            <set-status code="200" reason="Cache Purged" />
            <set-body>{"status":"cache_purged","timestamp":"@(DateTime.UtcNow.ToString("o"))"}</set-body>
        </return-response>
    </inbound>
</policies>

Kostnadsbesparelsesanalyse

Beregningsmodell

Parameter Verdi
Gjennomsnittlig tokens per request 2 000 (prompt) + 500 (completion)
GPT-4o pris per 1M input tokens $2.50
GPT-4o pris per 1M output tokens $10.00
Antall requests per dag 10 000
Gjennomsnittlig cache hit rate 30%

Kostnadsberegning

Scenario Daglig kostnad (NOK) Manedlig kostnad (NOK)
Uten caching ~750 ~22 500
Med 30% cache hit ~525 ~15 750
Med 50% cache hit ~375 ~11 250
Med 70% cache hit ~225 ~6 750

Tilleggskostnader for caching-infrastruktur

Komponent Manedlig kostnad (NOK)
Azure Managed Redis (Balanced B1) ~2 500
Embeddings API-kall (for semantisk caching) ~150
Total caching-overhead ~2 650

Netto besparelse ved 30% hit rate

  • Besparelse: 22 500 - 15 750 = 6 750 NOK/mnd
  • Caching-kostnad: 2 650 NOK/mnd
  • Netto besparelse: ~4 100 NOK/mnd (18% av total)

Score-threshold tuning

score-threshold i semantisk caching pavirker hit rate og kvalitet:

Threshold Hit Rate Kvalitetsrisiko
0.05 Hoy (50-70%) Hoy -- kan returnere irrelevante svar
0.10 Middels-hoy (30-50%) Lav-middels
0.15 (anbefalt) Middels (20-35%) Lav
0.25 Lav (10-15%) Svart lav
0.50 Svart lav (<5%) Neglisjerbar

Caching-tjenester: Intern vs. Ekstern

Egenskap Intern cache Ekstern (Redis)
Automatisk provisjonering Ja Nei
Tilleggskostnad Nei Ja
Semantisk caching Nei Ja
Tilgjengelig i alle tiers Nei (ikke Consumption) Ja
Persistent lagring Ja (v2), Nei (classic) Ja
Delt mellom instanser Nei Ja
Data preloading Nei Ja

Referanser

For Cosmo

  • Bruk denne referansen nar kunden onsker a redusere AI-kostnader gjennom caching, eller nar de trenger a forbedre responstider for brukere som stiller lignende sporsmol.
  • Start med score-threshold="0.15" for semantisk caching -- dette gir god balanse. Juster ned til 0.10 for hoyere hit rate i FAQ-scenarier, eller opp til 0.25 for mer presise matcher i kritiske applikasjoner.
  • Husk at semantisk caching krever Azure Managed Redis med RediSearch-modulen -- denne modulen ma velges ved opprettelse av Redis-instansen og kan ikke legges til i ettertid.
  • For norsk offentlig sektor med hoy grad av repetitive sporsmol (innbyggertjenester, veiledning), er semantisk caching en lavthengende frukt med typisk 20-40% kostnadsreduksjon.
  • Inkluder alltid <vary-by>@(context.Subscription.Id)</vary-by> for a forhindre at en leietakers svar returneres til en annen -- dette er kritisk for personvern og dataskille.