Tech AI · 10 min lesing

Hva er RAG?
Slik gir du AI tilgang til
dine egne dokumenter

En språkmodell vet ikke hva som er i dine interne rutinedokumenter, årsrapporten fra i fjor, eller kundebasen din. RAG løser dette — og det finnes mange måter å gjøre det på, fra enkle open source-løsninger til fullverdige pipelines.

01 — Grunnlaget

Hva er RAG?

RAG står for Retrieval-Augmented Generation — en teknikk der man gir en AI-modell tilgang til et eksternt kunnskapsgrunnlag i det øyeblikket den svarer på et spørsmål.

Uten RAG er en språkmodell som en svært lesekyndig kollega som sluttet å lese aviser for ett år siden. De vet mye, men de vet ingenting om det du nettopp la inn i systemet, og de har aldri sett bedriftens interne dokumenter.

RAG er ikke AI som husker mer — det er AI som slår opp i riktig kilde i samme øyeblikk den svarer.

I praksis betyr dette at modellen ikke "lærer" av dokumentene dine. Den henter relevant innhold i sanntid, bruker det som kontekst, og svarer deretter. Ingen finjustering, ingen retraining. Bare smart oppslag.

Finjustering vs. RAG — to ulike ting

Det er vanlig å blande disse to. Finjustering (fine-tuning) betyr å trene modellen på nytt med dine data, slik at kunnskapen bakes inn i modellens vekter. Det er tidkrevende, dyrt og vanskelig å oppdatere.

EgenskapFine-tuningRAG
Kunnskap oppdateresKrever ny treningOppdater kilden — ferdig
KostnadHøy (GPU-tid)Lav (infrastruktur)
SporbarhetVanskeligKan sitere kilden
Personlige / interne dataRisikabelt å inkludereTrygt med riktig tilgangsstyring
Passer tilTone, stil, domene-vokabularFaktainformasjon som endres
02 — Mekanikken

Slik fungerer RAG i praksis

RAG-systemet har to faser: en indekserings-fase som skjer én gang (eller periodisk), og en spørringsfase som skjer i sanntid ved hver brukerinteraksjon.

Fase 1 — Indeksering (én gang)
📄
Dokumenter
PDF, Word, nettside, database
✂️
Chunking
Del opp i passende biter
🔢
Embedding
Omgjør tekst til tallvektorer
🗄️
Vektordatabase
Lagre for rask søk
Fase 2 — Spørring (sanntid)
💬
Brukerspørsmål
"Hva er vår returpolitikk?"
🔍
Retrieval
Finn de mest relevante chunkene
🧩
Augmentation
Legg kontekst til prompten
🤖
Svar
Modellen svarer med kilde
Hele retrieval-prosessen tar typisk 200–500 ms og er usynlig for brukeren

Hva er egentlig en embedding?

En embedding er en matematisk representasjon av tekst som et punkt i et høydimensjonalt rom. Tekster som betyr det samme ender opp nær hverandre — selv om ordene er ulike. Setningen "returpolitikk" og "hvordan sender jeg tilbake en vare?" vil ha svært like vektorer, og søket vil finne begge.

Det er denne semantiske søkingen som skiller RAG fra vanlig nøkkelordssøk. Du trenger ikke å treffe de eksakte ordene — meningen er nok.

💡
Vektorbasen er ikke magisk. Den lagrer bare tall og returnerer de nærmeste treffene. Det er selve embedding-modellen og chunking-strategien som avgjør kvaliteten på retrieval.
03 — Use cases

Hvem bruker RAG — og til hva?

RAG passer ekstra godt når informasjonen er intern, hyppig oppdatert, eller for volumiøs til å inkludere i en prompt. Her er eksempler fra ulike sektorer.

🏥
Klinisk beslutningsstøtte
Helse
Leger og sykepleiere spør om legemiddelinteraksjoner, behandlingsprotokoll eller pasienthistorikk. RAG henter fra oppdaterte fagkilder og journalsystem.
Eksempel

