ms-ai-architect/skills/ms-ai-security/references/performance-scalability/prompt-caching-performance.md
Kjell Tore Guttormsen ddce43d8b2 feat(ms-ai-architect): Spor 1 — Port-1-substrat migrert på 4 ikke-advisor-skills (243 Source + 327 Type + 325 TOC + stale-verified poison fjernet) [skip-docs]
Steg 9 (R4): unified migrate-corpus.mjs --write over engineering/governance/
infrastructure/security. 327 filer mutert, verified=null, prosa byte-identisk
(fra første ## seksjon), advisor urørt (0 endringer).

To applier-fixes oppdaget under kjøring (TDD, RED→GREEN):
- insertHeaderFields: anker faller nå tilbake når en meta-linje selv passerer
  500B (2 filer pakket et avsnitt i **Status:** → Type/Source landet utenfor
  scan-vinduet, applierens post-write-assertion fanget + restaurerte).
- normalizeStaleVerified: fjerner nå ALLE stale non-date **Verified:** i
  500B-vinduet, inkl. stray body-dup rett under --- (9 mlops-genaiops-filer var
  ellers falskt "verified"/fresh, droppet fra worklist). Operatør-godkjent
  utvidelse av carve-out; kun stray metadata-linjer, aldri prosa.

test-transform-criterion: precondition oppdatert til post-migrasjons-sannhet
(fila bærer nå Source). Suite 728/728 grønn.
2026-07-04 10:19:11 +02:00

15 KiB

Prompt Caching for Performance

Last updated: 2026-06-19 Status: GA Category: Performance & Scalability Type: reference Source: https://learn.microsoft.com/azure/foundry/openai/concepts/provisioned-throughput


Innhold

Introduksjon

Azure OpenAI prompt caching er en innebygd mekanisme som reduserer latens og kostnad for forespørsler med identiske prefixer. Når de første 1024+ tokens i en prompt er identiske med en tidligere forespørsel, gjenbruker tjenesten de allerede beregnede token-representasjonene i stedet for å prosessere dem på nytt. Dette gir raskere time-to-first-token (TTFT) og lavere kostnad — cached tokens faktureres med rabatt for Standard deployments og opptil 100% rabatt for Provisioned (PTU) deployments.

Prompt caching er automatisk aktivert for alle støttede modeller (GPT-4o og nyere) uten ekstra konfigurasjon. Cachen er basert på en hash av de første ~256 tokens og krever minimum 1024 identiske tokens for å trigge. Etter den initiale 1024-token terskelen caches ytterligere identiske tokens i blokker på 128. Cacher tømmes typisk innen 5-10 minutter uten aktivitet og alltid innen 24 timer.

For norsk offentlig sektor der AI-applikasjoner ofte har lange, statiske system-prompts (inkludert regelverk, instruksjoner og eksempler), er prompt caching en svært effektiv optimaliseringsstrategi som kan gi 30-50% kostnadsreduksjon uten noen endring i output-kvalitet.

Kjernekomponenter

Komponent Formål Teknologi
Prompt Cache Automatisk caching av identiske prefixer Azure OpenAI
prompt_cache_key Valgfri parameter for å påvirke cache routing Azure OpenAI API
cached_tokens API-respons felt som viser cache hits prompt_tokens_details
Semantic Cache Ekstern cache for semantisk like forespørsler Azure Cosmos DB
Multi-layer Caching Kombinert caching-strategi Arkitektur-mønster

Cache Eligibility Requirements

Tekniske krav for prompt caching

# Krav for at prompt caching skal fungere:
CACHE_REQUIREMENTS = {
    "minimum_prefix_length": 1024,  # Tokens
    "hash_prefix_length": 256,      # Tokens brukt for routing-hash
    "subsequent_block_size": 128,    # Etter 1024, cache i 128-blokker
    "cache_ttl_inactive": "5-10 min",
    "cache_ttl_max": "24 timer",
    "cross_subscription": False,     # Cache deles IKKE mellom abonnement
    "supported_models": [
        "gpt-4o-*",
        "gpt-4o-mini-*",
        "gpt-4.1-*",
        "gpt-4.1-mini-*",
        "gpt-4.1-nano-*",
        "o1-*",
        "o3-*",
        "o3-mini-*"
    ],
    "supported_operations": [
        "chat-completions",
        "completions",
        "responses",
        "real-time"
    ]
}

# Sjekk om en prompt er cache-eligible
def is_cache_eligible(messages: list[dict], model: str = "gpt-4o") -> dict:
    """Check if a prompt is eligible for caching."""
    import tiktoken

    enc = tiktoken.encoding_for_model(model)

    # Beregn total tokens for alle meldinger
    total_tokens = 0
    for msg in messages:
        total_tokens += len(enc.encode(msg["content"]))
        total_tokens += 4  # Role tokens overhead

    return {
        "total_tokens": total_tokens,
        "eligible": total_tokens >= 1024,
        "cacheable_tokens": max(0, (total_tokens // 128) * 128)
            if total_tokens >= 1024 else 0,
        "recommendation": (
            "Eligible for caching" if total_tokens >= 1024
            else f"Need {1024 - total_tokens} more tokens in prefix"
        )
    }

Prefix Strategy Design

Optimaliser prompt-struktur for caching

def design_cacheable_prompt(
    system_instructions: str,
    few_shot_examples: list[dict],
    reference_documents: str,
    user_query: str
) -> tuple[list[dict], dict]:
    """
    Design prompt with optimal structure for caching.

    Prinsipp: Statisk innhold FØRST, dynamisk innhold SIST.
    Alt fra starten til det dynamiske innholdet caches.
    """
    messages = []

    # --- CACHEABLE PREFIX START ---

    # 1. System prompt (statisk per applikasjon)
    messages.append({
        "role": "system",
        "content": system_instructions
    })

    # 2. Few-shot eksempler (statisk per oppgave)
    for example in few_shot_examples:
        messages.append({"role": "user", "content": example["input"]})
        messages.append({"role": "assistant", "content": example["output"]})

    # 3. Referansedokumenter (statisk per sesjon)
    if reference_documents:
        messages.append({
            "role": "user",
            "content": f"Referansemateriale:\n\n{reference_documents}"
        })
        messages.append({
            "role": "assistant",
            "content": "Forstått. Jeg vil bruke referansematerialet."
        })

    # --- CACHEABLE PREFIX SLUTT ---

    # 4. Dynamisk brukerforespørsel (varierer — IKKE cached)
    messages.append({
        "role": "user",
        "content": user_query
    })

    # Beregn cache-statistikk
    import tiktoken
    enc = tiktoken.encoding_for_model("gpt-4o")

    static_tokens = sum(
        len(enc.encode(m["content"])) + 4
        for m in messages[:-1]  # Alt unntatt siste melding
    )
    dynamic_tokens = len(enc.encode(user_query)) + 4

    stats = {
        "static_prefix_tokens": static_tokens,
        "dynamic_tokens": dynamic_tokens,
        "cache_eligible": static_tokens >= 1024,
        "cache_hit_savings_pct": round(
            static_tokens / (static_tokens + dynamic_tokens) * 100, 1
        ) if static_tokens >= 1024 else 0
    }

    return messages, stats


# Eksempel: Saksbehandler-assistent for Direktoratet for digital tjenesteutvikling
messages, stats = design_cacheable_prompt(
    system_instructions="""Du er en AI-assistent for saksbehandlere i
    Direktoratet for digital tjenesteutvikling. Du hjelper med å analysere klager på vedtak om
    saksbehandling, vurdere om klagen har grunnlag, og foreslå svar.

    Regelverk du skal referere til:
    - Vegtrafikkloven § 24-34
    - fagforskriften
    - Forvaltningsloven § 28-36 (klagebehandling)

    Format: Alltid bruk overskrifter, vurder hvert punkt separat,
    og avslutt med en samlet anbefaling.""",

    few_shot_examples=[
        {
            "input": "Klage: Jeg fikk avslag på fornyelse...",
            "output": "## Vurdering\n### Regelverksvurdering..."
        },
        {
            "input": "Klage: Mitt saksbehandling ble inndratt...",
            "output": "## Vurdering\n### Regelverksvurdering..."
        }
    ],

    reference_documents="Vedtaket av 15.01.2025 om avslag...",

    user_query="Analyser denne nye klagen: ..."
)

print(f"Cacheable prefix: {stats['static_prefix_tokens']} tokens")
print(f"Cache savings: ~{stats['cache_hit_savings_pct']}%")

prompt_cache_key for forbedret hit rate

from openai import AzureOpenAI

client = AzureOpenAI(
    azure_endpoint="https://my-aoai.openai.azure.com",
    api_key="...",
    api_version="2024-12-01-preview"
)

# Bruk prompt_cache_key for å forbedre routing
# Forespørsler med samme key og prefix routes til samme cache
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    prompt_cache_key="ddt-complaint-handler-v2",  # Gruppert caching
    max_tokens=1000
)

# Sjekk cache hit
cached = response.usage.prompt_tokens_details.cached_tokens
total_prompt = response.usage.prompt_tokens

print(f"Cached tokens: {cached} / {total_prompt}")
print(f"Cache hit rate: {cached / total_prompt * 100:.1f}%")

# Advarsel: Mer enn ~15 RPM med samme prefix + cache_key
# kan overflow til andre maskiner og redusere cache-effektivitet

Cost Reduction Calculation

Beregn besparelser fra prompt caching

def calculate_caching_savings(
    monthly_requests: int,
    avg_total_input_tokens: int,
    avg_cached_tokens: int,  # Tokens som treffer cache
    model: str = "gpt-4o",
    deployment_type: str = "standard"  # "standard" eller "provisioned"
) -> dict:
    """Calculate cost savings from prompt caching."""

    # Priser (USD per 1M tokens, estimater)
    pricing = {
        "gpt-4o": {
            "standard": {"input": 2.50, "cached_discount": 0.50},
            "provisioned": {"input": 0, "cached_discount": 1.0}
        },
        "gpt-4.1": {
            "standard": {"input": 2.00, "cached_discount": 0.50},
            "provisioned": {"input": 0, "cached_discount": 1.0}
        },
        "gpt-4o-mini": {
            "standard": {"input": 0.15, "cached_discount": 0.50},
            "provisioned": {"input": 0, "cached_discount": 1.0}
        }
    }

    p = pricing.get(model, pricing["gpt-4o"])
    dt = p.get(deployment_type, p["standard"])

    non_cached_tokens = avg_total_input_tokens - avg_cached_tokens

    # Uten caching
    cost_without_cache = (
        monthly_requests * avg_total_input_tokens / 1_000_000 * dt["input"])

    # Med caching
    cached_cost = (
        monthly_requests * avg_cached_tokens / 1_000_000 *
        dt["input"] * (1 - dt["cached_discount"]))
    non_cached_cost = (
        monthly_requests * non_cached_tokens / 1_000_000 * dt["input"])
    cost_with_cache = cached_cost + non_cached_cost

    savings = cost_without_cache - cost_with_cache

    return {
        "monthly_requests": monthly_requests,
        "cache_hit_rate": round(
            avg_cached_tokens / avg_total_input_tokens * 100, 1),
        "cost_without_cache_nok": round(cost_without_cache * 11, 2),
        "cost_with_cache_nok": round(cost_with_cache * 11, 2),
        "monthly_savings_nok": round(savings * 11, 2),
        "savings_pct": round(savings / max(cost_without_cache, 0.01) * 100, 1),
        "note": (
            "PTU: cached tokens er 100% rabatt" if deployment_type == "provisioned"
            else "Standard: cached tokens er 50% rabatt")
    }

# Eksempel: 50K forespørsler/mnd med RAG-pipeline
savings = calculate_caching_savings(
    monthly_requests=50_000,
    avg_total_input_tokens=3000,
    avg_cached_tokens=2000,  # System prompt + examples cached
    model="gpt-4o",
    deployment_type="standard"
)
print(f"Månedlig besparelse: {savings['monthly_savings_nok']} NOK")
print(f"Besparelse: {savings['savings_pct']}%")

Cache Invalidation

Håndtering av cache-endringer

class CacheAwarePromptManager:
    """Manage prompts with cache invalidation awareness."""

    def __init__(self, base_system_prompt: str, version: str = "v1"):
        self.base_system_prompt = base_system_prompt
        self.version = version
        self._prefix_hash = self._compute_hash(base_system_prompt)

    def _compute_hash(self, text: str) -> str:
        import hashlib
        return hashlib.sha256(text.encode()).hexdigest()[:16]

    def update_system_prompt(self, new_prompt: str):
        """
        Oppdater system prompt. MERK: Dette invaliderer ALL cache
        for denne applikasjonen fordi prefix endres.

        Anbefaling: Gjør endringer i off-peak timer.
        """
        new_hash = self._compute_hash(new_prompt)
        if new_hash != self._prefix_hash:
            print(f"WARNING: System prompt endret. "
                  f"Cache invalideres for alle forespørsler.")
            print(f"Gammel hash: {self._prefix_hash}")
            print(f"Ny hash: {new_hash}")
            print(f"Anbefaling: Deploy endringen i off-peak timer "
                  f"for å minimere cache miss-kostnaden.")

        self.base_system_prompt = new_prompt
        self._prefix_hash = new_hash

    def get_cache_key(self) -> str:
        """Get cache key for prompt_cache_key parameter."""
        return f"app-{self.version}-{self._prefix_hash}"

# Cache invalidation triggers:
# 1. Endring i system prompt → Umiddelbar invalidering
# 2. Endring i few-shot examples → Invalidering fra det punktet
# 3. Inaktivitet > 5-10 min → Automatisk tømming
# 4. > 24 timer siden siste bruk → Garantert tømming
# 5. En eneste endret karakter i prefix → Full cache miss

Norsk offentlig sektor

  • Kostnadseffektivitet: Prompt caching er "gratis" optimalisering — ingen konfigurasjon nødvendig, bare riktig prompt-design. Spar 30-50% på input-token kostnader.
  • PTU-deployments: For PTU er cached tokens 100% gratis — dette betyr at riktig prefix-design kan doble effektiv throughput.
  • Personvern: Prompt caches er isolert per Azure-abonnement og deles ikke mellom kunder. Data i cache følger samme databehandling som vanlige forespørsler.
  • Forutsigbarhet: Cache hit rate kan monitoreres via cached_tokens i API-responsen — bygg dashboards for å spore cache-effektivitet over tid.

Beslutningsrammeverk

Scenario Anbefaling Begrunnelse
Lang system prompt (>500 tokens) Design for caching Mest å vinne
Mange few-shot examples Flytt til prefix, bruk caching Reduser input-kostnad
RAG med statisk kontekst Cache system + kontekst, varier spørsmål Høy hit rate
Unik prompt per forespørsel Caching gir lite Prefix endres for ofte
PTU deployment Maksimer caching 100% rabatt på cached tokens
Høy RPM (>15 per prefix) Bruk prompt_cache_key Forbedrer routing

Referanser

For Cosmo

  • Bruk denne referansen når kunden vil redusere kostnader eller latens for Azure OpenAI-workloads med repetitive prompt-strukturer.
  • Hovedregelen: Statisk innhold FØRST i prompten, dynamisk innhold SIST — alt statisk prefix caches automatisk.
  • Minimum 1024 tokens i identisk prefix for cache hit — legg til referansemateriale eller detaljerte instruksjoner for å nå terskelen.
  • For PTU: cached tokens teller 100% rabatt mot utilization — dette er den mest effektive optimaliseringen for PTU-deployments.
  • En eneste endret karakter i prefix gir full cache miss — vær forsiktig med dynamiske elementer (timestamps, request-IDs) i starten av prompts.