Ordre 20260912T193441Z-7358817909. Steg 1 var ikke transformen, men å rette
roadmapens R13-gate og få den ratifisert. Gaten `grep -rl "Cosmo"
skills/*/references -> 0` var usann på to uavhengige måter:
1. Ordren fanget den første: 451 av forekomstene er Azure Cosmos DB, ekte
produktinnhold. Diskriminatoren er ikke bokstaven «s» — `Cosmos <norsk
substantiv>` er genitiv av personaen (`### Cosmos tonalitet`), mens
`Cosmos DB`/`CosmosClient`/`cosmos_ru` er produkt.
2. Denne økten fant den andre: 132 persona-forekomster ligger i prosa,
tabeller, dialog-replikker og proveniens-linjer. Heading-nøytralisering
kan ikke nå dem, så «0 persona» er uoppnåelig også under den ratifiserte
formen. Operatøren ratifiserte alternativ A: gaten speiler formen, og de
132 bokføres til R13b/R14.
Tre korreksjoner av premisser som sto i ordren og STATE:
«ca 320 produkt» -> 451 (case-sensitivt nett manglet 327 lowercase
TOC-ankre + 99 identifikatorer; sann nevner 1 638)
«169 headinger» -> 401. 169 var `^## For Cosmo`-prefikset (168) og var
internt inkonsistent med sin egen topp-variant (204)
«417 matcher ingen
populasjon» -> 417 er cosmo-headinger utenfor kodefences; briefens
nevner var reell hele tiden
Fence-bevissthet er målt skadelig, ikke nødvendig: begge toggle-regler er
gale på dette korpuset (naiv toggle skjuler en ekte heading i
chain-of-thought-prompting.md, CommonMark-regelen ubalanserer
service-level-documentation-dr.md). Fence-agnostisk deteksjon finner 401
heading-linjer i nøyaktig de samme 40 variantene som fence-bevisst finner
400 i — ingen kodeblokk-linje er byte-identisk til en persona-heading. Derfor
nøkles transformen på 40 enumererte heading-tekster og ignorerer fences. En
ukjent variant kaster; en slug-kollisjon kaster. Ingenting auto-fikses.
TOC-en regenereres ikke, den rettes kirurgisk: alle 327 persona-lenker hadde
lenketekst lik én av de 40 heading-tekstene og anker lik slugify av den
(327/327, 0 avvik), så heading og TOC-entry skrives i samme operasjon og
ingen mellomtilstand etterlater en død lenke.
Ratifisert målform: `For Cosmo`, `For Cosmo Skyberg` og `For arkitekten
(Cosmo)` konvergerer på `For arkitekten`. To filer kolliderte og er adjudisert
ved å lese dem, ikke ved regel.
Verifisering (alle 7 kriterier fra ordren):
G1 persona på heading-linjer 401 -> 0
G2 døde fragmentlenker 1 -> 1 (pre-eksisterende, unntatt)
G3 produkt-forekomster 451 -> 451; `Cosmos DB|Azure Cosmos` 308 = 308
de 3 kun-produkt-filene byte-identiske
nettet validert begge veier injisert persona feller G1; genitiv feller G1;
produkt-heading og de 3 filene passerer
hele diffen 802 heading-linjer + 654 TOC-linjer, ANNET = 0
linjeantall 728 lagt til = 728 slettet
suite 1120/1120 (1097 + 23 nye)
validate-plugin 250 PASS / 0 FAIL
stikkprøve 10 filer, alle 5 skills, inkl. de 3 mest
produkt-tunge (26/20/19) — kun heading+TOC
Utenfor scope, urørt: de 4 SKILL.md, de 23 commands, CLAUDE.md, README.md,
NOTICE.md, docs/ (alt R14).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
- Kjernekomponenter
- Cache Eligibility Requirements
- Prefix Strategy Design
- Cost Reduction Calculation
- Cache Invalidation
- Norsk offentlig sektor
- Beslutningsrammeverk
- Referanser
- For arkitekten
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_tokensi 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
- Prompt caching — Offisiell guide
- Provisioned throughput — PTU caching-fordeler
- Semantic cache with Cosmos DB — Ekstern caching
- Application design for AI workloads — Multi-layer caching
For arkitekten
- 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.