RAG betyr slå opp først, svar etterpå

En språkmodell kjenner ikke dokumentene dine. RAG er måten du gir den dem på: finn de riktige avsnittene først, og la modellen svare ut fra dem.

Magnus Gribbestad Oppdatert oktober 2026 5 min
Figur · RAG fra spørsmål til svar
RAG: slå opp først, svar etterpå Et spørsmål stilles. Systemet søker i et dokumentarkiv og finner to relevante dokumenter. Utdrag fra dem legges sammen med spørsmålet og sendes til språkmodellen, som svarer med henvisning til kildene. DU Hva sier avtalen om oppsigelse? ARKIVET Veiledning Møtereferat Avtalen Tillegg Mal TREFF 1 TREFF 2 DET MODELLEN FÅR Spørsmålet ditt, pluss utdrag 1 og 2. «Svar bare ut fra utdragene.» SVARET Tre måneders oppsigelsestid. Med henvisning til kildene. AVTALEN TILLEGG
  1. Du stiller et spørsmål om dine egne dokumenter. Dem har ikke modellen sett.
  2. Før modellen får spørsmålet, søker systemet i et arkiv med dokumentene dine.
  3. Det finner de to som passer best, ofte ved å sammenligne mening, slik som i embedding.
  4. Utdrag fra treffene legges sammen med spørsmålet, med beskjed om å holde seg til dem.
  5. Modellen svarer, og kan vise hvor svaret kom fra. Du kan sjekke kilden selv.
Kort sagt

RAG står for retrieval-augmented generation, på norsk omtrent «generering styrket av oppslag». Systemet finner de riktige avsnittene i dokumentene dine først, og modellen svarer ut fra dem.

Språkmodellen er trent på mye tekst, men ikke på avtalene, rutinene eller rapportene dine. Spør du den om dem, gjetter den. RAG gjør at den slår opp i stedet.

Hvorfor ikke bare lime alt inn

Du kan lime et dokument inn i en samtale, og det fungerer fint for ett dokument. Men et arkiv på tusen sider passer ikke i kontekstvinduet, og selv om det gjorde det, ville det vært dyrt og tregt å sende alt hver gang. RAG sender bare det som trengs.

Tre ting som må stemme

Et bilde, ikke en fasit

Tenk på en student til muntlig eksamen som har lov til å bla i pensum. Hun svarer bedre enn en som bare husker, men bare hvis hun finner riktig side.

Hvorfor det gir kilder

Fordi systemet vet hvilke avsnitt det ga til modellen, kan det vise dem. Du kan klikke deg til avsnittene og sjekke om svaret står der. Det gjør kontroll mulig, men gjør den ikke unødvendig: modellen kan lese feil eller blande to utdrag, og likevel stå med riktig kildelenke.

Når det ikke virker

RAG er ikke en garanti. Har søket funnet feil avsnitt, svarer modellen trygt ut fra feil grunnlag. Spørsmål som krever å se hele dokumentet, som «hva er hovedpoenget i hele rapporten», passer dårlig, for modellen får bare biter. Og er dokumentene selv uklare eller motstridende, blir svaret det også. To ting til som en bedrift må ha oversikt over: hvem som får se hvilke avsnitt (en bruker skal ikke få svar bygd på dokumenter hun ikke har tilgang til), og at tekst i selve dokumentene kan inneholde instruksjoner modellen følger, slik det står under verktøy.

Vil du se hvordan du bygger og tester det? Les RAG i praksis. Er du bare ute etter å gi modellen ett bestemt dokument, er det enklere å legge det rett inn.

For deg som bygger

Rørleggerarbeidet i RAG, i ren Python uten pakker: del teksten i biter, regn en enkel TF-IDF (ord som er sjeldne i resten av bitene teller mer), finn de to bitene som ligner mest på spørsmålet, og sett sammen prompten. Teksten er konstruert. Prompten kan du sende til en modell som i verktøy-eksempelet, uten verktøy.

Python · RAG uten pakker
import math
import re
from collections import Counter

TEKST = """Reiseregninger leveres innen ti dager etter hjemkomst. Kvitteringer må
vedlegges, og beløp over tusen kroner må godkjennes av nærmeste leder før
utbetaling. Hotell bestilles gjennom avtalen vi har med kjeden, ellers dekkes
ikke prisen over seks hundre kroner natten.

Hjemmekontor kan avtales med nærmeste leder, inntil to dager i uka. Utstyr
til hjemmekontor kjøpes inn av arbeidsgiver, men maks én skjerm og én stol per
ansatt. Nettforbindelse hjemme dekkes ikke.

Ferie planlegges i februar. Hovedferien er tre uker sammenhengende i
perioden juni til august. Ønsker du ferie utenom, avtales det med leder senest
seks uker i forvegen."""


def del_i_biter(tekst, ord_per_bit=30, overlapp=8):
    ord_ = tekst.split()
    biter, start = [], 0
    while start < len(ord_):
        biter.append(" ".join(ord_[start:start + ord_per_bit]))
        start += ord_per_bit - overlapp
    return biter


def ordliste(tekst):
    return re.findall(r"\w+", tekst.lower())


