Tillbaka till bloggen
Bygga23 april 20268 min läsning

RAG är inte magi. Här är var det går sönder

Ett tänkt scenario: en organisation har byggt sitt eget RAG-system på tre veckor. Vektordatabas, embeddingmodell, proffsigt chattgränssnitt. Allt fungerar på demofrågorna. Sedan ställer någon en specifik fråga och systemet svarar med rätt fakta om fel sak.

Kapaciti

Tänk ett scenario som upprepas i marknaden just nu. En organisation har byggt sitt eget RAG-system på tre veckor. De har en vektordatabas, en embeddingmodell, och ett chattgränssnitt som ser proffsigt ut. Allt fungerar utmärkt på demofrågorna. Sedan ställer en säljare en fråga om garantivillkor för en specifik produktserie, och systemet svarar med information från en helt annan produktkategori. Inte fel i den meningen att det hittade på fakta, utan fel i den meningen att det hittade rätt fakta om fel sak. Det är den typen av fel som är farligast, för ingen reagerar på ett svar som låter rimligt.

RAG har blivit standardarkitekturen för företag som vill att en språkmodell ska kunna svara på frågor om intern dokumentation. Konceptet är elegant: istället för att stoppa in alla dokument i modellens kontext söker du fram de mest relevanta textbitarna och skickar bara dem. Men elegansen i konceptet döljer att varje steg i kedjan kan gå sönder på sätt som inte syns förrän systemet är i produktion. Mönstret är tydligt nog att man kan peka ut var det brukar haverera.

Chunking är inte ett löst problem

Det första steget i en RAG-pipeline är att dela upp dokument i mindre bitar, så kallade chunks, som sedan indexeras. Det låter trivialt. Det är det inte. Den vanligaste metoden är att dela efter ett fast antal tokens, ibland med lite överlapp mellan bitarna. Det fungerar hyfsat på löpande text, men de flesta företagsdokument är inte löpande text. De är avtal med numrerade paragrafer som refererar till varandra. De är tekniska manualer där en tabell på sida fyra förklarar en procedur på sida tre. De är policydokument där ett undantag i slutet av ett stycke ändrar innebörden av allt som stod innan.

Ett illustrativt scenario: ett bolag indexerar sina ramavtal med chunkstorlek satt till 512 tokens, vilket är en vanlig default. Problemet uppstår när en klausul om ansvarsbegränsning börjar i slutet av en chunk och fortsätter i nästa. När systemet hämtar den första chunken får användaren information om att ansvarsbegränsningen gäller, men missar undantaget som står i nästa bit. Svaret blir tekniskt korrekt baserat på den hämtade texten, men juridiskt vilseledande. Den typen av fel upptäcks inte av automatiska utvärderingar som bara kontrollerar om svaret innehåller rätt nyckelord.

Lösningen är sällan att bara öka chunkstorleken. Större chunks ger mer kontext men försämrar sökprecisionen, för nu tävlar fler orelaterade meningar om att representera samma vektor. Det jag har landat i är en kombination av semantisk chunking, där man delar vid naturliga ämnesbyten istället för vid en fast tokengräns, och att behålla metadata om dokumentstruktur. Om systemet vet att chunk 14 och 15 tillhör samma paragraf kan det hämta båda när den ena matchar. Det kräver mer arbete vid indexering, men det sparar tid i felsökning senare.

Svenska facktermer försvinner i embeddingmodellen

De flesta embeddingmodeller är tränade på engelska. Även de som marknadsförs som flerspråkiga har sett betydligt mer engelsk text under träningen. Det fungerar överraskande bra för vardagssvenska, men det faller isär när man arbetar med fackspråk. Ta ett typscenario från tillverkningsindustrin med termer som "härdningsprocess", "glödgning" och "sträckgräns" i dokumentationen. Embeddingmodellen placerar dessa termer nära varandra i vektorrymden, vilket i sig inte är fel, men den kan inte skilja dem åt med tillräcklig precision. En fråga om sträckgräns kan ge svar om härdning, för modellen behandlar hela det metallurgiska fältet som en enda klump.