"Hvilke kontraindikasjoner gjelder for metformin hos denne pasienten?" — svaret er basert på den faktiske journalen og nyeste legemiddelregister.

⚖️
Juridisk research
Juss
Advokater søker i kontraktsarkiver, tidligere dommer og lovverk. RAG henter relevante paragrafer og presedenser uten at man trenger å lese tusenvis av sider.
Eksempel

"Er det noen klausuler i kontraktene våre fra 2022 som begrenser videresalg i Norden?" — svar med kildehenvisning til de eksakte avsnittene.

🏦
Finansiell analyse
Finans
Analytikere og rådgivere henter innsikt fra årsrapporter, prospekter og markedsdata. RAG gjør det mulig å stille spørsmål til hundrevis av dokumenter på sekunder.
Eksempel

"Sammenlign EBITDA-marginen i de fem siste kvartalsrapportene og beskriv trenden." — med referanse til de aktuelle sidetallene.

🎓
HR og onboarding
Organisasjon
Nyansatte kan stille spørsmål om personalhåndbok, IT-prosedyrer og fordeler. Svar hentes fra de faktiske interne dokumentene — alltid oppdaterte.
Eksempel

"Hvor mange feriedager har jeg rett på det første arbeidsåret, og hvordan søker jeg?" — AI-en henter fra personalhandboken.

🛠️
Teknisk support
Produkt / SaaS
Kundeservice og utviklere spør mot produktdokumentasjon, release notes og kjente bugs. Gir presis støtte uten at agenten trenger å kjenne alt utenat.
Eksempel

"Hvilken versjon introduserte SSO-integrasjon og hva er setup-prosessen?" — hentet fra changelog og integrasjonsdokumentasjon.

🏭
Produksjon og vedlikehold
Industri
Teknikere på gulvet kan spørre om vedlikeholdsrutiner, reservedeler og feilsøkingsprosedyrer for spesifikk maskinvare — direkte fra teknisk dokumentasjon.
Eksempel

"Feilkode E-217 på CNC-maskin modell X500 — hva er årsaken og hva er trinn-for-trinn-prosedyren?" — hentet fra servicemanualen.

🏫
Utdanning og forskning
Akademia
Studenter og forskere stiller spørsmål mot et korpus av fagartikler, pensum og forelesningsnotater. RAG gir presis kildehenvisning til relevante avsnitt.
Eksempel

"Hvilke studier fra 2020–2024 omhandler effekten av søvnmangel på kognitiv ytelse?" — med DOI-referanse til de faktiske artiklene.

🏗️
Prosjekt og intranet
Alle bransjer
Ansatte søker i Confluence, SharePoint, møtereferater og e-postarkiver. RAG gjør at intern kunnskap faktisk er søkbar og tilgjengelig — ikke bare lagret.
Eksempel

"Hva ble besluttet om prismodellen i møtene fra Q3 i fjor?" — RAG-en henter relevante møtereferater og sammenstiller svaret.

Fellesnevneren: RAG passer best når svaret finnes et konkret sted i et dokument, og der det å finne feil dokument — eller gi hallusinert svar — har reelle konsekvenser.
04 — Best practice

Hva som faktisk avgjør kvaliteten

RAG er enkelt å sette opp, men vanskelig å gjøre bra. De fleste problemene skyldes ikke AI-modellen — de skyldes dårlig datagrunnlag eller dårlig retrieval.

01

Chunk med omhu — størrelse og overlapp

For store chunks gir for mye støy. For små chunks mister konteksten. En god tommelfingerregel: 300–600 ord per chunk, med 10–20 % overlapp mellom påfølgende chunks. Eksperimenter for ditt innhold.

02

Rens dokumentene dine

