AI-orkester: flera Claude-modeller routade per uppgift

Plattformens AI-endpoint är nu en modellorkester — servern äger hela
routingtabellen (modell, effort, systemprompt, schema per uppgift), så
den justeras utan klientändringar:

- handledning    → Claude Sonnet 5 (medium): realtidssvar på varje
                   bekräftad dokumentation — latenskänsligt på golvet.
- granskning     → Claude Opus 5 (hög effort): motsägelser, luckor och
                   förhastade slutsatser i hela arbetsloggen; ny knapp
                   'AI-granska underlaget' i briefen.
- sammanfattning → Claude Sonnet 5 (låg effort): AI-komplettering av
                   överlämningen med risker & osäkerheter; ny knapp i
                   överlämningsdialogen.
- metodikval     → Claude Haiku 4.5: klassificerar felbeskrivningen
                   när nyckelordsvalet inte träffar en specifik metodik;
                   valet sparas på ärendet (nytt fält metodikId).

Alla uppgifter delar AI-reglerna och det klassificerade svarsschemat;
modellen som svarade loggas i varje ai_svar-händelse och visas i UI:t.
Felbeskrivningsfältet fick även röstinmatning.

Verifierat: 26 vitest-tester gröna (inkl. att endpointen routar till
tre distinkta modeller och att granskningsprompten innehåller hela
loggen), produktionsbygge ok, båda Playwright-sviterna gröna.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
Claude
2026-08-03 01:23:04 +00:00
parent ed483774fb
commit 8b79e3dfd5
10 changed files with 339 additions and 84 deletions
+5 -3
View File
@@ -8,14 +8,14 @@ import { persist } from "zustand/middleware";
import type { Arende, Handelse, LoggPost, Objekt } from "./domain";
import { nyLoggPost } from "./domain";
import { felbeskrivning } from "./projektioner";
import { GENERISK_METODIK, valjMetodik, type Metodik } from "./metodik";
import { GENERISK_METODIK, metodikForId, valjMetodik, type Metodik } from "./metodik";
interface FelsokningState {
anvandare: string;
arenden: Record<string, Arende>;
nastaNummer: number;
sattAnvandare: (namn: string) => void;
skapaArende: (objekt: Objekt, felbeskrivningText: string) => string;
skapaArende: (objekt: Objekt, felbeskrivningText: string, metodikId?: string) => string;
laggTill: (arendeId: string, handelse: Handelse) => void;
// Används endast av synken: ersätter listan med den ihopflätade versionen.
// Semantiken är fortfarande append-only — flätningen lägger bara till.
@@ -39,7 +39,7 @@ export const useFelsokning = create<FelsokningState>()(
sattAnvandare: (namn) => set({ anvandare: namn.trim() }),
skapaArende: (objekt, felbeskrivningText) => {
skapaArende: (objekt, felbeskrivningText, metodikId) => {
const { anvandare, nastaNummer } = get();
const id = `arende-${Date.now().toString(36)}`;
const arende: Arende = {
@@ -47,6 +47,7 @@ export const useFelsokning = create<FelsokningState>()(
nummer: nastaNummer,
skapad: new Date().toISOString(),
delningskod: nyDelningskod(),
metodikId,
handelser: [
nyLoggPost(anvandare, { typ: "objekt_identifierat", objekt }),
nyLoggPost(anvandare, { typ: "felbeskrivning", text: felbeskrivningText }),
@@ -99,6 +100,7 @@ export const useFelsokning = create<FelsokningState>()(
export function metodikForArende(arende: Arende | undefined): Metodik {
if (!arende) return GENERISK_METODIK;
if (arende.metodikId) return metodikForId(arende.metodikId);
const text = felbeskrivning(arende);
return text ? valjMetodik(text) : GENERISK_METODIK;
}