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.
598 lines
20 KiB
Markdown
598 lines
20 KiB
Markdown
# Semantic Caching in APIM
|
||
|
||
**Last updated:** 2026-02
|
||
**Status:** GA
|
||
**Category:** API Management & AI Gateway
|
||
**Type:** reference
|
||
|
||
---
|
||
|
||
## Innhold
|
||
|
||
- [Introduksjon](#introduksjon)
|
||
- [Arkitektur](#arkitektur)
|
||
- [Forutsetninger](#forutsetninger)
|
||
- [Cache-lookup og Cache-store Policies](#cache-lookup-og-cache-store-policies)
|
||
- [Embedding-Based Similarity](#embedding-based-similarity)
|
||
- [Cache Invalidation Strategies](#cache-invalidation-strategies)
|
||
- [Cost Savings Analysis](#cost-savings-analysis)
|
||
- [Privacy Considerations](#privacy-considerations)
|
||
- [Rate Limiting etter Cache Lookup](#rate-limiting-etter-cache-lookup)
|
||
- [Verifisering og feilsøking](#verifisering-og-feilsøking)
|
||
- [Komplett policy for semantic caching](#komplett-policy-for-semantic-caching)
|
||
- [Tier-kompatibilitet](#tier-kompatibilitet)
|
||
- [For Cosmo](#for-cosmo)
|
||
|
||
## 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
|
||
|
||
```bicep
|
||
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
|
||
|
||
```bicep
|
||
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:
|
||
|
||
```bicep
|
||
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
|
||
|
||
```bicep
|
||
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):**
|
||
|
||
```xml
|
||
<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):**
|
||
|
||
```xml
|
||
<azure-openai-semantic-cache-store duration="3600" />
|
||
```
|
||
|
||
### Generelle LLM-policies
|
||
|
||
For tredjeparts LLM-er eller OpenAI-kompatible endepunkter:
|
||
|
||
**Inbound:**
|
||
|
||
```xml
|
||
<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:**
|
||
|
||
```xml
|
||
<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:
|
||
|
||
```xml
|
||
<!-- 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)
|
||
|
||
```xml
|
||
<!-- 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:**
|
||
```bash
|
||
# 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:**
|
||
```xml
|
||
<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
|
||
|
||
```xml
|
||
<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:
|
||
|
||
```xml
|
||
<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
|
||
|
||
```kusto
|
||
// 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
|
||
```
|
||
|
||
```kusto
|
||
// 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
|
||
|
||
```xml
|
||
<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).
|