server/docs/infra/ai_gateway.md
vegard 82ee710b47 Flytt ARCHITECTURE.md til docs/, CLAUDE.md som eneste startdokument
- ARCHITECTURE.md → docs/arkitektur.md
- CLAUDE.md: ny arbeidsflyt-seksjon (commit/push fritt, deploy krever ok)
- CLAUDE.md: dokumentasjonstre med alle docs/-referanser
- Alle interne referanser oppdatert (6 filer)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 15:50:49 +01:00

8.7 KiB

Infrastruktur: AI Gateway (LiteLLM)

Filsti: docs/infra/ai_gateway.md

1. Konsept

Sidelinja bruker en sentralisert AI Gateway (LiteLLM) som eneste kontaktpunkt for alle AI-kall i systemet. All kode — Rust-workers, SvelteKit server-side — snakker med http://ai-gateway:4000/v1. Aldri direkte til leverandør-APIer.

Fordeler:

  • BYOK (Bring Your Own Key): Direkte API-nøkler til Anthropic, Google, xAI — ingen markup
  • OpenRouter som fallback: Tilgang til alle modeller vi ikke har direkte nøkler til, og sikkerhetsventil ved nedetid
  • Kostnadskontroll: Rutineoppgaver rutes til gratisnivå (Gemini), dyre modeller kun når det trengs
  • Sentralisert logging: Token-bruk per funksjon (Podcastfabrikken, Research-Klipper, Live-assistent) på ett sted
  • Redundans: Automatisk failover mellom leverandører — redaksjonen merker ikke nedetid

2. Leverandører og bruksmønster

Leverandør Nøkkeltype Primært bruksområde
Google Gemini BYOK (gratisnivå) Rutineoppgaver: transkripsjonsvasking, research-oppsummering, metadata-uttrekk
Anthropic (Claude) BYOK Oppgaver som krever høy resonneringsevne: live-assistent faktoid-vurdering, kompleks analyse
xAI (Grok) BYOK Alternativ for analyse, sanntidssøk (når tilgjengelig)
OpenRouter BYOK Fallback for alle modeller, sikkerhetsventil ved leverandør-nedetid

Merk: Kvaliteten på norsk tekst varierer mellom modeller. Test alltid med norsk innhold før en modell tildeles en produksjonsoppgave.

3. Modellruting

Modellvalg styres av to mekanismer:

3.1 Standard ruting (config.yaml)

LiteLLM konfigureres med modellaliaser som mapper til billigste egnede leverandør:

model_list:
  # Ruting: billigste først, fallback til dyrere
  - model_name: "sidelinja/rutine"
    litellm_params:
      model: "gemini/gemini-2.0-flash"
      api_key: "os.environ/GEMINI_API_KEY"
  - model_name: "sidelinja/rutine"
    litellm_params:
      model: "openrouter/google/gemini-2.0-flash-001"
      api_key: "os.environ/OPENROUTER_API_KEY"

  - model_name: "sidelinja/resonering"
    litellm_params:
      model: "anthropic/claude-sonnet-4-20250514"
      api_key: "os.environ/ANTHROPIC_API_KEY"
  - model_name: "sidelinja/resonering"
    litellm_params:
      model: "openrouter/anthropic/claude-sonnet-4-20250514"
      api_key: "os.environ/OPENROUTER_API_KEY"

router_settings:
  routing_strategy: "simple-shuffle"  # prøv første, fallback til neste
  num_retries: 2
  timeout: 60

general_settings:
  master_key: "os.environ/LITELLM_MASTER_KEY"

3.2 Jobbkø-styrt modellvalg

Jobbkøen (se jobbkø.md) spesifiserer modellalias per jobbtype:

Jobbtype Modellalias Begrunnelse
whisper_postprocess (transkripsjonsvasking) sidelinja/rutine Høyt volum, lav kompleksitet
openrouter_analyze (metadata-uttrekk) sidelinja/rutine Strukturert output, lav kompleksitet
research_clip (research-oppsummering) sidelinja/rutine Høyt volum
live_factoid_eval (live-assistent) sidelinja/resonering Krever presis vurdering under tidspress

Modellalias lagres som felt på jobben i PG — kan overstyres manuelt per jobb ved behov.

4. Docker-oppsett

# docker-compose.dev.yml / docker-compose.yml
ai-gateway:
  image: ghcr.io/berriai/litellm:main
  restart: unless-stopped
  command: --config /etc/litellm/config.yaml
  environment:
    LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
    GEMINI_API_KEY: ${GEMINI_API_KEY}
    ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
    XAI_API_KEY: ${XAI_API_KEY}
    OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
  volumes:
    - ./config/litellm/config.yaml:/etc/litellm/config.yaml:ro
  ports:
    - "127.0.0.1:4000:4000"  # kun localhost (dev), ingen port i prod
  networks:
    - sidelinja-dev  # eller sidelinja-net i prod

5. Prompt-kvalitetssikring (Promptfoo)

Alle LLM-prompts i Sidelinja testes systematisk med Promptfoo før de brukes i produksjon. Dette er spesielt viktig fordi vi jobber med norsk tekst, der modellkvaliteten varierer kraftig mellom leverandører.