biter = del_i_biter(TEKST)
antall_biter = len(biter)
forekomst = Counter(o for b in biter for o in set(ordliste(b)))   # hvor mange biter har ordet?


def lag_vektor(tekst):                       # <-- denne linja byttes ved embeddings
    tf = Counter(ordliste(tekst))
    return {o: n * (math.log((1 + antall_biter) / (1 + forekomst[o])) + 1)
            for o, n in tf.items()}


def cosinus(a, b):
    skalar = sum(v * b.get(o, 0) for o, v in a.items())
    lengde = math.sqrt(sum(v * v for v in a.values())) * math.sqrt(sum(v * v for v in b.values()))
    return skalar / lengde if lengde else 0.0


def hent(sporsmal, antall=2):
    q = lag_vektor(sporsmal)
    rangert = sorted(((cosinus(q, lag_vektor(b)), b) for b in biter), reverse=True)
    return [(skår, b) for skår, b in rangert[:antall] if skår > 0]


def bygg_prompt(sporsmal, treff):
    kilder = "\n\n".join(f"<kilde nr=\"{i}\">{b}</kilde>" for i, (_, b) in enumerate(treff, 1))
    return (f"Svar bare ut fra kildene under. Står svaret ikke der, si det.\n\n"
            f"{kilder}\n\nSpørsmål: {sporsmal}")


sporsmal = "Hvor mange dager i uka kan jeg ha hjemmekontor?"
treff = hent(sporsmal)
print(f"{antall_biter} biter. Spørsmål: {sporsmal}\n")
for plass, (skår, bit) in enumerate(treff, 1):
    print(f"Treff {plass}, skår {skår:.2f}: {bit[:60]}...")
print("\n--- Prompten som sendes til modellen ---")
print(bygg_prompt(sporsmal, treff))

print("\n--- Samme spørsmål, andre ord ---")
print(hent("Lov å jobbe hjemmefra?") or "Ingen treff: ingen av ordene i spørsmålet står i teksten.")

Utskrift fra en kjøring:

Utskrift
5 biter. Spørsmål: Hvor mange dager i uka kan jeg ha hjemmekontor?

Treff 1, skår 0.22: Hotell bestilles gjennom avtalen vi har med kjeden, ellers d...
Treff 2, skår 0.15: leder, inntil to dager i uka. Utstyr til hjemmekontor kjøpes...

--- Prompten som sendes til modellen ---
Svar bare ut fra kildene under. Står svaret ikke der, si det.

<kilde nr="1">Hotell bestilles gjennom avtalen vi har med kjeden, ellers dekkes ikke prisen over seks hundre kroner natten. Hjemmekontor kan avtales med nærmeste leder, inntil to dager i uka. Utstyr til</kilde>

<kilde nr="2">leder, inntil to dager i uka. Utstyr til hjemmekontor kjøpes inn av arbeidsgiver, men maks én skjerm og én stol per ansatt. Nettforbindelse hjemme dekkes ikke. Ferie planlegges i februar.</kilde>

Spørsmål: Hvor mange dager i uka kan jeg ha hjemmekontor?

--- Samme spørsmål, andre ord ---
Ingen treff: ingen av ordene i spørsmålet står i teksten.

TF-IDF sammenligner ord, ikke mening. Det ser du i siste kjøring: «hjemmefra» finner ingenting, selv om spørsmålet handler om hjemmekontor. Se også at treff 1 starter midt i et avsnitt om hotell, fordi bitene deles på antall ord. Del heller på avsnitt eller overskrifter.

Et ekte oppsett bytter ut den markerte funksjonen med et kall til en embedding-modell. Resten er likt, men vektorene blir lister med tall i stedet for ordbøker, så sammenligningen skrives som i embedding. Sjekk også hvor lang tekst embedding-modellen tar imot. Noen åpne modeller stopper på 512 token, og det som er lenger, avkortes vanligvis uten feilmelding. Med Voyage AI, som Anthropic peker på i sin dokumentasjon, ser det slik ut:

Python · bytt ut lag_vektor
# pip install -U voyageai     (nøkkelen ligger i miljøvariabelen VOYAGE_API_KEY)
import voyageai

vo = voyageai.Client()
bit_vektorer = vo.embed(biter, model="voyage-4", input_type="document").embeddings
q = vo.embed([sporsmal], model="voyage-4", input_type="query").embeddings[0]
# Voyage-vektorene har lengde 1, så prikkproduktet er det samme som cosinus.
skar = [sum(x * y for x, y in zip(q, v)) for v in bit_vektorer]

Den første koden er kjørt her med Python 3.13 og bare standardbiblioteket, 8. oktober 2026, og utskriften er limt inn slik den kom. Skårene er fra en liten, konstruert tekst og sier ingenting om hvor godt det virker på dine dokumenter. Embedding-snutten er syntakssjekket og lest mot Anthropics embedding-dokumentasjon, men ikke kjørt: den trenger en Voyage-nøkkel. Ingenting er sendt til en modell.

Oppdatert oktober 2026. RAG er et mønster og ikke et produkt, og det finnes mange måter å bygge det på. Figuren forenkler: den viser hele dokumenter, mens et ekte system deler dem i biter og henter flere treff, ofte med en omrangering. Navnet kommer fra artikkelen «Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks» (Lewis m.fl., 2020).