RAG är inte magi. Här är var det går sönder
Förra hösten satt vi med en kund som hade byggt sitt eget RAG-system på tre veckor. De hade en vektordatabas, en embeddingmodell, och ett chattgränssnitt som såg proffsigt ut. Allt fungerade utmärkt på demofrågorna.
Alexander Galldin
Kapaciti
Förra hösten satt vi med en kund som hade byggt sitt eget RAG-system på tre veckor. De hade en vektordatabas, en embeddingmodell, och ett chattgränssnitt som såg proffsigt ut. Allt fungerade utmärkt på demofrågorna. Sedan ställde en av deras säljare en fråga om garantivillkor för en specifik produktserie, och systemet svarade 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. Vi har sett samma mönster hos tillräckligt många kunder nu för att kunna 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.
Vi hade ett uppdrag där kunden indexerade sina ramavtal. Chunkstorleken var satt till 512 tokens, vilket är en vanlig default. Problemet var att en klausul om ansvarsbegränsning började i slutet av en chunk och fortsatte i nästa. När systemet hämtade den första chunken fick användaren information om att ansvarsbegränsningen gällde, men missade undantaget som stod i nästa bit. Svaret var 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 vi har landat i hos de flesta kunder ä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. Ett tillverkningsföretag vi jobbade med hade termer som "härdningsprocess", "glödgning" och "sträckgräns" i sin dokumentation. Embeddingmodellen placerade dessa termer nära varandra i vektorrymden, vilket i sig inte är fel, men den kunde inte skilja dem åt med tillräcklig precision. En fråga om sträckgräns kunde ge svar om härdning, för modellen behandlade 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 mönster vi sett är att domänspecifika förkortningar skapar problem. 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 vi ser hos nya kunder saknar ett reranking-steg. 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 latency, typiskt 50 till 200 millisekunder beroende på antalet kandidater och modellens storlek. Men förbättringen i svarkvalitet är ofta dramatisk. Vi mätte hos en kund som hade ett internt supportverktyg och såg att andelen frågor där det korrekta svaret fanns i topp tre chunks gick från 64 procent till 89 procent efter att vi lade till reranking med en cross-encoder. Det är skillnaden mellan ett system som användarna litar på och ett de slutar använda efter två veckor.
Invändningen vi ofta hör är att det kostar för mycket i latency. 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 150 millisekunder en billig försäkring. Vi brukar 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.
Vi körde ett kontrollerat test hos ett fastighetsbolag som hade indexerat sina hyresavtal. Med enbart vektorsökning hittade systemet rätt avtal i 71 procent av frågorna. Med enbart BM25 låg siffran på 58 procent. Med hybrid sök, där vi viktade vektorsökning 60 procent och BM25 40 procent, nådde vi 86 procent. Den extra komplexiteten i att köra två sökningar parallellt var minimal, och BM25 kräver inga GPU-resurser. Det enda som krävs är att du indexerar dina dokument i både en 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 vi alltid sätter upp hos kunder är retrieval precision, faithfulness och answer relevance.
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.
Vi bygger utvärderingsset med minst 50 fråga-svar-par som domänexperter hos kunden 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. Vi har sett team som "optimerade" sitt system i veckor och slutade med sämre resultat än de började med, för de aldrig mätte utgångsläget.
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 latency 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.