5.1 Hva vi tester

Hver jobbtype som bruker LLM har et tilhørende testsett:

Jobbtype Testsett Eksempler på assertions
whisper_postprocess Norske transkripsjoner med kjente feil Egennavn korrigert, setningsflyt bevart
openrouter_analyze Episoder med kjent metadata Riktig tittel, kapitler matcher innhold
research_clip Nyhetsartikler med kjente aktører/fakta Aktører identifisert, faktoider korrekte
live_factoid_eval Transkripsjons-chunks med kjente entiteter Riktig entity-match, lav falsk-positiv-rate

5.2 Hva vi sammenligner

Promptfoo kjøres mot alle kandidatmodeller via AI Gateway:

# promptfoo-config.yaml
providers:
  - id: "openai:chat:sidelinja/rutine"
    config:
      apiBaseUrl: "http://localhost:4000/v1"
      apiKey: "${LITELLM_MASTER_KEY}"
  - id: "openai:chat:sidelinja/resonering"
    config:
      apiBaseUrl: "http://localhost:4000/v1"
      apiKey: "${LITELLM_MASTER_KEY}"

Dette lar oss svare på:

  • Klarer Gemini (gratis) denne oppgaven like bra som Claude (betalt)?
  • Fungerer prompten på norsk, eller trenger vi en annen formulering?
  • Har en modelloppgradering hos leverandøren degradert kvaliteten?

5.3 Når vi kjører tester

  • Ved ny prompt: Før den tas i bruk i produksjon
  • Ved modellbytte: Før en leverandør/modell settes som primær for en jobbtype
  • Periodisk (CI): Månedlig cron-jobb i Forgejo Actions kjører promptfoo eval mot alle testsett. Resultater postes som issue ved regresjoner. Leverandører oppdaterer modeller uten varsel — automatisk regresjonssjekk fanger dette opp.
  • Ved kvalitetsklager: Når redaksjonen rapporterer dårlig output

5.4 Lagring av testsett

Testsett og promptfoo-config versjonskontrolleres i Git under tests/prompts/. Testdata er norske eksempler fra faktiske episoder og artikler.

tests/prompts/
├── promptfooconfig.yaml
├── whisper_postprocess/
│   ├── prompt.txt
│   └── dataset.json
├── metadata_extract/
│   ├── prompt.txt
│   └── dataset.json
└── research_clip/
    ├── prompt.txt
    └── dataset.json

6. Kostnadskontroll

LiteLLM har innebygd logging, men mangler workspace-nivå budsjettering. For å forhindre kostnadssprekk:

6.1 Workspace-budsjett

Hver workspace har et månedlig AI-budsjett lagret i workspaces.settings (JSONB):

{
  "ai_budget": {
    "monthly_limit_usd": 50,
    "alert_threshold_pct": 80,
    "auto_fallback": true
  }
}
  • Sporing: SvelteKit logger token-bruk per AI-kall med workspace_id og jobbtype i ai_usage_log-tabellen (flyktig, TTL 90 dager).
  • Alert: Når 80 % av budsjettet er brukt, postes varsel i workspace-chat (system-channel).
  • Auto-fallback: Når budsjettet er nådd og auto_fallback: true, rutes alle kall til sidelinja/rutine (billigste modell). Ellers blokkeres AI-kall med feilmelding.

6.2 Per-episode maks-kostnad

Podcastfabrikken-jobber (whisper + metadata + oppsummering) kan estimere totalkostnad basert på lydlengde. Jobben avbrytes med varsel hvis estimert kostnad overstiger max_cost_per_episode (default: $5).

6.3 Modell-nedgradering

Jobbkøen støtter automatisk modell-nedgradering ved kostnadsmål:

  1. Prøv sidelinja/resonering (Claude)
  2. Ved budsjett-nær: fall tilbake til sidelinja/rutine (Gemini gratis)
  3. Ved budsjett-nådd: sett jobb i paused-status med varsel

7. Dataklassifisering (ref. docs/arkitektur.md 2.2)

Data Kategori Detaljer
LiteLLM config.yaml Gjenskapbar (Git) Versjonskontrollert
API-nøkler Kritisk (.env) Aldri i Git
Token-bruk-logger Flyktig (TTL 90 dager) For kostnadsoversikt, ryddes automatisk
Promptfoo testsett Gjenskapbar (Git) tests/prompts/ — versjonskontrollert
Promptfoo testresultater Flyktig (lokal) Kjøres on-demand, ikke lagret permanent

8. Instruks for Claude Code

  • All AI-kode skal peke på http://ai-gateway:4000/v1 — aldri direkte til leverandør
  • Bruk modellaliaser (sidelinja/rutine, sidelinja/resonering) — aldri hardkod leverandør-spesifikke modellnavn i applikasjonskode
  • API-nøkler i .env, aldri i config-filer eller kode
  • Test alltid med norsk innhold før en ny modell/leverandør tas i bruk for en produksjonsoppgave
  • Kjør promptfoo eval før du endrer prompts eller bytter modell for en jobbtype
  • Nye jobbtyper som bruker LLM skal ha et tilhørende testsett i tests/prompts/ før de merges