Skannet PDF med dårlig OCR, tabeller i ustrukturert format, og rotete HTML gir dårlig retrieval. Bruk tid på dokumentforberedelse — det er den viktigste investeringen du gjør i et RAG-system.

03

Bruk metadata aktivt

Legg til metadata som dato, forfatter, avdeling og dokumenttype til hver chunk. Da kan du filtrere retrieval: "finn kun fra HR-dokumenter" eller "nyere enn 2023". Hybrid søk — vektor + filter — er langt mer presis enn ren semantisk søk.

04

Evaluer retrieval separat fra generering

Splitt problemet i to: Finner systemet de riktige chunkene? Og formulerer modellen et godt svar fra dem? Mange feil skyldes dårlig retrieval, ikke dårlig generering. Mål begge.

05

Siter alltid kilden

Et RAG-system uten kildehenvisning er et system ingen stoler på. Designet systemet slik at modellen alltid refererer til hvilken kilde svaret kommer fra — dokument, side, dato. Dette gir sporbarhet og gjør det enkelt å verifisere.

06

Tilgangsstyring er ikke valgfritt

Hvis systemet ditt inneholder HR-dokumenter, finansdata og offentlig innhold, kan ikke alle spørre om alt. Implementer tilgangsstyring på chunk-nivå — ikke bare på applikasjonsnivå. En bruker skal kun få svar basert på dokumenter de har tilgang til.

07

Lag et fallback for "vet ikke"

Instruer modellen eksplisitt: om retrieval ikke finner noe relevant, skal den si det — ikke finne på noe. En god systemmelding: "Basér svaret utelukkende på de vedlagte dokumentene. Hvis svaret ikke finnes der, si at du ikke vet."

08

Hold indeksen oppdatert

Et RAG-system er bare så godt som indeksen det søker i. Sett opp automatisk re-indeksering når dokumenter endres. Vurder versjonsstyring slik at du kan spore endringer over tid.

⚠️
Vanlig feil: Mange tester RAG med spørsmål de vet svaret på. Test heller med spørsmål der svaret ikke finnes i dokumentene — det avslører om systemet hallusinerer eller ærlig innrømmer at det ikke vet.
05 — Lagring

Hvor lagrer du vektorene?

Det finnes mange måter å lagre embeddings på — fra dedikerte vektordatabaser som Qdrant, Chroma og Weaviate, til vektorutvidelser i databaser du kanskje allerede kjører: pgvector for PostgreSQL, Atlas Vector Search for MongoDB, og Elasticsearch med hybrid søk. Det finnes til og med løsninger uten server i det hele tatt, som SQLite + sqlite-vss for lokal prototyping.

Valget avhenger i stor grad av hva du allerede har. I neste seksjon tar vi et konkret dypdykk i to av de mest brukte open source-alternativene: Chroma for å komme raskt i gang, og pgvector for de som allerede kjører PostgreSQL.

💡
Tommelfingerregel: Har du ikke noe fra før — start med Chroma. Kjører du allerede Postgres — bruk pgvector og slipp en ekstra tjeneste. Trenger du distribusjon og høy ytelse i produksjon — se på Qdrant.
06 — Kom i gang

Lavterskel: din første RAG på 30 minutter

Her er to konkrete veier fra null til fungerende RAG — én med Chroma (enklest, ingen server) og én med PostgreSQL + pgvector (for de som allerede har Postgres).

Alternativ A — Chroma (lokalt, ingen server)

Chroma er en vektordatabase som kjører direkte i Python-prosessen og lagrer data lokalt på disk. Perfekt for å forstå RAG uten å sette opp infrastruktur.

Python — minimal RAG med LangChain + Chroma
# pip install langchain chromadb sentence-transformers anthropic

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import SentenceTransformerEmbeddings
import anthropic, pathlib

# 1. Les inn og del opp dokumenter
tekst = pathlib.Path("rutiner.txt").read_text()
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.create_documents([tekst])

