KB-currency refresh (medium priority, 2026-06-19) via /architect:kb-update. 74 medium-prioritets filer re-verifisert mot Microsoft Learn (MCP) — delegert til 15 parallelle Opus-subagenter (3 bølger) gruppert etter delt kilde, med disjunkte fil-sett. Verifisert i hovedkontekst (scope-sjekk + diff-review av de faktatunge gruppene + tester). Hovedendringer (faktuelle korreksjoner + currency): - Azure AI Search semantic ranker: TILGJENGELIG PÅ ALLE TIERS (også Free/Basic m/ gratis månedlig kvote) — gammel KB sa feilaktig "kun S1+". Korrigert i tier-tabell, anti-patterns og beslutningstabell (azure-ai-search-setup). - APIM score-threshold = DISTANSE (lavere = strengere): tuning-tabellen i rag-caching-optimization hadde retningen baklengs — invertert til korrekt. - Agentic retrieval GA/preview-nyanse presisert (hovedkontekst-korreksjon mot agentic-retrieval-how-to-migrate): GA via REST 2026-04-01 returnerer EKSTRAKTIV grounding (references + activity), IKKE syntetiserte svar. Answer synthesis, ikke-minimal reasoning effort (LLM query planning) og multi-turn messages forblir preview (2026-05-01-preview). Subagent hadde overforenklet til "hele kjernepipelinen GA"; rettet i agentic-rag-patterns + citation-tracking. - Copilot Studio modell-tabeller (platforms/copilot-studio): fjernet Claude Opus 4.5 + GPT-5.2 (borte fra kilde), lagt til Claude Sonnet 4.6/Opus 4.6 (GA), Opus 4.7 + Mistral Medium 3.5 (experimental); GPT-5 Reasoning/Auto = preview; A2A GA (apr 2026). - Computer Use (CUA): Copilot Studio GA 2026-05-07; 4 modeller m/ tier/status (OpenAI CUA + Sonnet 4.5 GA, Sonnet 4.6 + Opus 4.6 experimental); 5 credits/ steg standard, 15 premium; US-only region-krav FJERNET i GA-dok; Cloud PC pool + Hosted browser + bring-your-own-machine. - Azure AI Search REST API-versjoner bumpet: 2025-09-01 -> 2026-04-01 (stabil), 2025-11-01-preview -> 2026-05-01-preview (hybrid-search, rag-security-rbac, chunking). - Power Automate-integrasjon: trigger "Run a flow from Copilot" -> "When an agent calls the flow"; App Service innebygd MCP (preview) lagt til. - M365 Copilot-manifest v1.26 -> v1.28 (GA, mai) / v1.29 dokumentert (juni); "Tenant graph grounding" -> "Work IQ". - Speech fast transcription 2t/300MB -> 5t/500MB; multilingual 14 -> 15 locales (+ pt-BR). Content Understanding reasoning preview -> GA (v1.0, 2025-11-01). - Security Copilot E5 -> E5+E7. Død Databricks-URL ci-cd/best-practices -> ci-cd/flows. Prompt Flow retirement (2027-04-20 -> MAF) notert der den presenteres som go-forward. Gateway-topologi-tabell-feil rettet. - Alle 74 Last updated -> 2026-06-19. Discovery ikke kjørt (historisk kun Databricks-støy) -> 389-telling uendret, ingen resync. validate 239 PASS, kb-integrity 115/115 (262 orphan-warnings uendret), gitleaks clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REiKFhP4w6xGXXqWKpPCJJ
374 lines
14 KiB
Markdown
374 lines
14 KiB
Markdown
# Prompt Caching for Performance
|
|
|
|
**Last updated:** 2026-06-19
|
|
**Status:** GA
|
|
**Category:** Performance & Scalability
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
- [Prompt caching](https://learn.microsoft.com/azure/foundry/openai/how-to/prompt-caching) — Offisiell guide
|
|
- [Provisioned throughput](https://learn.microsoft.com/azure/foundry/openai/concepts/provisioned-throughput) — PTU caching-fordeler
|
|
- [Semantic cache with Cosmos DB](https://learn.microsoft.com/azure/cosmos-db/gen-ai/semantic-cache) — Ekstern caching
|
|
- [Application design for AI workloads](https://learn.microsoft.com/azure/well-architected/ai/application-design) — Multi-layer caching
|
|
|
|
## 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.
|