Hoppa till innehållet

Devportalen /portal/dev

Startsideportalens ekosystem för människa och agent

Agentvy

AI-lagret

Var AI får bo, hur turkontraktet garanteras och varför servern alltid fattar beslutet.

page manifest · /dev/ai
id: ai
path: /dev/ai
titel: AI-lagret
summering: Var AI får bo, hur turkontraktet garanteras och varför servern alltid fattar beslutet.
nyckelord: ai, anthropic, structured outputs, turkontrakt, prompt, rate limit, eneo, wso2

## Var AI får bo

- AI-anrop går alltid via en route handler på servern. Nyckeln finns aldrig i klientkod – POC:en anropade api.anthropic.com direkt från webbläsaren, och det får inte följa med in i modulen.
- Prompten, profilen och beslutet att skapa något bor i route handlern, inte i komponenten.
- Klienten skickar text och renderar svaret. Inget mer.
- AI_PROVIDER=mock ger en regelmotor för offline-demo. Kontraktet är detsamma oavsett provider.
- Svar som blir data hämtas med structured outputs. Fri chatt som bara visas – Min AI – strömmas som text med strommaChatt och renderas som typade block, aldrig som HTML (ADR-023).
- Verktyg ges till modellen av servern, efter användarens sparade val – aldrig efter något i klientens anrop. Egna verktyg har strict-schema mot modellen och zod på servern innan något skapas; webbsidor och MCP-svar är data, inte instruktioner (ADR-024).

## Turkontraktet
Ett samtalsflöde är en serie turer med samma form. Formen garanteras av API:et, inte av en instruktion i prompten.

apps/portal/src/ai/anthropic.ts:
```
const schema = buildTurnSchema(flow);          // zod ur flödesdefinitionen
const system = byggSystemprompt(flow, profil);

const svar = await client.messages.parse({
  model: process.env.AI_MODELL ?? "claude-sonnet-5",
  max_tokens: 4000,
  system,
  output_config: {
    format: zodOutputFormat(schema),           // structured outputs
    effort: "low",
  },
  messages: historik,
});
```

- Structured outputs (output_config.format) eller strict: true på verktyget – aldrig "svara bara med JSON".
- Schemat byggs ur flödet och validerar dessutom svaret på servern.
- Uteblir ett giltigt svar görs ett omtag, inte en loop: latensen hålls nere och kostnaden förutsägbar.
- Modellen styrs av AI_MODELL så den kan bytas utan kodändring.

## Servern fattar besluten

Att modellen sätter create: true räcker aldrig. Route handlern validerar själv att allt obligatoriskt finns innan något skapas – valideringen är inte till för formatets skull utan för behörighetsbeslutet.

```
const user = await getCurrentUser();            // identiteten ur sessionen,
                                               // aldrig ur klientens payload
if (!inomTakgrans(user.userId)) return 429;    // takgräns: 40 anrop / 5 min

const rad = Body.safeParse(await request.json());   // zod på inkommande
const varden = sanera(tur.varden);                  // styrtecken bort, 500 tecken
if (!allaObligatoriskaIfyllda(flow, varden)) { /* fråga vidare, skapa inte */ }
```

## Märkning och färg

- Allt AI-genererat märks synligt: AiChip i vyn, aiNote på en action-rad.
- Grönt (--color-ai-*) är reserverat för AI-ytor och får aldrig betyda status.
- En AI-yta ska kunna peka på sin källa – lämna plats för en källrad eller en länk.

## Framåt

Steg | Innebär
M3 | Ärendeadapter: ärendet dyker upp i modulen arenden och som notis.
M4 | Motorn lyfts till packages/flow-engine. Acceptanskriteriet är att ett andra flöde kan definieras på under en timme utan ny UI-kod.
Fas 4 | Express-BFF: sessioner, nycklar, rate limit i Redis, adaptrar mot riktiga källor.
Eneo | Kommunens AI-plattform bakom WSO2 som ett tredje värde på AI_PROVIDER.

Hela portalen i samma form: /dev/llms.txt · /dev/spec.json