Så klassificerar Klassio
Vad som händer mellan att du trycker på Sök och att Klassio visar ett KN-nummer. Varje steg är dokumenterat och loggat — så att du, en revisor eller en tulltjänsteman kan följa kedjan från fråga till svar.
- Granskningsbar end-to-end
- CN 2026 · CELEX 02025R1926
- GDPR · självhostad embedding
Flödet, från fråga till svar
Sju steg från sökning till svar. Steg fyra är villkorligt — det körs bara när frågans huvudord pekar på ett specifikt kapitel.
-
Sökning
Du kan skriva en produktbeskrivning på svenska eller engelska ("ekstol med läderklädsel"), eller ange ett KN-nummer direkt ("94016100"). Klassio hanterar båda — fri text går genom hela pipelinen, KN-nummer går direkt till uppslag. För fri text normaliserar Klassio först din beskrivning till dess tullmässiga "natur" (huvudord + material + funktion, varumärkes- och marknadsföringsord tas bort) för att driva sökningen — domaren ser fortfarande din ursprungliga formulering.
-
Inbäddning
Frågan kodas till en 1024-dimensionell vektor av en modell som tränats parallellt på svenska och engelska. Det betyder att "ekstol" och "oak chair" mappar till liknande vektorer — semantisk likhet, inte bara ordmatchning.
-
Hybridsökning
Två sökmotorer körs parallellt. Vektorsökning hittar semantiska träffar via cosinusavstånd i pgvector; BM25 (Postgres FTS) hittar lexikala token-matchningar. Resultaten slås samman med RRF — robust mot olika ranking-skalor och fångar det enskild sökmotor missar.
-
Kapitelförankring
VillkorligtEn liten lexikon av huvudord ("stol" → 94 möbler, "bönor" → 09/12, "stövel" → 64, "låda" → 94) ger en kapitelhint. När hinten finns hämtar vi topp-10 inom det kapitlet via vektorsökning och prependerar dem i listan — så domaren alltid ser rätt kapitel även när råembeddingen drar fel.
-
GRI-domare
Den omrankade kortlistan (en cross-encoder trimmar de topp-50 hämtade kandidaterna till ~30) går till en LLM-domare tillsammans med kapitelbeskrivningar, klassificeringsregler, residual-flagga och EUDR-omfång per kandidat. Modellen tillämpar EUs allmänna tolkningsregler (GRI) i ordning och returnerar en strukturerad dom: vinnare + tvåa + tillämpade regler + bilingual motivering. När beskrivningen signalerar en ofullständig vara (t.ex. "säljs separat", "ingår inte") injiceras även parts-undernummer (xx 91/xx 99) i kandidatlistan så att GRI 2(a) kan tillämpas korrekt — ett delkomponent-kit hamnar då på rätt parts-undernummer i stället för den kompletta varans rubrik.
-
Kalibrering
Statusen avgörs av sju regler — finns en rest-rubrik på topp-1, ligger domarens kapitel utanför topp-2, ligger topp-1 och topp-2 i olika kapitel, har domaren fallerat, är distansmarginalen mellan topp-1 och topp-2 under 5 pp, har domaren tillämpat GRI 2(a) (ofullständig vara — alltid Granska rekommenderas), och (tillagd 2026-05-19) när domaren inte kunde slutföras OCH embedding-fallbackens topp-1 hamnar utanför den lösta kapitelhinten — då nedgraderas statusen till Manuell granskning i stället för att presenteras som ett konfident svar. Klassio är konservativ som standard — endast när alla grindar är klara höjs statusen till Konfident.
-
Svar
Varje svar bär med sig den GRI-regel som tillämpades, en kort motivering på både svenska och engelska, kapitelbeskrivningen, EUDR-omfång (in/out + is_ex) och MFN-tullsatsen från Tulltaxan. Resultatet är granskningsbart utan att klicka vidare.
De sju stegen landar på ett 8-siffrigt KN-nummer — de allmänna tolkningsreglerna styr till KN-8. Den 10:e siffran är TARIC:s åtgärdsnivå, inte en tolkningsfråga, så den avgörs separat: finns bara en deklarerbar underkod väljs den automatiskt; en objektiv produktegenskap (spänning, alkoholhalt, vikt/m²…) ger ett begränsat val som flaggas för granskning; en fråga om användning, ursprung eller tillverkare besvaras inte med en gissning — vi frågar i stället.
Tullsatsen (MFN, erga omnes) är arvssäker: saknar 10-sifferkoden en egen sats ärver den närmaste föräldernivåns — t.ex. en bärbar dator (8471300000) ärver 0 % från rubriken 8471. Är satsen okänd visas aldrig 0 %, utan ”Tullsats okänd — verifiera i Taric”. Den 10:e siffran visas som en mjuk granskningssignal och höjer aldrig KN-nivåns konfidens.
En LLM ensam hallucinerar KN-nummer — den hittar på koder som inte finns. En ren embedding ger fel kapitel när material drar starkare än produkten ("ekstol med läderklädsel" → läder, inte stol). Vi kombinerar embedding för bredd, BM25 för precision, och låter domaren välja inom en kortlista som vi själva har förankrat i CN-nomenklaturen. Domaren kan inte hitta på — den måste välja från listan, och valet valideras strikt mot kortlistan innan det visas.
Varje sökning skriver en rad i judge_decisions: vad embeddingen valde, vad domaren valde, vilken GRI-regel som tillämpades, en bilingual motivering, och svarstid. Den loggen är grunden för att förbättra Klassio över tid — synonymförslag genereras automatiskt från motiveringarna, hård-fall (där embedding och domare är oense) lyfts till regressionstester, och med tillräckligt många validerade fall kan vi finjustera embeddingen själva.
Den hashade frågetexten (sha256, inte personuppgift) och en normaliserad form loggas alltid — för drift, säkerhet och produktförbättring. I driftsloggen behålls den okrypterade frågetexten i högst 30 dagar (säkerhetsbuffert mot missbruk) och raderas sedan. Frågor används också för att förbättra Klassio — anonyma besökare och Free-konton som standard, betalplaner via opt-in — och sådana frågor (inklusive frågetexten) bevaras som träningsunderlag. Stäng av en enskild sökning direkt när du söker, eller hantera delning i Inställningar → Integritet. Inloggades data omfattas av rätten att glömmas (GDPR Art. 17), som kaskaderas till judge_decisions.
Tekniska detaljer Modellnamn, parametrar och bibliotek per steg — för utvecklare och revisorer som vill djupdyka.
- 01 · Sökning
- /api/mcp via STDIO eller HTTP. Indata: free text (≤4000 tecken) eller KN-nummer (2/4/6/8/10 siffror). Fri text normaliseras för sökning (normalize-query.ts, nq-v2); domaren ser originalet.
- 02 · Inbäddning
- Modell: multilingual-e5-large-instruct (1024 dim). Sidecar i Coolify, ingen extern API-anrop.
- 03 · Hybridsökning
- pgvector cosinusavstånd + Postgres FTS (websearch_to_tsquery, swedish + english). RRF k=60. Topp-50 → cross-encoder-omrankning → topp-30 till domaren.
- 04 · Kapitelförankring
- src/lib/server/head-noun-lexicon.ts — ~190 stems + ~30 multi-word phrases. Vid match: topp-10 in-chapter via vektorsökning, dedup mot huvudlistan.
- 05 · GRI-domare
- claude-haiku-4-5-20251001 med tool_use forced output (submit_verdict). Schema: VerdictSchema (winner + runner_up + gri_applied + bilingual rationale). 10s timeout, 2048 max output tokens, 24h judge_cache.
- 06 · Kalibrering
- src/lib/server/calibrate.ts — sex regler. Cosinusavståndströskel 5 pp för "konfident". GRI 2(a) → alltid review_recommended.
- 07 · Svar
- Output: 3 candidates med eudr, duty, classification_rules, judge_winner, gri_applied, bilingual rationale, judge_debug. Loggas till judge_decisions för audit.