# 2. Embed og lagre lokalt — ingen server nødvendig
embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
db = Chroma.from_documents(chunks, embeddings, persist_directory="./min_rag_db")

# 3. Søk og generer svar
def spor(sporsmaal: str) -> str:
    treff = db.similarity_search(sporsmaal, k=4)
    kontekst = "

".join(t.page_content for t in treff)

    client = anthropic.Anthropic()
    svar = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        system="Svar kun basert på dokumentene nedenfor. Si fra om du ikke finner svaret.",
        messages=[{"role": "user", "content": f"Dokumenter:
{kontekst}

Spørsmål: {sporsmaal}"}]
    )
    return svar.content[0].text

print(spor("Hva er prosedyren for å melde inn ferie?"))

Alternativ B — PostgreSQL + pgvector

Har du allerede en Postgres-database er pgvector en utvidelse som legger til en ny kolonnetype (vector) og en ny operatør (<->) for nærhetssøk. Du slipper en ekstra tjeneste, og data bor på samme sted som resten av systemet ditt.

SQL — sett opp pgvector
-- Installer utvidelsen (én gang per database)
CREATE EXTENSION IF NOT EXISTS vector;

-- Tabell for chunks + embeddings
CREATE TABLE dokumenter (
    id          SERIAL PRIMARY KEY,
    kilde       TEXT,
    dato        DATE,
    tekst       TEXT,
    embedding   vector(384)  -- dimensjon avhenger av embedding-modellen
);

-- Indeks for rask nærhetssøk (IVFFlat eller HNSW)
CREATE INDEX ON dokumenter
    USING hnsw (embedding vector_cosine_ops);

-- Søk: finn de 5 nærmeste naboene til en gitt vektor
SELECT kilde, tekst, embedding <-> '[0.12, 0.34, ...]' AS distanse
FROM dokumenter
ORDER BY distanse
LIMIT 5;
Python — indekser og søk med pgvector
# pip install psycopg2-binary pgvector sentence-transformers anthropic

import psycopg2
from pgvector.psycopg2 import register_vector
from sentence_transformers import SentenceTransformer
import anthropic

model = SentenceTransformer("all-MiniLM-L6-v2")
conn  = psycopg2.connect("postgresql://bruker:passord@localhost/mindb")
register_vector(conn)

# Indekser en chunk
def legg_til(tekst: str, kilde: str):
    vektor = model.encode(tekst).tolist()
    with conn.cursor() as cur:
        cur.execute(
            "INSERT INTO dokumenter (tekst, kilde, embedding) VALUES (%s, %s, %s)",
            (tekst, kilde, vektor)
        )
    conn.commit()

# Søk og svar
def spor(sporsmaal: str) -> str:
    vektor = model.encode(sporsmaal).tolist()
    with conn.cursor() as cur:
        cur.execute(
            "SELECT kilde, tekst FROM dokumenter ORDER BY embedding <-> %s LIMIT 4",
            (vektor,)
        )
        treff = cur.fetchall()

    kontekst = "

".join(f"[{r[0]}]
{r[1]}" for r in treff)
    client = anthropic.Anthropic()
    svar = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        system="Svar kun basert på dokumentene nedenfor.",
        messages=[{"role": "user", "content": f"Dokumenter:
{kontekst}

Spørsmål: {sporsmaal}"}]
    )
    return svar.content[0].text
🚀
Begge alternativene er gratis og open source. Chroma er raskest å komme i gang med. pgvector er bedre om du vil unngå en ny tjeneste og allerede har Postgres i stacken din.
07 — E-post i praksis

Fra Outlook-arkiv til søkbar AI-base

Outlook er det de fleste faktisk bruker. La oss ta et konkret eksempel: du vil indeksere et e-postarkiv — kanskje en supportboks, et prosjektarkiv, eller en hel avdelings korrespondanse — og gjøre det søkbart med AI. Steg én er å få dataen ut.

Steg 1 — Ta backup fra Outlook

Outlook lagrer e-post lokalt i .pst-filer (Personal Storage Table). Du eksporterer disse via innebygd funksjonalitet:

Fil → Åpne og eksporter → Importer/eksporter

Velg "Eksporter til en fil", deretter "Outlook-datafil (.pst)". Velg hvilke mapper du vil eksportere — du kan velge én mappe, én konto, eller hele postkassen. Inkluder undermapper.

Exchange / Microsoft 365

Bruker dere Exchange Online (Microsoft 365) kan administrator eksportere postkasser via PowerShell eller Exchange Admin Center til .pst — uavhengig av om Outlook er installert lokalt.

Alternativ: direkte fra Exchange via Graph API

Microsoft Graph API lar deg hente e-post programmatisk uten å eksportere til fil. Mer teknisk, men den rette veien for løpende integrasjon fremfor en engangsdump.

Steg 2 — Les .pst-filen i Python

En .pst-fil er et proprietært binærformat, men biblioteket pypff (eller libpff) lar deg lese det direkte. Alternativt kan du eksportere via Outlook til .msg-filer og lese disse med extract-msg.

Python — les .pst og ekstraher e-poster
# pip install pypff extract-msg
# Merk: pypff krever libpff kompilert på systemet
# Alternativt: konverter til .msg i Outlook og bruk extract-msg

import pypff, pathlib

def les_mappe(mappe, dybde=0):
    """Rekursivt gå gjennom alle mapper i .pst"""
    for i in range(mappe.number_of_sub_messages):
        melding = mappe.get_sub_message(i)
        yield {
            "emne":   melding.subject or "",
            "fra":    melding.sender_name or "",
            "dato":   str(melding.delivery_time)[:10],
            "tekst":  melding.plain_text_body or "",
        }
    for i in range(mappe.number_of_sub_folders):
        yield from les_mappe(mappe.get_sub_folder(i), dybde+1)

pst = pypff.file()
pst.open("eksport.pst")
rotmappe = pst.get_root_folder()

epost_liste = list(les_mappe(rotmappe))
print(f"Fant {len(epost_liste)} e-poster")

Steg 3 — Rens og bygg chunks

E-post er støyete: videresend-headers, signaturer, HTML-rester, og lange svar-tråder med gjentatt innhold. Du bør rydde opp før du indekserer.

Python — rens og bygg chunks med metadata
import re

def rens(tekst: str) -> str:
    # Fjern videresend-headers og svar-sitater
    tekst = re.sub(rr"Fra:.*?
", "", tekst)
    tekst = re.sub(rr"Den \d.*?skrev:", "", tekst, flags=re.DOTALL)
    tekst = re.sub(rr"_{3,}.*", "", tekst, flags=re.DOTALL)  # signaturskillelinje
    # Fjern HTML-rester og normaliser whitespace
    tekst = re.sub(rr"<[^>]+>", " ", tekst)
    return re.sub(rr"\s+", " ", tekst).strip()

def bygg_chunks(epost_liste: list) -> list:
    chunks = []
    for e in epost_liste:
        tekst = rens(e["tekst"])
        if len(tekst) < 30:   # Hopp over tomme/trivielle e-poster
            continue

        # Inkluder emnet i teksten — hjelper retrieval
        chunk_tekst = f"Emne: {e['emne']}

{tekst}"

        chunks.append({
            "text": chunk_tekst,
            "metadata": {
                "emne":  e["emne"],
                "fra":   e["fra"],
                "dato":  e["dato"],
            }
        })
    return chunks

chunks = bygg_chunks(epost_liste)
print(f"{len(chunks)} chunks klare for indeksering")

Steg 4 — Indekser i PostgreSQL med pgvector

Med chunkene klare setter vi dem inn i Postgres. Tabellen trenger en vector-kolonne for embeddings og vanlige kolonner for metadata — da kan du kombinere semantisk søk med SQL-filtrering på avsender, dato eller emne.

SQL — opprett tabell for e-post-chunks
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE epost_chunks (
    id        SERIAL PRIMARY KEY,
    emne      TEXT,
    fra       TEXT,
    dato      DATE,
    tekst     TEXT,
    embedding vector(384)
);

-- HNSW-indeks for rask nærhetssøk
CREATE INDEX ON epost_chunks
    USING hnsw (embedding vector_cosine_ops);
Python — embed og sett inn alle chunks
# pip install psycopg2-binary pgvector sentence-transformers anthropic

import psycopg2
from pgvector.psycopg2 import register_vector
from sentence_transformers import SentenceTransformer
import anthropic

model = SentenceTransformer("all-MiniLM-L6-v2")
conn  = psycopg2.connect("postgresql://bruker:passord@localhost/mindb")
register_vector(conn)

# Sett inn alle chunks i én batch
with conn.cursor() as cur:
    for c in chunks:
        vektor = model.encode(c["text"]).tolist()
        cur.execute(
            "INSERT INTO epost_chunks (emne, fra, dato, tekst, embedding)"
            "VALUES (%s, %s, %s, %s, %s)",
            (c["metadata"]["emne"],
             c["metadata"]["fra"],
             c["metadata"]["dato"],
             c["text"],
             vektor)
        )
conn.commit()
print("Indeksering ferdig")

Steg 5 — Søk og generer svar

Nå kan du kombinere vektorsøk med vanlig SQL. Det er her pgvector virkelig skiller seg fra Chroma: du kan filtrere på eksakt dato, avsender, eller hvilken som helst kombinasjon av metadata — i samme spørring som selve nærhetssøket.

Python — søk med pgvector og generer svar
def spor(sporsmaal: str, fra_dato: str = "2024-01-01") -> str:
    vektor = model.encode(sporsmaal).tolist()

    with conn.cursor() as cur:
        cur.execute("""
            SELECT emne, fra, dato, tekst
            FROM   epost_chunks
            WHERE  dato >= %s
            ORDER  BY embedding <-> %s
            LIMIT  5
        """, (fra_dato, vektor))
        treff = cur.fetchall()

    # Bygg kontekst med metadata synlig for modellen
    kontekst = "\n\n".join(
        f"[{r[2]} | Fra: {r[1]}]\nEmne: {r[0]}\n{r[3]}"
        for r in treff
    )

    client = anthropic.Anthropic()
    svar = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        system="Svar kun basert på e-postene nedenfor. Referer til dato og avsender.",
        messages=[{
            "role": "user",
            "content": f"E-poster:\n{kontekst}\n\nSpørsmål: {sporsmaal}"
        }]
    )
    return svar.content[0].text

# Eksempel
print(spor("Hva ble besluttet om budsjettet for Q3?"))
print(spor("Har noen rapportert problemer med innlogging?", fra_dato="2023-01-01"))
⚠️
Personvern: E-postarkiver inneholder gjerne personopplysninger. Avklar med personvernansvarlig hva som kan indekseres, hvem som har tilgang til å spørre mot systemet, og om e-postadresser og avsenderinformasjon bør anonymiseres.
Avslutning

RAG er ikke et prosjekt — det er en arkitektur

Det finnes ikke ett RAG-system. Det finnes tusenvis av varianter, fra én PDF lastet opp i en chat til distribuerte retrieval-pipelines med re-ranking, hybrid søk og agentisk orkestrering.

Det viktige å huske er at teknologien er sekundær. Kvaliteten på svarene avhenger nesten alltid mer av datakvaliteten, chunking-strategien, og systemprompten enn av hvilken modell eller vektordatabase du velger.

Start enkelt. Forstå hva retrieval faktisk returnerer. Evaluer ærlig. Og bygg videre derfra.

Det beste RAG-systemet er det du faktisk bruker — ikke det du planlegger å bygge.