Det finns flera sätt att hantera det. Ett är att finjustera embeddingmodellen på kundens egna data, men det kräver träningsexempel och kompetens som inte alla har tillgång till. Ett enklare steg som ofta ger märkbar förbättring är att bygga en synonymlista eller termexpansion som körs innan sökningen. Om användaren skriver "sträckgräns" kan systemet automatiskt lägga till "yield strength" som en parallell sökterm, eftersom embeddingmodellen förmodligen har bättre representation av det engelska begreppet. Det är inte vackert, men det fungerar, och i produktion är funktion viktigare än elegans.

Ett annat återkommande problem är domänspecifika förkortningar. Inom bygg kan "BBR" betyda Boverkets byggregler, men embeddingmodellen har ingen aning om det. Den ser tre bokstäver och placerar dem nära andra trebokstavskombinationer. Att lägga till en förbearbetning som expanderar kända förkortningar till deras fulla form innan embedding förbättrar retrieval markant. Det är det ingen som nämner i tutorials, för tutorials arbetar med Wikipedia-artiklar på engelska där det problemet inte existerar.

Reranking är inte en lyx

De flesta RAG-implementationer som beskrivs i tutorials saknar ett reranking-steg, alltså ett steg som ordnar om träffarna efter hur väl de svarar på frågan. Pipeline ser ut så här: fråga in, vektorsökning, topp fem chunks skickas till modellen. Det fungerar okej i demos, men i produktion märker man snabbt att vektorsökning är bra på att hitta ungefär rätt område men dålig på att ranka inom det området. De fem chunks som kommer tillbaka är alla relevanta på något sätt, men ordningen är ofta fel, och den chunk som faktiskt innehåller svaret kan ligga på plats fyra eller fem.

Ett reranking-steg tar de kandidater som vektorsökningen returnerar och kör dem genom en mer sofistikerad modell som bedömer hur väl varje chunk faktiskt besvarar frågan, inte bara hur nära den ligger i vektorrymden. Det lägger till svarstid, hur mycket beror på antalet kandidater och modellens storlek. Men förbättringen i svarkvalitet är ofta stor. Andelen frågor där det korrekta svaret hamnar i topp tre chunks brukar lyfta när man lägger till reranking med en cross-encoder, en modell som läser fråga och kandidat tillsammans. Hur mycket beror på materialet, och det enda som ger dig svaret för din data är att mäta före och efter. Det är skillnaden mellan ett system som användarna litar på och ett de slutar använda efter två veckor.

Invändningen är alltid att det kostar för mycket i svarstid. Men om alternativet är att användaren får ett dåligt svar och måste ställa frågan igen, eller ännu värre, agerar på felaktig information, så är en bråkdels sekund en billig försäkring. Ett rimligt upplägg är att hämta fler kandidater från vektorsökningen, säg tjugo istället för fem, och sedan låta rerankern välja de bästa tre. Det ger rerankern mer att arbeta med och förbättrar resultaten ytterligare.

Hybrid sök löser problem du inte visste att du hade

Vektorsökning är semantisk. Den förstår att "uppsägning av anställd" och "avsluta ett arbetsförhållande" handlar om samma sak. Det är dess styrka. Men den har en blind fläck: exakta termer. Om en användare söker efter "avtal 2024-0847" så finns det inget semantiskt att fånga. Det är en identifierare, och vektorsökningen kommer att returnera chunks som handlar om avtal i allmänhet istället för det specifika avtalet.

BM25, den klassiska nyckelordsbaserade sökningen, är motsatsen. Den bryr sig inte om semantik utan letar efter exakta ordmatchningar. Den hittar "avtal 2024-0847" utan problem, men den missar att "uppsägning" och "avsluta arbetsförhållande" är relaterade. Hybrid sök kombinerar båda. Du kör vektorsökning och BM25 parallellt, normaliserar poängen, och slår ihop resultaten. Det låter som en kompromiss, men i praktiken vinner hybriden nästan alltid.

