diff --git a/felsokning/app/src/felsokning/__tests__/metodiker.test.ts b/felsokning/app/src/felsokning/__tests__/metodiker.test.ts new file mode 100644 index 0000000..b9baafa --- /dev/null +++ b/felsokning/app/src/felsokning/__tests__/metodiker.test.ts @@ -0,0 +1,152 @@ +// Metodikbiblioteket är produktens innehåll — går det sönder märks det +// inte som ett krasch, utan som att teknikern får sämre frågor. Därför +// låser testerna strukturen, inte bara att koden kör. +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; +import { ALLA_METODIKER, GENERISK, HOGVOLT, metodikForId, metodikPoang, valjMetodik } from "../metodik"; + +describe("metodikbiblioteket", () => { + it("har unika id:n och stabila namn", () => { + const id = ALLA_METODIKER.map((m) => m.id); + expect(new Set(id).size).toBe(id.length); + for (const m of ALLA_METODIKER) { + expect(m.namn.length, m.id).toBeGreaterThan(0); + expect(m.omrade.length, m.id).toBeGreaterThan(0); + } + }); + + it("varje kontroll kräver bevis — en kryssruta är inte evidens", () => { + for (const m of ALLA_METODIKER) { + for (const steg of m.steg) { + for (const kontroll of steg.kontroller ?? []) { + expect(kontroll.krav, `${m.id}/${steg.id}/${kontroll.id}`).toBeDefined(); + } + } + } + }); + + it("varje metodik börjar med att verifiera symptomet, inte med att åtgärda", () => { + for (const m of ALLA_METODIKER) { + const symptom = m.steg.find((s) => s.id === "symptom"); + expect(symptom, m.id).toBeDefined(); + expect(symptom!.fragor?.length, m.id).toBeGreaterThan(0); + + // Undantaget är det enda som får gå före: säkerheten. + const index = m.steg.indexOf(symptom!); + expect(m.steg.slice(0, index).map((s) => s.id), m.id).toEqual( + index === 0 ? [] : ["sakerhet"], + ); + } + }); + + it("id:n inom en metodik är unika — nastaSteg spårar dem som steg/id", () => { + for (const m of ALLA_METODIKER) { + const nycklar = m.steg.flatMap((s) => [ + ...(s.fragor ?? []).map((f) => `${s.id}/f/${f.id}`), + ...(s.kontroller ?? []).map((k) => `${s.id}/k/${k.id}`), + ]); + expect(new Set(nycklar).size, m.id).toBe(nycklar.length); + expect(new Set(m.steg.map((s) => s.id)).size, m.id).toBe(m.steg.length); + } + }); + + it("val-frågor har alternativ att välja mellan", () => { + for (const m of ALLA_METODIKER) { + for (const steg of m.steg) { + for (const f of steg.fragor ?? []) { + if (f.svarstyp === "val") { + expect(f.val?.length, `${m.id}/${steg.id}/${f.id}`).toBeGreaterThan(1); + } + } + } + } + }); + + it("högvolt kan inte påbörjas utan att säkerheten är dokumenterad", () => { + // Fel här kan döda någon. Steget ligger först och kräver mätvärde på + // spänningsfrihet — inte ett ja på en fråga. + expect(HOGVOLT.steg[0].id).toBe("sakerhet"); + const krav = HOGVOLT.steg[0].kontroller ?? []; + expect(krav.some((k) => k.id === "spanningsfrihet" && k.krav === "matvarde")).toBe(true); + expect(krav.some((k) => k.id === "service_disconnect")).toBe(true); + expect(HOGVOLT.steg[0].beskrivning).toMatch(/livsfarlig/i); + }); +}); + +describe("val av metodik", () => { + it("väljer på symptomets ord, inte på ordningen i biblioteket", () => { + const fall: [string, string][] = [ + ["Bilen vibrerar runt 88 km/h", "vibration"], + ["Reläet klickar inte", "elsystem"], + ["Ingen spänning till bränslepumpen", "elsystem"], + ["Belysningen fungerar inte", "elsystem"], + ["Bilen startar inte, det klickar bara", "start_laddning"], + ["Motorn rycker vid acceleration och tappar kraft", "motor_drift"], + ["Det gnisslar vid inbromsning och bromspedalen pulserar", "bromsar"], + ["Bilen överhettar i kö, kylvätskan försvinner", "kylsystem"], + ["Automatlådan rycker vid växling", "drivlina"], + ["Partikelfiltret regenererar hela tiden", "avgas_emission"], + ["AC:n kyler inte längre", "klimat"], + ["Elbilen laddar inte på DC och räckvidden har minskat", "hogvolt"], + ["Ingen kontakt med styrenheten, flera lampor lyser", "diagnos_natverk"], + ["Det droppar olja på garagegolvet", "lackage"], + ["Ett knackande missljud från fram vänster", "missljud"], + ["Filhållningsassistansen måste kalibreras efter byte av vindruta", "adas"], + ["Stötdämparen läcker och bilen gungar", "styrning_fjadring"], + ]; + for (const [text, forvantat] of fall) { + expect(valjMetodik(text).id, text).toBe(forvantat); + } + }); + + it("faller tillbaka på generisk i stället för att gissa", () => { + for (const text of ["", "Kunden är missnöjd", "Se bifogad arbetsorder", "Konstigt beteende ibland"]) { + expect(valjMetodik(text).id, text).toBe("generisk"); + } + }); + + it("korta nyckelord matchas som helt ord, långa som ordstam", () => { + // "ac" får inte träffa "acceleration" — annars hamnar en vibration i + // klimatanläggningen. + expect(metodikPoang(ALLA_METODIKER.find((m) => m.id === "klimat")!, "vibration vid acceleration")).toBe(0); + // Men stammen "vibration" ska träffa böjda former. + const vibration = ALLA_METODIKER.find((m) => m.id === "vibration")!; + expect(metodikPoang(vibration, "kraftiga vibrationer")).toBeGreaterThan(0); + expect(metodikPoang(vibration, "skakningar i ratten")).toBeGreaterThan(0); + }); + + it("mer specifikt nyckelord vinner över mindre specifikt", () => { + // Både elsystem ("batteri" via ström/spänning) och högvolt kan kännas + // relevanta — "traktionsbatteri" avgör. + expect(valjMetodik("Traktionsbatteriet ger felmeddelande").id).toBe("hogvolt"); + }); + + it("är stabil: samma text ger samma metodik", () => { + const text = "Bilen drar åt höger och det gnisslar"; + const forsta = valjMetodik(text).id; + for (let i = 0; i < 5; i += 1) expect(valjMetodik(text).id).toBe(forsta); + }); + + it("metodikForId faller tillbaka på generisk vid okänt id", () => { + expect(metodikForId("hogvolt").id).toBe("hogvolt"); + expect(metodikForId("finns_inte")).toBe(GENERISK); + }); +}); + +describe("orkestern och klienten känner till samma metodiker", () => { + // Klassificeraren i orkestern kan bara returnera id:n som finns i + // schemat. Glider listorna isär returnerar den ett id klienten inte + // känner igen, och valet faller tyst tillbaka på generisk. + const idn = ALLA_METODIKER.map((m) => m.id).sort(); + + for (const fil of ["../services/ai-orkester/server.mjs", "../supabase/functions/felsokning-ai/index.ts"]) { + it(fil, () => { + const kalla = readFileSync(fil, "utf8"); + const katalog = kalla.match(/const METODIK_KATALOG = \[([\s\S]*?)\n\];/); + expect(katalog, "METODIK_KATALOG saknas").not.toBeNull(); + + const iOrkestern = [...katalog![1].matchAll(/\["([a-z_]+)",/g)].map((m) => m[1]).sort(); + expect(iOrkestern).toEqual(idn); + }); + } +}); diff --git a/felsokning/app/src/felsokning/__tests__/projektioner.test.ts b/felsokning/app/src/felsokning/__tests__/projektioner.test.ts index 3a0e34c..7baf8f0 100644 --- a/felsokning/app/src/felsokning/__tests__/projektioner.test.ts +++ b/felsokning/app/src/felsokning/__tests__/projektioner.test.ts @@ -42,7 +42,9 @@ describe("verifierade checklistor", () => { describe("metodikval", () => { it("väljer vibrationsmetodiken när felbeskrivningen nämner vibration", () => { expect(valjMetodik("Bilen vibrerar runt 88 km/h").id).toBe("vibration"); - expect(valjMetodik("Motorn startar inte").id).toBe("generisk"); + // Utan igenkännbart symptom väljs ingen ingång — generisk är svaret, + // inte en gissning. (Se metodiker.test.ts för hela urvalet.) + expect(valjMetodik("Kunden vill ha en genomgång").id).toBe("generisk"); }); it("väljer elsystemmetodiken vid elrelaterade symptom", () => { diff --git a/felsokning/app/src/felsokning/metodik.ts b/felsokning/app/src/felsokning/metodik.ts index bbf2695..9402d7a 100644 --- a/felsokning/app/src/felsokning/metodik.ts +++ b/felsokning/app/src/felsokning/metodik.ts @@ -2,11 +2,12 @@ // Systemet presenterar aldrig en hypotes som ett konstaterat fel — // motorn föreslår nästa verifierbara steg utifrån vad som redan är dokumenterat. // -// I MVP:t är metodikerna fördefinierade flöden. Den framtida AI-integrationen -// ansluter här: den väljer/genererar steg, men händelseloggen och -// projektionerna är oförändrade. +// Här bor motorn: typerna, valet av metodik och härledningen av nästa +// steg. Själva metodikerna ligger i ./metodiker — de växer i antal, och +// motorn ska inte behöva ändras när de gör det. import type { Arende } from "./domain"; +import { ALLA_METODIKER, ELSYSTEM, GENERISK, VIBRATION } from "./metodiker"; export interface Fraga { id: string; @@ -36,205 +37,108 @@ export interface MetodikSteg { export interface Metodik { id: string; namn: string; + /** Fordonets system, för gruppering i listor. */ + omrade: string; + /** + * Ord ur kundens eller teknikerns språk, inte ur konstruktörens. + * Används enbart för att välja ingång — aldrig för att dra en slutsats + * om vad felet är. + */ + nyckelord: string[]; steg: MetodikSteg[]; } -export const VIBRATION_METODIK: Metodik = { - id: "vibration", - namn: "Vibration under körning", - steg: [ - { - id: "symptom", - rubrik: "Verifiera symptom", - fragor: [ - { id: "hastighetsberoende", text: "Är vibrationen hastighetsberoende?", svarstyp: "janej" }, - { - id: "var", - text: "Var känns vibrationen?", - svarstyp: "val", - val: ["I ratten", "I stolen", "I hela bilen"], - }, - { - id: "nar", - text: "När uppstår vibrationen?", - svarstyp: "val", - val: ["Vid acceleration", "Vid jämn fart", "Vid inbromsning", "Alltid under körning"], - }, - { - id: "intervall", - text: "Försvinner den över eller under ett visst hastighetsintervall?", - svarstyp: "text", - }, - ], - }, - { - id: "visuell", - rubrik: "Visuell kontroll", - beskrivning: "Fotografera samtliga hjul. Skilj på observation och slutsats.", - kontroller: [ - { id: "foto_vf", text: "Fotografera vänster framhjul", krav: "foto" }, - { id: "foto_hf", text: "Fotografera höger framhjul", krav: "foto" }, - { id: "foto_vb", text: "Fotografera vänster bakhjul", krav: "foto" }, - { id: "foto_hb", text: "Fotografera höger bakhjul", krav: "foto" }, - { id: "dot", text: "Dokumentera DOT-/tillverkningsdatum", krav: "kommentar" }, - { id: "slitage", text: "Kontrollera däckslitage", krav: "kommentar" }, - ], - }, - { - id: "kontroller", - rubrik: "Rekommenderade kontroller", - kontroller: [ - { id: "lufttryck", text: "Kontrollera lufttryck", krav: "matvarde" }, - { id: "hjulmoment", text: "Kontrollera hjulmoment", krav: "matvarde" }, - { id: "kast", text: "Kontrollera radial- och sidokast", krav: "matvarde" }, - { id: "balansering", text: "Kontrollera hjulbalansering", krav: "kommentar" }, - { id: "bussningar", text: "Kontrollera bussningar och leder", krav: "kommentar" }, - ], - }, - { - id: "provkorning", - rubrik: "Provkörning", - beskrivning: "Verifiera under provkörning:", - kontroller: [ - { id: "pk_hastighet", text: "Hastighet där vibration uppstår", krav: "kommentar" }, - { id: "pk_acc", text: "Förändring vid acceleration", krav: "kommentar" }, - { id: "pk_motorbroms", text: "Förändring vid motorbroms", krav: "kommentar" }, - { id: "pk_kurva", text: "Förändring vid kurvtagning", krav: "kommentar" }, - { id: "pk_var", text: "Om vibration känns i ratt eller kaross", krav: "kommentar" }, - ], - }, - ], -}; +// ---- Biblioteket --------------------------------------------------------- +// +// Metodikerna definieras i ./metodiker. Namnen nedan finns kvar för att +// äldre kod och tester ska fortsätta fungera oförändrat. -export const GENERISK_METODIK: Metodik = { - id: "generisk", - namn: "Generell strukturerad felsökning", - steg: [ - { - id: "symptom", - rubrik: "Verifiera symptom", - fragor: [ - { id: "reproducerbart", text: "Går felet att reproducera?", svarstyp: "janej" }, - { - id: "nar", - text: "När uppträder felet?", - svarstyp: "val", - val: ["Alltid", "Ibland", "Endast under drift", "Endast vid start"], - }, - { - id: "var", - text: "Var upplevs felet?", - svarstyp: "val", - val: ["Fram", "Bak", "Höger", "Vänster", "Motor/drivkälla", "Kupé/förarplats", "Okänt"], - }, - { - id: "hur", - text: "Hur upplevs felet?", - svarstyp: "val", - val: ["Skakar/vibrerar", "Missljud (knack/gnissel/vinande)", "Rycker", "Tappar kraft", "Stannar helt", "Annat"], - }, - { id: "forlopp", text: "Beskriv förloppet när felet uppstår", svarstyp: "text" }, - { id: "forandring", text: "Har något förändrats nyligen (service, reparation, miljö)?", svarstyp: "text" }, - ], - }, - { - id: "visuell", - rubrik: "Visuell kontroll", - kontroller: [ - { id: "foto_objekt", text: "Fotografera objektet och det berörda området", krav: "foto" }, - { id: "skador", text: "Kontrollera synliga skador, läckage eller lösa anslutningar", krav: "kommentar" }, - { id: "typskylt", text: "Dokumentera typskylt/märkning", krav: "foto" }, - ], - }, - { - id: "grundkontroller", - rubrik: "Grundkontroller", - kontroller: [ - { id: "matning", text: "Utför grundläggande mätningar (spänning, tryck, temperatur — det som är relevant)", krav: "matvarde" }, - { id: "sakringar", text: "Kontrollera säkringar/skydd", krav: "kommentar" }, - { id: "anslutningar", text: "Kontrollera kontaktdon och anslutningar", krav: "kommentar" }, - ], - }, - { - id: "funktionstest", - rubrik: "Funktionstest", - kontroller: [ - { id: "test", text: "Genomför funktionstest och dokumentera resultatet", krav: "kommentar" }, - ], - }, - ], -}; +export { + ALLA_METODIKER, + VIBRATION, + BROMSAR, + STYRNING_FJADRING, + ELSYSTEM, + START_LADDNING, + MOTOR_DRIFT, + KYLSYSTEM, + DRIVLINA, + AVGAS_EMISSION, + KLIMAT, + HOGVOLT, + DIAGNOS_NATVERK, + LACKAGE, + MISSLJUD, + ADAS, + GENERISK, +} from "./metodiker"; -// Elsystem-metodik enligt visionens relä-exempel: mätningar med krav på -// värden, reläkontroll och funktionstest. -export const ELSYSTEM_METODIK: Metodik = { - id: "elsystem", - namn: "Elsystem / strömförsörjning", - steg: [ - { - id: "symptom", - rubrik: "Verifiera symptom", - fragor: [ - { id: "reproducerbart", text: "Går felet att reproducera?", svarstyp: "janej" }, - { - id: "omfattning", - text: "Är funktionen helt död eller delvis fungerande?", - svarstyp: "val", - val: ["Helt död", "Delvis fungerande", "Intermittent"], - }, - { id: "funktion", text: "Vilken funktion saknas eller felar?", svarstyp: "text" }, - { id: "forandring", text: "Har något åtgärdats eller förändrats nyligen?", svarstyp: "text" }, - ], - }, - { - id: "visuell", - rubrik: "Visuell kontroll", - kontroller: [ - { id: "foto_komponent", text: "Fotografera komponenten och dess anslutningar", krav: "foto" }, - { id: "skador", text: "Kontrollera synliga skador, korrosion och lösa kontaktdon", krav: "kommentar" }, - ], - }, - { - id: "matningar", - rubrik: "Mätningar", - kontroller: [ - { id: "batterispanning", text: "Mät batterispänning", krav: "matvarde" }, - { id: "sakringar", text: "Kontrollera berörda säkringar", krav: "kommentar" }, - { id: "matningsspanning", text: "Mät matningsspänning vid komponenten", krav: "matvarde" }, - { id: "jord", text: "Mät spänningsfall mot jord", krav: "matvarde" }, - ], - }, - { - id: "rela", - rubrik: "Relä och styrsignal", - kontroller: [ - { id: "rela_klick", text: "Kontrollera om reläet klickar vid aktivering", krav: "kommentar" }, - { id: "styrsignal", text: "Mät styrsignal till reläet", krav: "matvarde" }, - ], - }, - { - id: "funktionstest", - rubrik: "Funktionstest", - kontroller: [ - { id: "test", text: "Genomför funktionstest och dokumentera resultatet", krav: "kommentar" }, - ], - }, - ], -}; - -export const METODIKER = [VIBRATION_METODIK, ELSYSTEM_METODIK, GENERISK_METODIK]; +export const VIBRATION_METODIK = VIBRATION; +export const ELSYSTEM_METODIK = ELSYSTEM; +export const GENERISK_METODIK = GENERISK; +export const METODIKER = ALLA_METODIKER; export function metodikForId(id: string): Metodik { - return METODIKER.find((m) => m.id === id) ?? GENERISK_METODIK; + return ALLA_METODIKER.find((m) => m.id === id) ?? GENERISK; } -export function valjMetodik(felbeskrivning: string): Metodik { - const text = felbeskrivning.toLowerCase(); - if (/vibr|skak|obalans/.test(text)) return VIBRATION_METODIK; - if (/relä|rela\b|säkring|elektr|spänning|volt\b|batteri|lampa|belysning|ström|kortslut/.test(text)) { - return ELSYSTEM_METODIK; +// ---- Val av metodik ------------------------------------------------------ + +// Korta ord matchas som helt ord, längre som ordstam. Annars skulle "ac" +// träffa "acceleration" och en vibration hamna i klimatanläggningen. +const HELT_ORD = 3; +const ORDTECKEN = "a-z0-9åäöéèü"; + +const monsterCache = new Map(); + +function monster(nyckelord: string): RegExp { + let m = monsterCache.get(nyckelord); + if (!m) { + const bokstavligt = nyckelord.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const slut = nyckelord.length <= HELT_ORD ? `(?![${ORDTECKEN}])` : ""; + m = new RegExp(`(? basta) { + basta = poang; + bast = metodik; + } + } + return bast; } export interface NastaSteg { diff --git a/felsokning/app/src/felsokning/metodiker.ts b/felsokning/app/src/felsokning/metodiker.ts new file mode 100644 index 0000000..ca0e6f6 --- /dev/null +++ b/felsokning/app/src/felsokning/metodiker.ts @@ -0,0 +1,899 @@ +// Metodikbiblioteket. +// +// En metodik är inte en checklista över vad som brukar vara fel — det +// vore att gissa åt teknikern. Den är en ordning för att utesluta: +// symptomet verifieras först, sedan det billiga och synliga, sedan det +// som kräver mätning, sist det som kräver demontering. +// +// Tre regler gäller alla metodiker här: +// +// 1. Varje kontroll har ett minimikrav (mätvärde, foto eller +// observation). En kryssruta är inte evidens. +// 2. Symptomsteget frågar aldrig "vad är fel" utan "vad händer, när, +// var". Kundens ord blir ett verifierat symptom först när det +// reproducerats. +// 3. Där arbetet kan skada någon kommer säkerhetssteget först och kan +// inte hoppas över — se HÖGVOLT. +// +// Att "täcka allt" går inte att lova. Det som går är att täcka fordonets +// system systematiskt och låta GENERISK vara ett strukturellt komplett +// skyddsnät: den fungerar även för det ingen förutsett. + +import type { Metodik } from "./metodik"; + +export const VIBRATION: Metodik = { + id: "vibration", + namn: "Vibration under körning", + omrade: "Hjul och balans", + nyckelord: ["vibration", "vibrer", "skak", "obalans", "ojämn gång", "rattvibration"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { id: "hastighetsberoende", text: "Är vibrationen hastighetsberoende?", svarstyp: "janej" }, + { id: "var", text: "Var känns vibrationen?", svarstyp: "val", val: ["I ratten", "I stolen", "I hela bilen"] }, + { + id: "nar", + text: "När uppstår vibrationen?", + svarstyp: "val", + val: ["Vid acceleration", "Vid jämn fart", "Vid inbromsning", "Alltid under körning"], + }, + { id: "intervall", text: "Försvinner den över eller under ett visst hastighetsintervall?", svarstyp: "text" }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + beskrivning: "Fotografera samtliga hjul. Skilj på observation och slutsats.", + kontroller: [ + { id: "foto_vf", text: "Fotografera vänster framhjul", krav: "foto" }, + { id: "foto_hf", text: "Fotografera höger framhjul", krav: "foto" }, + { id: "foto_vb", text: "Fotografera vänster bakhjul", krav: "foto" }, + { id: "foto_hb", text: "Fotografera höger bakhjul", krav: "foto" }, + { id: "dot", text: "Dokumentera DOT-/tillverkningsdatum", krav: "kommentar" }, + { id: "slitage", text: "Kontrollera däckslitage och slitagemönster", krav: "kommentar" }, + ], + }, + { + id: "kontroller", + rubrik: "Mätningar", + kontroller: [ + { id: "lufttryck", text: "Kontrollera lufttryck", krav: "matvarde" }, + { id: "hjulmoment", text: "Kontrollera hjulmoment", krav: "matvarde" }, + { id: "kast", text: "Kontrollera radial- och sidokast", krav: "matvarde" }, + { id: "balansering", text: "Kontrollera hjulbalansering", krav: "kommentar" }, + { id: "bussningar", text: "Kontrollera bussningar och leder", krav: "kommentar" }, + ], + }, + { + id: "provkorning", + rubrik: "Provkörning", + beskrivning: "Verifiera under provkörning:", + kontroller: [ + { id: "pk_hastighet", text: "Hastighet där vibration uppstår", krav: "kommentar" }, + { id: "pk_acc", text: "Förändring vid acceleration", krav: "kommentar" }, + { id: "pk_motorbroms", text: "Förändring vid motorbroms", krav: "kommentar" }, + { id: "pk_kurva", text: "Förändring vid kurvtagning", krav: "kommentar" }, + { id: "pk_var", text: "Om vibration känns i ratt eller kaross", krav: "kommentar" }, + ], + }, + ], +}; + +export const ELSYSTEM: Metodik = { + id: "elsystem", + namn: "Elsystem och strömförsörjning", + omrade: "El", + nyckelord: ["relä", "rela", "säkring", "elektr", "spänning", "volt", "lampa", "belysning", "ström", "kortslut", "kabel", "kabla", "kontaktdon", "jordfel"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { id: "reproducerbart", text: "Går felet att reproducera?", svarstyp: "janej" }, + { + id: "omfattning", + text: "Är funktionen helt död eller delvis fungerande?", + svarstyp: "val", + val: ["Helt död", "Delvis fungerande", "Intermittent"], + }, + { id: "funktion", text: "Vilken funktion saknas eller felar?", svarstyp: "text" }, + { id: "forandring", text: "Har något åtgärdats eller förändrats nyligen?", svarstyp: "text" }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + kontroller: [ + { id: "foto_komponent", text: "Fotografera komponenten och dess anslutningar", krav: "foto" }, + { id: "skador", text: "Kontrollera synliga skador, korrosion och lösa kontaktdon", krav: "kommentar" }, + ], + }, + { + id: "matningar", + rubrik: "Mätningar", + beskrivning: "Mät under belastning. En spänning utan last säger nästan ingenting om en dålig förbindelse.", + kontroller: [ + { id: "batterispanning", text: "Mät batterispänning", krav: "matvarde" }, + { id: "sakringar", text: "Kontrollera berörda säkringar", krav: "kommentar" }, + { id: "matningsspanning", text: "Mät matningsspänning vid komponenten under last", krav: "matvarde" }, + { id: "jord", text: "Mät spänningsfall mot jord under last", krav: "matvarde" }, + ], + }, + { + id: "rela", + rubrik: "Relä och styrsignal", + kontroller: [ + { id: "rela_klick", text: "Kontrollera om reläet klickar vid aktivering", krav: "kommentar" }, + { id: "styrsignal", text: "Mät styrsignal till reläet", krav: "matvarde" }, + ], + }, + { + id: "funktionstest", + rubrik: "Funktionstest", + kontroller: [{ id: "test", text: "Genomför funktionstest och dokumentera resultatet", krav: "kommentar" }], + }, + ], +}; + +export const START_LADDNING: Metodik = { + id: "start_laddning", + namn: "Start- och laddningssystem", + omrade: "El", + nyckelord: ["startar inte", "startproblem", "startmotor", "generator", "laddning", "batteriet dör", "urladdat", "batterilampa", "drar inte runt", "klick vid start"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "beteende", + text: "Vad händer vid startförsök?", + svarstyp: "val", + val: ["Ingenting alls", "Ett klick", "Drar runt långsamt", "Drar runt normalt men tänder inte", "Startar ibland"], + }, + { id: "kallt_varmt", text: "Uppträder felet kallt, varmt eller alltid?", svarstyp: "val", val: ["Kallt", "Varmt", "Alltid"] }, + { id: "stillestand", text: "Hur länge har fordonet stått mellan försöken?", svarstyp: "text" }, + { id: "laddat", text: "Har batteriet laddats eller bytts nyligen?", svarstyp: "text" }, + ], + }, + { + id: "batteri", + rubrik: "Batteriets kondition", + beskrivning: "Ett svagt batteri ger följdfel i hela fordonet. Uteslut det först.", + kontroller: [ + { id: "vilospanning", text: "Mät vilospänning efter minst en timmes vila", krav: "matvarde" }, + { id: "batteritest", text: "Belastningstest — fotografera testarens resultat", krav: "foto" }, + { id: "poler", text: "Kontrollera polskor och jordflätor", krav: "kommentar" }, + ], + }, + { + id: "start", + rubrik: "Startsystem", + kontroller: [ + { id: "startspanning", text: "Mät spänning vid startmotorn under startförsök", krav: "matvarde" }, + { id: "spanningsfall_plus", text: "Mät spänningsfall i plusledningen under start", krav: "matvarde" }, + { id: "spanningsfall_jord", text: "Mät spänningsfall i jordledningen under start", krav: "matvarde" }, + { id: "startsignal", text: "Kontrollera styrsignal till startrelä", krav: "matvarde" }, + ], + }, + { + id: "laddning", + rubrik: "Laddningssystem", + kontroller: [ + { id: "laddspanning", text: "Mät laddspänning vid tomgång och vid förhöjt varvtal", krav: "matvarde" }, + { id: "rippel", text: "Mät rippel från generatorn", krav: "matvarde" }, + { id: "rem", text: "Kontrollera drivrem och spänning", krav: "kommentar" }, + ], + }, + { + id: "krypstrom", + rubrik: "Krypström", + beskrivning: "Endast relevant när batteriet laddar ur under stillestånd.", + kontroller: [ + { id: "vilostrom", text: "Mät viloström efter att fordonet somnat", krav: "matvarde" }, + { id: "sakringsdrag", text: "Lokalisera krets genom successiv säkringsurtagning", krav: "kommentar" }, + ], + }, + ], +}; + +export const MOTOR_DRIFT: Metodik = { + id: "motor_drift", + namn: "Motorgång och effekt", + omrade: "Motor", + nyckelord: ["ryck", "tappar kraft", "effektbrist", "misständ", "ojämn tomgång", "tomgång", "motorlampa", "kraftlös", "hack", "går på tre"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "nar", + text: "När uppträder felet?", + svarstyp: "val", + val: ["Vid kallstart", "Vid tomgång", "Under belastning", "Vid visst varvtal", "Alltid"], + }, + { id: "varvtal", text: "Vid vilket varvtal eller vilken last?", svarstyp: "text" }, + { id: "varningslampa", text: "Lyser eller blinkar motorlampan?", svarstyp: "val", val: ["Lyser fast", "Blinkar", "Slocknad"] }, + { id: "brankslebyte", text: "Har bränsle tankats eller service utförts strax innan?", svarstyp: "text" }, + ], + }, + { + id: "felkoder", + rubrik: "Felkoder och mätdata", + beskrivning: "Läs innan något demonteras. En felkod pekar på en krets, inte på en trasig komponent.", + kontroller: [ + { id: "dtc", text: "Läs felkoder — fotografera diagnosskärmen", krav: "foto" }, + { id: "frysdata", text: "Dokumentera frysramsdata för koden", krav: "kommentar" }, + { id: "misstandning", text: "Läs misständningsräknare per cylinder", krav: "matvarde" }, + { id: "branslekorrigering", text: "Läs kort- och långtidsbränslekorrigering", krav: "matvarde" }, + ], + }, + { + id: "mekanik", + rubrik: "Motorns grundkondition", + beskrivning: "Utan grundkondition är alla givarvärden svåra att tolka.", + kontroller: [ + { id: "kompression", text: "Mät kompression eller relativ kompression", krav: "matvarde" }, + { id: "vevhustryck", text: "Kontrollera vevhustryck", krav: "kommentar" }, + { id: "insugslackage", text: "Kontrollera insugssystemet för falskluft", krav: "kommentar" }, + ], + }, + { + id: "tandning_bransle", + rubrik: "Tändning och bränsle", + kontroller: [ + { id: "tandstift", text: "Kontrollera tändstift — fotografera samtliga", krav: "foto" }, + { id: "spolar", text: "Mät tändspolarnas resistans eller byt mellan cylindrar", krav: "matvarde" }, + { id: "bransletryck", text: "Mät bränsletryck under belastning", krav: "matvarde" }, + { id: "insprutare", text: "Kontrollera insprutarnas returmängd eller styrning", krav: "matvarde" }, + ], + }, + { + id: "provkorning", + rubrik: "Provkörning med loggning", + kontroller: [ + { id: "logg", text: "Logga mätdata under körning där felet uppträder", krav: "foto" }, + { id: "reproducerat", text: "Dokumentera under vilka förhållanden felet uppträdde", krav: "kommentar" }, + ], + }, + ], +}; + +export const BROMSAR: Metodik = { + id: "bromsar", + namn: "Bromssystem", + omrade: "Chassi", + nyckelord: ["broms", "bromspedal", "skevhet", "gnisslar vid inbromsning", "abs", "bromsvätska", "handbroms", "drar snett vid inbromsning"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "karaktar", + text: "Hur yttrar sig felet?", + svarstyp: "val", + val: ["Skevhet/pulsering", "Missljud", "Lång pedalväg", "Mjuk pedal", "Drar åt sidan", "Bromsen ligger på"], + }, + { id: "nar", text: "Vid vilken hastighet och bromskraft?", svarstyp: "text" }, + { id: "varningslampa", text: "Lyser broms- eller ABS-varning?", svarstyp: "janej" }, + { id: "senaste", text: "När byttes bromskomponenter senast?", svarstyp: "text" }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + kontroller: [ + { id: "foto_belagg", text: "Fotografera belägg och skivor på samtliga hjul", krav: "foto" }, + { id: "belaggtjocklek", text: "Mät beläggtjocklek", krav: "matvarde" }, + { id: "skivtjocklek", text: "Mät skivtjocklek och jämför med minmått", krav: "matvarde" }, + { id: "lackage", text: "Kontrollera slangar, rör och ok för läckage", krav: "kommentar" }, + ], + }, + { + id: "matningar", + rubrik: "Mätningar", + kontroller: [ + { id: "skivkast", text: "Mät skivkast med mätklocka", krav: "matvarde" }, + { id: "tjockleksvariation", text: "Mät tjockleksvariation på skivan", krav: "matvarde" }, + { id: "vatska", text: "Mät bromsvätskans kokpunkt eller vatteninnehåll", krav: "matvarde" }, + { id: "okens_rorlighet", text: "Kontrollera okens och styrbultarnas rörlighet", krav: "kommentar" }, + ], + }, + { + id: "system", + rubrik: "System och ABS", + kontroller: [ + { id: "dtc", text: "Läs felkoder ur ABS-styrenheten", krav: "foto" }, + { id: "hjulgivare", text: "Kontrollera hjulhastighetsgivarnas signal", krav: "matvarde" }, + { id: "bromsprov", text: "Utför bromsprov i provbänk — fotografera protokollet", krav: "foto" }, + ], + }, + ], +}; + +export const STYRNING_FJADRING: Metodik = { + id: "styrning_fjadring", + namn: "Styrning och fjädring", + omrade: "Chassi", + nyckelord: ["drar åt", "styrning", "ratt", "glapp", "stötdämpar", "fjäder", "fjädr", "hjulinställning", "spårar", "gungar", "knäpper i kurva", "servostyrning"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "karaktar", + text: "Hur yttrar sig felet?", + svarstyp: "val", + val: ["Drar åt sidan", "Glapp i ratten", "Missljud", "Dålig vägkänsla", "Ojämnt däckslitage", "Gungar/studsar"], + }, + { id: "underlag", text: "Uppträder det på visst underlag eller i viss kurva?", svarstyp: "text" }, + { id: "handelse", text: "Har fordonet kört på något eller varit i kontakt med trottoarkant?", svarstyp: "text" }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + kontroller: [ + { id: "foto_slitage", text: "Fotografera däckens slitagemönster", krav: "foto" }, + { id: "dampare", text: "Kontrollera dämpare för läckage", krav: "kommentar" }, + { id: "damask", text: "Kontrollera damasker på styrleder och drivknutar", krav: "kommentar" }, + ], + }, + { + id: "glapp", + rubrik: "Glappkontroll", + beskrivning: "Utförs med hjulet avlastat. Dokumentera var glappet finns, inte bara att det finns.", + kontroller: [ + { id: "hjullager", text: "Kontrollera hjullagerspel", krav: "kommentar" }, + { id: "styrleder", text: "Kontrollera styr- och bärleder", krav: "kommentar" }, + { id: "bussningar", text: "Kontrollera bussningar", krav: "kommentar" }, + { id: "kuggstang", text: "Kontrollera kuggstång och styrstagsändar", krav: "kommentar" }, + ], + }, + { + id: "installning", + rubrik: "Hjulinställning", + kontroller: [ + { id: "matning", text: "Mät hjulinställning — fotografera protokollet före justering", krav: "foto" }, + { id: "avvikelse", text: "Dokumentera avvikelser mot tillverkarens värden", krav: "matvarde" }, + ], + }, + ], +}; + +export const KYLSYSTEM: Metodik = { + id: "kylsystem", + namn: "Kylsystem och överhettning", + omrade: "Motor", + nyckelord: ["överhett", "koka", "kylvätska", "temperatur", "termostat", "kylare", "vattenpump", "värme i kupén", "expansionskärl"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "nar", + text: "När stiger temperaturen?", + svarstyp: "val", + val: ["Vid tomgång", "I låg fart", "I hög fart", "Vid belastning/backe", "Alltid"], + }, + { id: "kylvatskeforlust", text: "Förloras kylvätska? Hur mycket och hur ofta?", svarstyp: "text" }, + { id: "varme", text: "Fungerar kupévärmen?", svarstyp: "janej" }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + kontroller: [ + { id: "niva", text: "Kontrollera kylvätskenivå kall — fotografera expansionskärlet", krav: "foto" }, + { id: "lackage", text: "Kontrollera synligt läckage vid slangar, kylare och pump", krav: "kommentar" }, + { id: "kylarpaket", text: "Kontrollera kylarpaketet för igensättning", krav: "foto" }, + ], + }, + { + id: "matningar", + rubrik: "Mätningar", + kontroller: [ + { id: "trycktest", text: "Trycktesta kylsystemet", krav: "matvarde" }, + { id: "lock", text: "Trycktesta expansionskärlets lock", krav: "matvarde" }, + { id: "temperatur", text: "Mät temperatur in och ut ur kylaren med IR-termometer", krav: "matvarde" }, + { id: "flakt", text: "Kontrollera att kylfläkten startar vid rätt temperatur", krav: "kommentar" }, + { id: "givare", text: "Jämför styrenhetens temperaturvärde med uppmätt", krav: "matvarde" }, + ], + }, + { + id: "packning", + rubrik: "Topplockspackning", + beskrivning: "Görs när kylvätska förloras utan yttre läckage.", + kontroller: [ + { id: "avgastest", text: "Avgastest i kylvätskan (CO2-test)", krav: "foto" }, + { id: "kompression_kyl", text: "Kontrollera trycksättning av kylsystemet vid motorgång", krav: "kommentar" }, + ], + }, + ], +}; + +export const DRIVLINA: Metodik = { + id: "drivlina", + namn: "Växellåda och drivlina", + omrade: "Drivlina", + nyckelord: ["växellåda", "växlar", "koppling", "automatlåda", "rycker vid växling", "slir", "drivknut", "kardan", "differential", "kärvar i växeln"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "karaktar", + text: "Hur yttrar sig felet?", + svarstyp: "val", + val: ["Slirar", "Rycker vid växling", "Går inte i växel", "Missljud", "Vibration vid acceleration", "Läckage"], + }, + { id: "vaxel", text: "Vilken växel eller vilket varvtal?", svarstyp: "text" }, + { id: "kall_varm", text: "Kall eller varm låda?", svarstyp: "val", val: ["Kall", "Varm", "Ingen skillnad"] }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + kontroller: [ + { id: "olja", text: "Kontrollera oljenivå och oljans skick — fotografera", krav: "foto" }, + { id: "lackage", text: "Kontrollera läckage vid tätningar och sump", krav: "kommentar" }, + { id: "fasten", text: "Kontrollera växellådsfästen", krav: "kommentar" }, + ], + }, + { + id: "matningar", + rubrik: "Mätningar och avläsning", + kontroller: [ + { id: "dtc", text: "Läs felkoder ur växellådsstyrenheten", krav: "foto" }, + { id: "adaptioner", text: "Dokumentera adaptionsvärden och kopplingsslitage", krav: "matvarde" }, + { id: "oljetemp", text: "Mät oljetemperatur under provkörning", krav: "matvarde" }, + ], + }, + { + id: "provkorning", + rubrik: "Provkörning", + kontroller: [ + { id: "vaxlingspunkter", text: "Dokumentera växlingspunkter och beteende", krav: "kommentar" }, + { id: "belastning", text: "Provkör under belastning där felet uppträder", krav: "kommentar" }, + ], + }, + ], +}; + +export const AVGAS_EMISSION: Metodik = { + id: "avgas_emission", + namn: "Avgassystem och emissioner", + omrade: "Motor", + nyckelord: ["partikelfilt", "dpf", "adblue", "katalysator", "lambda", "avgas", "emission", "rökutveckling", "svart rök", "blå rök", "os", "egr", "regenerer", "utsläpp"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "karaktar", + text: "Hur yttrar sig felet?", + svarstyp: "val", + val: ["Varningslampa", "Rök", "Effektbegränsning", "Lukt", "Underkänd besiktning"], + }, + { id: "rokfarg", text: "Om rök — vilken färg och när?", svarstyp: "text" }, + { id: "korprofil", text: "Beskriv fordonets körprofil (mest kort- eller långkörning)?", svarstyp: "text" }, + ], + }, + { + id: "avlasning", + rubrik: "Avläsning", + kontroller: [ + { id: "dtc", text: "Läs felkoder — fotografera diagnosskärmen", krav: "foto" }, + { id: "sotmangd", text: "Läs beräknad och uppmätt sotmängd i partikelfiltret", krav: "matvarde" }, + { id: "regenerering", text: "Dokumentera antal och utfall av senaste regenereringar", krav: "kommentar" }, + { id: "lambda", text: "Läs lambdagivarnas signaler före och efter katalysator", krav: "matvarde" }, + ], + }, + { + id: "matningar", + rubrik: "Mätningar", + kontroller: [ + { id: "mottryck", text: "Mät avgasmottryck", krav: "matvarde" }, + { id: "differenstryck", text: "Mät differenstryck över partikelfiltret", krav: "matvarde" }, + { id: "adblue_niva", text: "Kontrollera AdBlue-nivå och kvalitet där sådant finns", krav: "matvarde" }, + { id: "egr", text: "Kontrollera EGR-ventilens funktion och läge", krav: "matvarde" }, + ], + }, + { + id: "orsak", + rubrik: "Bakomliggande orsak", + beskrivning: "Ett igensatt filter är oftast en följd, inte orsaken. Dokumentera vad som skapade sotet.", + kontroller: [ + { id: "grundorsak", text: "Kontrollera insprutning, turbo och luftflöde", krav: "kommentar" }, + { id: "korstrackor", text: "Bedöm om körprofilen tillåter regenerering", krav: "kommentar" }, + ], + }, + ], +}; + +export const KLIMAT: Metodik = { + id: "klimat", + namn: "Klimatanläggning", + omrade: "Komfort", + nyckelord: ["ac", "luftkonditionering", "klimatanläggning", "kyler inte", "kompressor", "köldmedi", "imma", "immig", "fläkt", "lukt i kupén", "värmepaket"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "karaktar", + text: "Hur yttrar sig felet?", + svarstyp: "val", + val: ["Kyler inte alls", "Kyler dåligt", "Kyler ibland", "Missljud", "Lukt", "Imma på rutorna"], + }, + { id: "sidor", text: "Skiljer det mellan höger och vänster sida?", svarstyp: "janej" }, + { id: "service", text: "När utfördes AC-service senast?", svarstyp: "text" }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + kontroller: [ + { id: "kondensor", text: "Fotografera kondensorn — kontrollera igensättning och skador", krav: "foto" }, + { id: "kupefilter", text: "Kontrollera kupéfiltret", krav: "foto" }, + { id: "kompressor_rem", text: "Kontrollera kompressorns drivning och magnetkoppling", krav: "kommentar" }, + ], + }, + { + id: "matningar", + rubrik: "Mätningar", + kontroller: [ + { id: "tryck", text: "Mät hög- och lågtryck vid känd omgivningstemperatur", krav: "matvarde" }, + { id: "utblastemp", text: "Mät utblåstemperatur vid mittre munstycke", krav: "matvarde" }, + { id: "mangd", text: "Väg upptagen köldmediemängd vid tömning", krav: "matvarde" }, + { id: "lackagesok", text: "Läcksök med spårgas eller UV", krav: "kommentar" }, + ], + }, + { + id: "styrning", + rubrik: "Styrning", + kontroller: [ + { id: "dtc", text: "Läs felkoder ur klimatstyrenheten", krav: "foto" }, + { id: "spjall", text: "Kontrollera spjällmotorernas rörelse", krav: "kommentar" }, + ], + }, + ], +}; + +export const HOGVOLT: Metodik = { + id: "hogvolt", + namn: "Högvoltsystem — elbil och hybrid", + omrade: "Högvolt", + nyckelord: ["högvolt", "elbil", "hybrid", "traktionsbatteri", "hv", "laddning av bil", "räckvidd", "laddbox", "orange kabel", "sköldpadda"], + steg: [ + { + id: "sakerhet", + rubrik: "Säkerhet — utförs först", + beskrivning: + "Högvoltsystem kan vara livsfarliga. Inget arbete på systemet påbörjas innan stegen nedan är dokumenterade. Kräver behörighet för arbete på högvoltsystem.", + fragor: [ + { id: "behorighet", text: "Har du behörighet för arbete på högvoltsystem?", svarstyp: "janej" }, + { id: "avstangt", text: "Är fordonet spänningslöst enligt tillverkarens rutin?", svarstyp: "janej" }, + ], + kontroller: [ + { id: "service_disconnect", text: "Dokumentera att servicebrytaren är urtagen och säkrad", krav: "foto" }, + { id: "vantetid", text: "Dokumentera väntetid enligt tillverkarens anvisning", krav: "kommentar" }, + { id: "spanningsfrihet", text: "Mät och dokumentera spänningsfrihet före ingrepp", krav: "matvarde" }, + { id: "skyddsutrustning", text: "Dokumentera använd skyddsutrustning och avspärrning", krav: "foto" }, + ], + }, + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "karaktar", + text: "Hur yttrar sig felet?", + svarstyp: "val", + val: ["Laddar inte", "Kortare räckvidd", "Effektbegränsning", "Varningsmeddelande", "Fordonet startar inte"], + }, + { id: "laddtyp", text: "Vid vilken laddning uppträder felet (AC, DC, vilken laddpunkt)?", svarstyp: "text" }, + { id: "temperatur", text: "Vid vilken omgivnings- och batteritemperatur?", svarstyp: "text" }, + ], + }, + { + id: "avlasning", + rubrik: "Avläsning", + beskrivning: "Utförs med diagnosverktyg — inga ingrepp i systemet krävs.", + kontroller: [ + { id: "dtc", text: "Läs felkoder ur samtliga högvoltstyrenheter", krav: "foto" }, + { id: "soh", text: "Läs batteriets hälsa (SOH) och laddnivå (SOC)", krav: "matvarde" }, + { id: "cellspanning", text: "Läs cellspänningar och dokumentera största avvikelse", krav: "matvarde" }, + { id: "isolationsvarde", text: "Läs systemets isolationsvärde", krav: "matvarde" }, + { id: "temperaturer", text: "Läs batteriets temperaturgivare", krav: "matvarde" }, + ], + }, + { + id: "laddning", + rubrik: "Laddkedjan", + kontroller: [ + { id: "laddkabel", text: "Kontrollera laddkabel och laddkontakt — fotografera stiften", krav: "foto" }, + { id: "laddlogg", text: "Läs laddhistorik och avbrottsorsaker", krav: "kommentar" }, + { id: "extern", text: "Testa mot annan laddpunkt för att utesluta yttre orsak", krav: "kommentar" }, + ], + }, + ], +}; + +export const DIAGNOS_NATVERK: Metodik = { + id: "diagnos_natverk", + namn: "Felkoder och kommunikation", + omrade: "Diagnos", + nyckelord: ["felkod", "dtc", "can", "canbuss", "kommunikationsfel", "ingen kontakt", "styrenhet", "obd", "diagnos", "flera lampor"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "omfattning", + text: "Hur många system är berörda?", + svarstyp: "val", + val: ["En styrenhet", "Flera styrenheter", "Hela fordonet", "Endast diagnosverktyget"], + }, + { id: "nar", text: "Uppstod felet efter ingrepp, batteribyte eller uppdatering?", svarstyp: "text" }, + ], + }, + { + id: "grund", + rubrik: "Grundförutsättningar", + beskrivning: "Kommunikationsfel beror oftare på matning och jord än på styrenheten.", + kontroller: [ + { id: "batteri", text: "Mät batterispänning under avläsning", krav: "matvarde" }, + { id: "obd_matning", text: "Mät matning och jord i diagnosuttaget", krav: "matvarde" }, + { id: "sakringar", text: "Kontrollera säkringar till berörda styrenheter", krav: "kommentar" }, + ], + }, + { + id: "buss", + rubrik: "Bussmätning", + kontroller: [ + { id: "terminering", text: "Mät bussens termineringsresistans med systemet spänningslöst", krav: "matvarde" }, + { id: "bussignal", text: "Mät CAN-signalen med oscilloskop — fotografera kurvan", krav: "foto" }, + { id: "avdelning", text: "Dela av bussen för att lokalisera störande nod", krav: "kommentar" }, + ], + }, + { + id: "koder", + rubrik: "Felkoder i sammanhang", + beskrivning: "En felkod pekar på en krets, inte på en trasig komponent. Läs alla system innan någon kod raderas.", + kontroller: [ + { id: "helavlasning", text: "Läs samtliga styrenheter — fotografera listan", krav: "foto" }, + { id: "frysdata", text: "Dokumentera frysramsdata innan radering", krav: "kommentar" }, + { id: "efter_radering", text: "Radera, provkör och dokumentera vilka koder som återkom", krav: "kommentar" }, + ], + }, + ], +}; + +export const LACKAGE: Metodik = { + id: "lackage", + namn: "Läckage", + omrade: "Övrigt", + nyckelord: ["läck", "läckage", "oljefläck", "dropp", "vatten i bilen", "fukt", "blöt matta", "oljeförbrukning"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "vatska", + text: "Vilken vätska misstänks?", + svarstyp: "val", + val: ["Motorolja", "Kylvätska", "Växellådsolja", "Bromsvätska", "Bränsle", "Vatten/kondens", "Okänt"], + }, + { id: "mangd", text: "Hur mycket och hur ofta?", svarstyp: "text" }, + { id: "plats", text: "Var på marken syns fläcken i förhållande till fordonet?", svarstyp: "text" }, + ], + }, + { + id: "visuell", + rubrik: "Lokalisering", + beskrivning: "Rengör först. Ett gammalt läckage döljer var det nya kommer ifrån.", + kontroller: [ + { id: "foto_fore", text: "Fotografera området före rengöring", krav: "foto" }, + { id: "rengoring", text: "Rengör området och dokumentera att det är torrt", krav: "foto" }, + { id: "provkorning", text: "Provkör eller låt gå och fotografera var vätskan uppträder", krav: "foto" }, + ], + }, + { + id: "metod", + rubrik: "Läcksökning", + kontroller: [ + { id: "uv", text: "Läcksök med UV-spårämne där det är tillämpligt", krav: "kommentar" }, + { id: "trycktest", text: "Trycktesta systemet", krav: "matvarde" }, + { id: "niva", text: "Mät nivåförändring över tid", krav: "matvarde" }, + ], + }, + ], +}; + +export const MISSLJUD: Metodik = { + id: "missljud", + namn: "Missljud", + omrade: "Övrigt", + nyckelord: ["missljud", "ljud", "knack", "gnissel", "gnissl", "vinande", "brumm", "skram", "väsande", "tjut"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + beskrivning: "Ett missljud som inte reproducerats går inte att åtgärda. Spela in det.", + fragor: [ + { + id: "karaktar", + text: "Vilken karaktär har ljudet?", + svarstyp: "val", + val: ["Knack", "Gnissel", "Vinande", "Brummande", "Skrammel", "Väsande", "Tjut"], + }, + { + id: "nar", + text: "När hörs det?", + svarstyp: "val", + val: ["Vid start", "Vid tomgång", "Under körning", "Vid inbromsning", "Vid kurvtagning", "Över ojämnheter"], + }, + { id: "beroende", text: "Följer ljudet varvtal eller hastighet?", svarstyp: "val", val: ["Varvtal", "Hastighet", "Ingetdera"] }, + { id: "temperatur", text: "Skillnad kallt mot varmt?", svarstyp: "text" }, + ], + }, + { + id: "inspelning", + rubrik: "Dokumentera ljudet", + beskrivning: "Video med ljud är evidens. En beskrivning i text är det inte.", + kontroller: [ + { id: "video", text: "Spela in ljudet med video under de förhållanden där det uppträder", krav: "foto" }, + { id: "forhallanden", text: "Dokumentera hastighet, varvtal och underlag vid inspelningen", krav: "kommentar" }, + ], + }, + { + id: "lokalisering", + rubrik: "Lokalisering", + kontroller: [ + { id: "stetoskop", text: "Lokalisera med stetoskop eller ljudsond", krav: "kommentar" }, + { id: "avlastning", text: "Kontrollera om ljudet ändras vid avlastning eller belastning", krav: "kommentar" }, + { id: "uteslutning", text: "Dokumentera vilka källor som uteslutits och hur", krav: "kommentar" }, + ], + }, + ], +}; + +export const ADAS: Metodik = { + id: "adas", + namn: "Förarassistans och kalibrering", + omrade: "Diagnos", + nyckelord: ["adas", "kamera", "radar", "filhållning", "adaptiv farthållare", "kalibrer", "vindruta", "parkeringssensor", "döda vinkeln", "nödbroms"], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { + id: "funktion", + text: "Vilken funktion felar?", + svarstyp: "val", + val: ["Filhållning", "Adaptiv farthållare", "Automatisk nödbroms", "Döda vinkeln", "Parkeringshjälp", "Backkamera"], + }, + { id: "ingrepp", text: "Har vindruta, kaross eller fjädring åtgärdats nyligen?", svarstyp: "text" }, + { id: "forhallanden", text: "Uppträder felet i visst väder eller ljus?", svarstyp: "text" }, + ], + }, + { + id: "forutsattningar", + rubrik: "Förutsättningar för kalibrering", + beskrivning: + "En kalibrering på fel förutsättningar ger ett fel som ser åtgärdat ut. Dokumentera att kraven är uppfyllda innan den utförs.", + kontroller: [ + { id: "dtc", text: "Läs felkoder ur berörda styrenheter", krav: "foto" }, + { id: "hjulinstallning", text: "Kontrollera hjulinställning — bakaxelns spårning påverkar kameran", krav: "matvarde" }, + { id: "dacktryck", text: "Kontrollera däcktryck och körhöjd", krav: "matvarde" }, + { id: "sikt", text: "Kontrollera kamerans och radarns siktfält för smuts eller skada", krav: "foto" }, + { id: "yta", text: "Dokumentera att kalibreringsytan uppfyller kraven på planhet och ljus", krav: "foto" }, + ], + }, + { + id: "kalibrering", + rubrik: "Kalibrering", + kontroller: [ + { id: "utford", text: "Utför kalibrering enligt tillverkarens metod — fotografera resultatet", krav: "foto" }, + { id: "provkorning", text: "Provkör och verifiera funktionen under de förhållanden felet uppträdde", krav: "kommentar" }, + ], + }, + ], +}; + +export const GENERISK: Metodik = { + id: "generisk", + namn: "Generell strukturerad felsökning", + omrade: "Övrigt", + nyckelord: [], + steg: [ + { + id: "symptom", + rubrik: "Verifiera symptom", + fragor: [ + { id: "reproducerbart", text: "Går felet att reproducera?", svarstyp: "janej" }, + { + id: "nar", + text: "När uppträder felet?", + svarstyp: "val", + val: ["Alltid", "Ibland", "Endast under drift", "Endast vid start"], + }, + { + id: "var", + text: "Var upplevs felet?", + svarstyp: "val", + val: ["Fram", "Bak", "Höger", "Vänster", "Motor/drivkälla", "Kupé/förarplats", "Okänt"], + }, + { + id: "hur", + text: "Hur upplevs felet?", + svarstyp: "val", + val: ["Skakar/vibrerar", "Missljud (knack/gnissel/vinande)", "Rycker", "Tappar kraft", "Stannar helt", "Annat"], + }, + { id: "forlopp", text: "Beskriv förloppet när felet uppstår", svarstyp: "text" }, + { id: "forandring", text: "Har något förändrats nyligen (service, reparation, miljö)?", svarstyp: "text" }, + ], + }, + { + id: "visuell", + rubrik: "Visuell kontroll", + kontroller: [ + { id: "foto_objekt", text: "Fotografera objektet och det berörda området", krav: "foto" }, + { id: "skador", text: "Kontrollera synliga skador, läckage eller lösa anslutningar", krav: "kommentar" }, + { id: "typskylt", text: "Dokumentera typskylt/märkning", krav: "foto" }, + ], + }, + { + id: "grundkontroller", + rubrik: "Grundkontroller", + kontroller: [ + { + id: "matning", + text: "Utför grundläggande mätningar (spänning, tryck, temperatur — det som är relevant)", + krav: "matvarde", + }, + { id: "sakringar", text: "Kontrollera säkringar/skydd", krav: "kommentar" }, + { id: "anslutningar", text: "Kontrollera kontaktdon och anslutningar", krav: "kommentar" }, + ], + }, + { + id: "funktionstest", + rubrik: "Funktionstest", + kontroller: [{ id: "test", text: "Genomför funktionstest och dokumentera resultatet", krav: "kommentar" }], + }, + ], +}; + +// Ordningen styr bara hur de visas. Valet görs på nyckelord, inte på +// position — GENERISK ligger sist eftersom den är fallback. +export const ALLA_METODIKER: Metodik[] = [ + VIBRATION, + BROMSAR, + STYRNING_FJADRING, + ELSYSTEM, + START_LADDNING, + MOTOR_DRIFT, + KYLSYSTEM, + DRIVLINA, + AVGAS_EMISSION, + KLIMAT, + HOGVOLT, + DIAGNOS_NATVERK, + LACKAGE, + MISSLJUD, + ADAS, + GENERISK, +]; diff --git a/felsokning/docs/DEMO.md b/felsokning/docs/DEMO.md index c2a580a..d28dfb8 100644 --- a/felsokning/docs/DEMO.md +++ b/felsokning/docs/DEMO.md @@ -64,7 +64,7 @@ Det ersätter inte teknikern. Det ersätter pärmen, minneslapparna och "fråga | I demon | I produktion | | --- | --- | -| Deterministisk metodikmotor (3 metodiker) | LLM väljer/genererar steg genom samma motorgränssnitt | +| Deterministisk metodikmotor (16 metodiker) | LLM väljer/genererar steg genom samma motorgränssnitt | | Webbläsarens taligenkänning | Leverantörens Voice-to-Text bakom samma gränssnitt | | localStorage + synk vid inloggning | Multi-tenant-backend (migration finns), roller enligt Master Prompt | | Delningslänk kräver synkat ärende | Live Share med behörighetsnivåer kund/intern/partner | diff --git a/felsokning/docs/MVP.md b/felsokning/docs/MVP.md index 4716bd0..2a0cb3a 100644 --- a/felsokning/docs/MVP.md +++ b/felsokning/docs/MVP.md @@ -28,7 +28,7 @@ Demomanus för visning: [DEMO.md](DEMO.md). Knappen **Skapa demoärende** på st | Direktivets kärna | Status i MVP | | --- | --- | | Objektidentifiering först | ✅ **QR-/streckkodsläsning** (`src/felsokning/streckkod.ts`): kameraströmmen läser QR, Code 39/128, Data Matrix och PDF417 via webbläsarens BarcodeDetector. Avläst kod klassificeras innan den används — VIN (17 tecken utan I/O/Q), svenskt regnr (båda serierna) eller serienummer — och identifieraren plockas ut även ur QR-innehåll som URL:er eller `vin=…`-fält; fritext och nakna URL:er avvisas. Saknar webbläsaren API:t (t.ex. iOS/Safari) **fotograferas typskylten** i stället och plattformens bildtolkning läser av den — kameran är gränssnittet oavsett enhet. Manuell inmatning med bekräftelsesteg finns kvar. | -| AI-guidad felsökning | ✅ Deterministisk metodikmotor (en fråga i taget, tre metodiker) **plus Claude-orkestern driven av plattformen**: edge-funktionen `felsokning-ai` äger Claude API-nyckeln (serverhemligheten `ANTHROPIC_API_KEY`) och routar per uppgift — handledning i realtid (Sonnet 5), djupgranskning av hela underlaget via knapp i briefen (Opus 5, hög effort), AI-komplettering av överlämningen med risker & osäkerheter (Sonnet 5) och metodikklassificering av felbeskrivningen (Haiku 4.5). Alla svar är schema-bundna och klassificerade enligt AI-reglerna, med automatisk fallback till Anthropics rekommenderade reservmodell vid avböjd förfrågan; modellen som svarade loggas i varje händelse. Kräver inloggad användare; svaren är interna och delas aldrig i kundvyer. I lokalt läge guidar metodiken ensam. | +| AI-guidad felsökning | ✅ Deterministisk metodikmotor (en fråga i taget, sexton metodiker) **plus Claude-orkestern driven av plattformen**: edge-funktionen `felsokning-ai` äger Claude API-nyckeln (serverhemligheten `ANTHROPIC_API_KEY`) och routar per uppgift — handledning i realtid (Sonnet 5), djupgranskning av hela underlaget via knapp i briefen (Opus 5, hög effort), AI-komplettering av överlämningen med risker & osäkerheter (Sonnet 5) och metodikklassificering av felbeskrivningen (Haiku 4.5). Alla svar är schema-bundna och klassificerade enligt AI-reglerna, med automatisk fallback till Anthropics rekommenderade reservmodell vid avböjd förfrågan; modellen som svarade loggas i varje händelse. Kräver inloggad användare; svaren är interna och delas aldrig i kundvyer. I lokalt läge guidar metodiken ensam. | | Arbetslogg | ✅ Append-only händelselogg med tidsstämpel och användare på varje post. Ingenting skrivs över. | | Tidredovisning | ✅ Kategorier (aktiv felsökning, väntetid, provkörning …) via kategoribyten i loggen; paus räknas inte i total tid. Inaktivitetsfråga efter 20 min utan händelser. | | Dokumentation | ✅ Observationer, mätvärden, foton (nedskalade), **video med ljud** (E3-evidens för det som låter eller rör sig — kort klipp med obligatorisk beskrivning, hård storleksgräns, originalfilen bevaras och visas i logg, rapport och Live Share), kommentarer och hypoteser. Hypoteser märks alltid som ej verifierade och kan aldrig loggas som konstaterade fel. **Avslutet signeras automatiskt** av teknikern (”Felsökning avslutad — signerad av …”), redovisat i kvalitetsgrinden. | @@ -40,7 +40,7 @@ Demomanus för visning: [DEMO.md](DEMO.md). Knappen **Skapa demoärende** på st | Export | ✅ Versionsmärkt JSON-export (version = antal händelser vid exporttillfället, med användare och tidpunkt); exporten loggas själv som händelse. PDF via utskrift. CSV och API i backend-fasen. | | Multi-tenant & roller | ✅ I självhostat läge: registrering skapar organisation + systemadministratör; admin hanterar användare (tekniker/arbetsledare/admin) via UI; all ärendedata organisationsisolerad i API:t; roll + organisation i JWT:n. **Arbetsledarvy** (`/felsokning/oversikt`): organisationens alla ärenden med status, deltagande tekniker och statistik (pågående/avslutade/ledtid) — härlett ur händelseloggen; ärenden kan hämtas till enheten med konfliktfri flätning. **Felorsaksstatistik** (flottdata): orsakskategorierna ur alla felorsaksanalyser aggregeras per organisation och visas som stapelöversikt i arbetsledarvyn. **Ansvarig tekniker** per ärende härleds ur loggen (skapare → överlämning → omfördelning) och arbetsledaren kan omfördela pågående ärenden — loggat som den organisationsinterna händelsen `ansvarig_satt`, aldrig synlig i kund-/partnerdelningar. Integrationstestat mot riktig Postgres (isolering, rollstyrning, append-only, översiktens behörighet och härledningar). | | Backend & synk | ✅ Databas-migration (`supabase/migrations/20260802230000_guidad_felsokning.sql`): ärenden + händelser med RLS, append-only även i databasen (inga update/delete-rättigheter). Synklager i klienten: konfliktfri ihopflätning av händelser per id (testad), push av lokala + pull av kollegors händelser var 15:e sekund. Utan inloggning arbetar appen i lokalt läge; status visas i ärendehuvudet. | -| Metodiker | ✅ Tre: vibration, elsystem/strömförsörjning (relä-exemplet ur visionen) och generisk — vald automatiskt utifrån felbeskrivningen. | +| Metodiker | ✅ **Sexton**, i `src/felsokning/metodiker.ts`: vibration, bromsar, styrning/fjädring, elsystem (relä-exemplet ur visionen), start & laddning, motorgång, kylsystem, drivlina, avgas & emission, klimat, högvolt (elbil/hybrid), felkoder & kommunikation, läckage, missljud, förarassistans (ADAS) — plus generisk. Metodiken väljs på nyckelord ur felbeskrivningen, poängsatt så att det mest specifika ordet vinner (`traktionsbatteri` slår `batteri`); träffar inget väljs generisk, som är strukturellt komplett — ett ärligt "vi vet inte var vi ska börja" i stället för en gissning, varefter orkesterns klassificerare (Haiku 4.5) får försöka. Tre regler är låsta av test: varje kontroll har ett minimikrav, varje metodik börjar med att verifiera symptomet, och där arbetet kan skada någon ligger säkerhetssteget först — högvoltsmetodiken kan inte påbörjas utan dokumenterad spänningsfrihet, urtagen servicebrytare och skyddsutrustning. | | Live Share | ✅ **Delningsgränsen är en tillåtelselista**: händelsetyper räknas upp per nivå (kund/partner/intern) i stället för att nekas en och en, så en ny händelsetyp är intern tills någon aktivt släpper fram den — låst av ett test som kräver att varje typ i domänmodellen är klassificerad. Skrivskyddad livevy per ärende (`/felsokning/dela/:id`): status ✔/🔄/⏳, bilder, mätvärdestabell, tidslinje, rekommenderat nästa steg. Uppdateras automatiskt, interna poster filtreras bort. Publik delningssida (`/felsokning/delad/:kod`) läser via `hamta_delat_arende` utan inloggning och pollar för liveuppdatering; "Kopiera delningslänk" finns i rapportfliken. **Behörighetsnivåer**: återkallbara delningslänkar per nivå — kund (det kunddelbara), extern partner (även hypoteser, märkta ej verifierade), intern (full insyn) — med serverstyrd filtrering, hanterade från rapportfliken i självhostat läge. | | Dashboard | ✅ Enligt direktivet: räknare och filter för Alla/Pågående/Klara plus Starta nytt ärende. | | Ärendestart via arbetsorder | ✅ Primärvägen när ett ärende startas: fota arbetsorderns framsida — orkesterns dokumenttolkning (Claude Sonnet 5, vision) läser kund-, fordons- och verkstadsuppgifter oavsett layout och sätter konfidens per fält. 🟢 ≥95 % godkänns automatiskt, 🟡 80–95 % markeras för genomläsning, 🔴 <80 % kräver aktiv bekräftelse — teknikern granskar bara osäkra fält. Visuell granskning med dokumentet bredvid fälten (klick markerar ungefärlig position), sedan skapas hela ärendet med ett tryck. Tolkningen loggas som organisationsintern händelse (`arbetsorder_skannad`) och delas aldrig i kund-/partnervyer. Manuell inmatning finns kvar som andrahandsväg; i lokalt läge visas en tydligt märkt demo-tolkning. Inloggade användare tillfrågas aldrig om namn — kontot vet redan. | @@ -66,7 +66,7 @@ Demomanus för visning: [DEMO.md](DEMO.md). Knappen **Skapa demoärende** på st - **Händelseloggen är enda sanningskällan.** `src/felsokning/domain.ts` definierar händelsetyperna; poster läggs endast till. - **Alla vyer är projektioner.** `src/felsokning/projektioner.ts` — brief, tidsfördelning, överlämningstext och kundrapport är rena funktioner av loggen och kan alltid regenereras. Testerna i `src/felsokning/__tests__/` låser detta. -- **Metodikmotorn är deterministisk.** `src/felsokning/metodik.ts` — nästa steg härleds ur vad som redan dokumenterats. Det är här den framtida AI:n ansluter, utan att logg eller projektioner ändras. +- **Metodikmotorn är deterministisk.** `src/felsokning/metodik.ts` är motorn (typer, val av metodik, härledning av nästa steg); `src/felsokning/metodiker.ts` är innehållet. Nästa steg härleds ur vad som redan dokumenterats. Biblioteket kan växa utan att motorn ändras, och orkesterns metodikkatalog jämförs mot klientens i test så att listorna inte kan glida isär. - **Ingen slutsats utan evidens.** `src/felsokning/ecm.ts` — regelmotorn (ECM) validerar varje påstående mot händelseloggen: fullbordansregler, evidensnivåer och kvalitetsgrind. Kameran är integrationslagret (visual-first) — det som syns på en skärm eller ett instrument fotograferas och tolkas i stället för att integreras. - **Terminologi.** Produkten beskrivs som ett evidensbaserat diagnossystem/intelligent beslutsstöd — i UI och kundkommunikation används *systemet/analysen/bedömningen/beslutsstödet*, aldrig "AI" om det inte är tekniskt nödvändigt. - **Egen ikongrafik.** `src/felsokning/ikoner.tsx` — enkla industriella linjeikoner (SVG, stroke i aktuell textfärg) i stället för emojis; tillförlitlighets- och statusnivåer visas som färgpunkter. diff --git a/felsokning/services/ai-orkester/server.mjs b/felsokning/services/ai-orkester/server.mjs index 9281a40..4fcc398 100644 --- a/felsokning/services/ai-orkester/server.mjs +++ b/felsokning/services/ai-orkester/server.mjs @@ -58,10 +58,33 @@ const SVARS_SCHEMA = { additionalProperties: false, }; +// Metodikkatalogen. Måste vara identisk med app/src/felsokning/metodiker.ts — +// testet "orkestern känner till exakt samma metodiker som klienten" faller +// om listorna glider isär. Beskrivningen är klassificerarens enda underlag, +// så den ska säga vad symptomet är, inte vad felet tros vara. +const METODIK_KATALOG = [ + ["vibration", "vibrationer, skakningar eller obalans under körning"], + ["bromsar", "bromsverkan, bromspedal, skevhet, gnissel vid inbromsning, ABS"], + ["styrning_fjadring", "styrning, ratt, glapp, stötdämpare, hjulinställning, fordonet drar åt sidan"], + ["elsystem", "elektriska fel — reläer, säkringar, spänning, belysning, kablage, kontaktdon"], + ["start_laddning", "fordonet startar inte, startmotor, generator, batteriet laddar ur"], + ["motor_drift", "motorgång och effekt — rycker, misständer, ojämn tomgång, tappar kraft"], + ["kylsystem", "överhettning, kylvätska, termostat, kylare, värme i kupén"], + ["drivlina", "växellåda, koppling, automatlåda, drivknutar, kardan, differential"], + ["avgas_emission", "avgassystem och emissioner — partikelfilter, AdBlue, katalysator, lambda, EGR"], + ["klimat", "klimatanläggning — kyler inte, kompressor, köldmedium, imma, lukt i kupén"], + ["hogvolt", "högvoltsystem i elbil eller hybrid — traktionsbatteri, laddning, räckvidd"], + ["diagnos_natverk", "felkoder, kommunikationsfel, ingen kontakt med styrenhet, CAN/buss"], + ["lackage", "läckage — olja, vätska, vatten som tar sig in i fordonet"], + ["missljud", "missljud — knack, gnissel, vinande, brummande, skrammel"], + ["adas", "förarassistans och kalibrering — kamera, radar, filhållning, parkeringssensorer"], + ["generisk", "allt annat, eller när det är oklart"], +]; + const METODIK_SCHEMA = { type: "object", properties: { - metodikId: { type: "string", enum: ["vibration", "elsystem", "generisk"] }, + metodikId: { type: "string", enum: METODIK_KATALOG.map(([id]) => id) }, }, required: ["metodikId"], additionalProperties: false, @@ -180,10 +203,10 @@ const ORKESTER = { modell: "claude-haiku-4-5", maxTokens: 256, system: `Du klassificerar en felbeskrivning från en verkstad till EN felsökningsmetodik: -- "vibration": vibrationer, skakningar eller obalans under körning. -- "elsystem": elektriska fel — reläer, säkringar, spänning, batteri, belysning, givare, strömförsörjning. -- "generisk": allt annat, eller när det är oklart. -Gissa inte: välj "generisk" om beskrivningen inte tydligt hör till en specifik metodik.`, +${METODIK_KATALOG.map(([id, beskrivning]) => `- "${id}": ${beskrivning}.`).join("\n")} + +Valet är en frågeordning, inte en diagnos — du avgör var teknikern ska börja leta, inte vad som är fel. +Gissa inte: välj "generisk" om beskrivningen inte tydligt hör till en specifik metodik. "generisk" är ett fullgott svar och alltid bättre än en gissning.`, schema: METODIK_SCHEMA, }, // Ärendestart: teknikern fotograferar arbetsordern; vi läser dokumentet diff --git a/felsokning/supabase/functions/felsokning-ai/index.ts b/felsokning/supabase/functions/felsokning-ai/index.ts index cfc28af..e5826b2 100644 --- a/felsokning/supabase/functions/felsokning-ai/index.ts +++ b/felsokning/supabase/functions/felsokning-ai/index.ts @@ -56,10 +56,33 @@ const SVARS_SCHEMA = { additionalProperties: false, }; +// Metodikkatalogen. Måste vara identisk med app/src/felsokning/metodiker.ts — +// testet "orkestern känner till exakt samma metodiker som klienten" faller +// om listorna glider isär. Beskrivningen är klassificerarens enda underlag, +// så den ska säga vad symptomet är, inte vad felet tros vara. +const METODIK_KATALOG = [ + ["vibration", "vibrationer, skakningar eller obalans under körning"], + ["bromsar", "bromsverkan, bromspedal, skevhet, gnissel vid inbromsning, ABS"], + ["styrning_fjadring", "styrning, ratt, glapp, stötdämpare, hjulinställning, fordonet drar åt sidan"], + ["elsystem", "elektriska fel — reläer, säkringar, spänning, belysning, kablage, kontaktdon"], + ["start_laddning", "fordonet startar inte, startmotor, generator, batteriet laddar ur"], + ["motor_drift", "motorgång och effekt — rycker, misständer, ojämn tomgång, tappar kraft"], + ["kylsystem", "överhettning, kylvätska, termostat, kylare, värme i kupén"], + ["drivlina", "växellåda, koppling, automatlåda, drivknutar, kardan, differential"], + ["avgas_emission", "avgassystem och emissioner — partikelfilter, AdBlue, katalysator, lambda, EGR"], + ["klimat", "klimatanläggning — kyler inte, kompressor, köldmedium, imma, lukt i kupén"], + ["hogvolt", "högvoltsystem i elbil eller hybrid — traktionsbatteri, laddning, räckvidd"], + ["diagnos_natverk", "felkoder, kommunikationsfel, ingen kontakt med styrenhet, CAN/buss"], + ["lackage", "läckage — olja, vätska, vatten som tar sig in i fordonet"], + ["missljud", "missljud — knack, gnissel, vinande, brummande, skrammel"], + ["adas", "förarassistans och kalibrering — kamera, radar, filhållning, parkeringssensorer"], + ["generisk", "allt annat, eller när det är oklart"], +]; + const METODIK_SCHEMA = { type: "object", properties: { - metodikId: { type: "string", enum: ["vibration", "elsystem", "generisk"] }, + metodikId: { type: "string", enum: METODIK_KATALOG.map(([id]) => id) }, }, required: ["metodikId"], additionalProperties: false, @@ -203,10 +226,10 @@ Uppgift: En tekniker lämnar över ärendet. Komplettera överlämningen med det modell: "claude-haiku-4-5", maxTokens: 256, system: `Du klassificerar en felbeskrivning från en verkstad till EN felsökningsmetodik: -- "vibration": vibrationer, skakningar eller obalans under körning. -- "elsystem": elektriska fel — reläer, säkringar, spänning, batteri, belysning, givare, strömförsörjning. -- "generisk": allt annat, eller när det är oklart. -Gissa inte: välj "generisk" om beskrivningen inte tydligt hör till en specifik metodik.`, +${METODIK_KATALOG.map(([id, beskrivning]) => `- "${id}": ${beskrivning}.`).join("\n")} + +Valet är en frågeordning, inte en diagnos — du avgör var teknikern ska börja leta, inte vad som är fel. +Gissa inte: välj "generisk" om beskrivningen inte tydligt hör till en specifik metodik. "generisk" är ett fullgott svar och alltid bättre än en gissning.`, schema: METODIK_SCHEMA, }, // Ärendestart: teknikern fotograferar arbetsordern; vi läser dokumentet