Agentvy
AI-lagret
Var AI får bo, hur turkontraktet garanteras och varför servern alltid fattar beslutet.
page manifest · /dev/aiid: 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