Tänk ett fastighetsbolag som indexerar sina hyresavtal. Där blir skillnaden mellan ren vektorsökning och hybrid sök snabbt tydlig. Vektorsökning ensam missar avtal där det som spelar roll är en exakt term och inte en betydelse. BM25 ensam missar det omvända. En hybrid som viktar de två mot varandra fångar båda fallen, och rätt viktning hittar man genom att köra sitt eget material genom båda. Den extra komplexiteten i att köra två sökningar parallellt är minimal, och BM25 kräver inga GPU-resurser. Det enda som krävs är att du indexerar dina dokument i både ett vektorindex och ett inverterat index, vilket de flesta moderna databaser som stödjer vektorsökning redan erbjuder.

Mätning bortom magkänsla

Det vanligaste sättet att utvärdera ett RAG-system är att ställa några frågor och se om svaren "känns rätt". Det är ungefär lika tillförlitligt som att testa en bil genom att titta på den. Du behöver mäta, och du behöver mäta rätt saker. De tre mått jag vill se uppsatta är retrieval precision, faithfulness och answer relevance, alltså träffsäkerhet i sökningen, trohet mot underlaget och svarets relevans.

Retrieval precision mäter om de chunks som hämtas faktiskt innehåller information som behövs för att besvara frågan. Du kan ha ett system som ger bra svar trots dålig retrieval precision, för språkmodellen är bra på att ignorera irrelevant kontext, men det betyder att du slösar tokens och pengar på att skicka in text som inte bidrar. Faithfulness mäter om svaret faktiskt baseras på den hämtade kontexten eller om modellen fyller i med egen kunskap. Ett RAG-system som hallucinerar trots att det har rätt kontext framför sig har ett problem med prompten, inte med sökningen. Answer relevance mäter om svaret faktiskt besvarar frågan som ställdes, inte en närliggande fråga.

Ett utvärderingsset behöver ett par tiotal fråga-svar-par som någon med domänkunskap har validerat. Det tar tid att skapa, men utan det flyger du blint. Varje gång du ändrar chunking-strategi, byter embeddingmodell eller justerar sökparametrar kör du utvärderingssettet och ser om siffrorna gick upp eller ner. Utan den disciplinen är varje förändring en gissning. Det är fullt möjligt att "optimera" ett system i veckor och landa sämre än man började, just för att utgångsläget aldrig mättes.

Var du börjar spelar roll

Om du ska bygga eller förbättra ett RAG-system, börja inte med modellen. Börja med datan. Titta på dina dokument och fråga dig hur en människa skulle hitta svaret. Om en människa behöver läsa tre stycken i följd för att förstå svaret, behöver ditt system också göra det, och då måste din chunking-strategi stödja det. Om dina dokument är fulla av facktermer och förkortningar, testa att söka efter dem i ditt embeddingindex och se vad som kommer tillbaka. Du kommer förmodligen att bli förvånad.

Sedan: mät tidigt. Bygg ditt utvärderingsset innan du börjar optimera, inte efter. Det är frestande att skjuta upp det, men utan mätning vet du inte om dina ändringar hjälper eller stjälper. Lägg till reranking, det kostar lite svarstid men ger mycket tillbaka. Testa hybrid sök, särskilt om dina dokument innehåller identifierare, datum eller andra exakta termer. Och framför allt, acceptera att RAG inte är magi. Det är en ingenjörsdisciplin med kända fallgropar och beprövade lösningar. Skillnaden mellan ett system som fungerar i demo och ett som fungerar i produktion är att någon tog sig tiden att förstå var det kan gå sönder.

Vill ni prata om det här?

Ta en kaffe med oss

Om något här resonerade med er situation, hör av er. Vi sitter gärna ner och pratar om var ni är och vad nästa rimliga steg kunde vara. Inga säljpitchar, bara ett samtal.

Skriv till oss

Nyhetsbrev

AI-förordningen, läget för den statliga sandlådan och svensk AI i praktiken.

En sammanfattning ungefär en gång i månaden. Vad som ändrats i regelverket, vad vi lär oss när vi bygger, och vilka leverantörsbyten som påverkar svenska bolag.

Uppgifterna används endast för nyhetsbrevet. Inga utskick utöver det utan separat samtycke. Avregistrera när som helst via länk i mailet.