AI Engineering Academy · leksjon

RAG-arkitekturen: indeksering og uthenting

Kartlegg de to fasene i RAG: den offline-baserte indekseringsfasen, som deler opp, embedder og lagrer dokumenter, og den online-baserte uthentingsfasen, som finner relevant kontekst for hver spørring.

Leksjon 2 av 413 trinn

RAG-arkitekturen: indeksering og uthenting er en gratis leksjon i AI Engineering Academy på CoddyKit. Dette er leksjon 2 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i AI Engineering Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i AI Engineering Academy inneholder totalt 4 leksjoner.

RAG har to separate faser

Et RAG-system opererer i to grunnleggende forskjellige faser som kjøres på ulike tidspunkter. Den offline indekseringsfasen behandler dokumentene dine én gang (eller når de endres) og klargjør en søkbar indeks. Den online hente-fasen kjører i sanntid for hver brukerspørring. Det er viktig å forstå dette skillet når du skal utforme systemer som både er raske ved spørringer og enkle å vedlikeholde over tid.

Indekseringsfasen: Trinn én – last inn

Indekseringen starter med innlasting av dokumenter: lesing av råfiler fra kildesystemene dine. Dokumentene kan være PDF-er, Word-filer, HTML-sider, Markdown-filer, databas rader eller hvilken som helst tekstkilde. Hvert dokument lastes inn i minnet som ren tekst, samtidig som strukturen bevares så langt det er mulig. Biblioteker som pypdf, python-docx og unstructured håndterer det krevende arbeidet med formatspesifikk parsing.

from pypdf import PdfReader

def load_pdf(path):
    reader = PdfReader(path)
    pages = []
    for i, page in enumerate(reader.pages):
        text = page.extract_text()
        pages.append({'text': text, 'page': i + 1, 'source': path})
    return pages

docs = load_pdf('company_policy.pdf')
print(f'Loaded {len(docs)} pages')

Indekseringsfasen: Trinn to – del opp

LLM-er har begrensede kontekstvinduer, og det er lite effektivt å hente hele dokumenter. Den innlastede teksten deles opp i mindre deler på omtrent 200–1000 tokens hver. God oppdeling bevarer den semantiske sammenhengen: En del bør uttrykke en komplett idé. En vanlig strategi bruker overlappende vinduer, slik at setninger nær grensene mellom delene forekommer i to deler og hindrer informasjonstap ved oppdelingen.

from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # characters per chunk
    chunk_overlap=50,    # overlap between chunks
    separators=['\n\n', '\n', '. ', ' ']
)

for page in docs:
    chunks = splitter.split_text(page['text'])
    for chunk in chunks:
        # Each chunk carries metadata from its source page
        print(f'Chunk ({len(chunk)} chars): {chunk[:80]}...')

Indekseringsfasen: Trinn tre – lag embedding

Hver tekstdel konverteres til en tett vektorembedding som representerer den semantiske betydningen numerisk. Du kaller en embedding-modell – for eksempel OpenAI sin text-embedding-3-small – for hver del og mottar en flyttallstabell med mange dimensjoner. Deler med lignende betydning gir vektorer som ligger nær hverandre i dette høydimensjonale rommet, og det er dette som muliggjør semantisk likhetssøk.

from openai import OpenAI

client = OpenAI()

def embed_chunks(chunks):
    texts = [c['text'] for c in chunks]
    response = client.embeddings.create(
        model='text-embedding-3-small',
        input=texts
    )
    for i, chunk in enumerate(chunks):
        chunk['embedding'] = response.data[i].embedding
    return chunks

# Batch up to 2048 texts per API call
embedded = embed_chunks(all_chunks)

Indekseringsfasen: Trinn fire – lagre

De embeddede delene lagres i en vektordatabase sammen med metadataene deres (kildefil, sidetall, seksjonstittel). Vektorlageret bygger en indeksstruktur (vanligvis HNSW) som muliggjør raskt tilnærmet nærmeste-nabo-søk. Denne indeksen lagres på disk, slik at den overlever omstarter. Indeksering skjer vanligvis én gang ved oppsett og trinnvis når nye dokumenter legges til.

import pinecone

pc = pinecone.Pinecone(api_key='YOUR_KEY')
index = pc.Index('rag-documents')

# Upsert vectors with metadata
vectors_to_upsert = [
    (
        chunk['id'],
        chunk['embedding'],
        {'text': chunk['text'], 'source': chunk['source'], 'page': chunk['page']}
    )
    for chunk in embedded_chunks
]

# Upsert in batches of 100
for i in range(0, len(vectors_to_upsert), 100):
    index.upsert(vectors=vectors_to_upsert[i:i+100])
print('Indexing complete')

Hentefasen: Embedding av spørringen

Når en bruker sender inn en spørring, starter den nettbaserte hentefasen. Det første trinnet er å lage en embedding av brukerens spørsmål ved hjelp av den samme embedding-modellen som ble brukt under indekseringen. Dette er avgjørende: Hvis du indekserte med text-embedding-3-small, må du også bruke text-embedding-3-small ved spørringer. Spørringsembeddingen er en vektor som koder den semantiske betydningen av det brukeren spør om.

def embed_query(question):
    response = client.embeddings.create(
        model='text-embedding-3-small',  # MUST match indexing model
        input=[question]
    )
    return response.data[0].embedding

user_question = 'What is our parental leave policy?'
query_vector = embed_query(user_question)
print(f'Query embedded: {len(query_vector)}-dim vector')

Hentefasen: ANN-søk

Spørringsembeddingen sendes til vektorlageret, som utfører et tilnærmet nærmeste-nabo-søk (ANN) for å finne de K delene hvis embedding-er ligner mest på spørringsvektoren. Dette søket er svært raskt (vanligvis under 10 ms), fordi HNSW-indekser bytter bort litt treffsikkerhet mot enorme hastighetsgevinster sammenlignet med uttømmende søk. Du henter de K beste delene – vanligvis K=5 til K=20.

results = index.query(
    vector=query_vector,
    top_k=5,
    include_metadata=True
)

print(f'Retrieved {len(results.matches)} chunks:')
for match in results.matches:
    print(f'  Score: {match.score:.3f} | Source: {match.metadata["source"]}')
    print(f'  Text: {match.metadata["text"][:100]}...')
    print()

Koble sammen de to fasene

Det viktigste poenget er at indeksering og henting er utformet for å fungere sammen. Embedding-modellen må være identisk i begge faser, fordi det matematiske rommet vektorene ligger i, er spesifikt for modellen. Hvis du bytter embedding-modell, må du indeksere alle dokumentene på nytt. Vektorlageret er bindeleddet: Det mottar vektorer under indekseringen og returnerer vektorer under hentingen. Dermed kobles fasene tidsmessig fra hverandre, samtidig som de holdes samordnet i vektorrommet.

Metadat filtrering under henting

Vektorsøk finner semantisk lignende deler, men noen ganger trenger du også å filtrere etter metadata. Du kan for eksempel bare hente deler fra dokumenter som ble lastet opp i 2025, eller bare fra HR-avdelingen. Vektorlager støtter forhåndsfiltrering eller etterfiltrering på metadatafelter. Forhåndsfiltrering (støttet av Pinecone og Qdrant) bruker filteret før ANN-søket, noe som er raskere og mer treffsikkert enn etterfiltrering av de K beste resultatene.

# Retrieve only from HR department documents
results = index.query(
    vector=query_vector,
    top_k=5,
    filter={'department': {'$eq': 'HR'}},
    include_metadata=True
)

# Or filter by date range
results = index.query(
    vector=query_vector,
    top_k=5,
    filter={
        'upload_year': {'$gte': 2024},
        'doc_type': {'$eq': 'policy'}
    },
    include_metadata=True
)

Trinnvis indeksering ved oppdateringer

I produksjon endres dokumentsamlingen over tid. En effektiv indekseringsarkitektur støtter trinnvise oppdateringer: Når et dokument redigeres, sletter du de eksisterende vektorene etter ID og oppdaterer med de nye. Når dokumenter slettes, fjerner du vektorene deres. Gi hver del en deterministisk ID basert på kildebanen til dokumentet og delens posisjon, slik at du alltid kan finne og oppdatere de riktige vektorene uten å indeksere alt på nytt.

import hashlib

def make_chunk_id(source_path, chunk_index):
    # Deterministic, stable ID for each chunk
    key = f'{source_path}::chunk_{chunk_index}'
    return hashlib.md5(key.encode()).hexdigest()

def update_document(source_path, index):
    # Delete old vectors for this document
    index.delete(filter={'source': source_path})
    # Re-index the updated document
    new_chunks = load_and_chunk(source_path)
    new_embedded = embed_chunks(new_chunks)
    index.upsert(vectors=new_embedded)
    print(f'Updated {source_path}: {len(new_chunks)} chunks')

Hele RAG-pipelinen i oversikt

Den komplette RAG-arkitekturen ser slik ut: Offline: Dokumenter → Laster → Oppdeler → Embedding-modell → Vektorlager. Online: Brukerspørring → Embedding-modell → Vektorlager (ANN-søk) → K beste deler → Prompt-sammensetting → LLM → Svar. Offline-pipelinen kjører én gang per dokumentoppdatering. Online-pipelinen kjører på millisekunder for hver brukerspørring, og LLM-en ser bare den relevante konteksten, ikke hele dokumentsamlingen.

Hurtigsjekk

Test forståelsen din av konsepter innen KI-teknikk fra denne leksjonen.

Oppsummering av leksjonen

I denne leksjonen lærte du: den offline indekseringsfasen, som består av trinnene last inn, del opp, lag embedding og lagre, og som kjøres én gang per dokument, den online hentefasen, som lager embedding av spørringen, utfører ANN-søk og returnerer de K beste delene på millisekunder, samt strategier for trinnvis indeksering som holder vektorlageret oppdatert når dokumentene endres. Neste gang ser vi på hvordan du lager effektive utvidede prompter som bruker hentet kontekst på en god måte.

Gratis å komme i gang

Lær deg Python med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «RAG-arkitekturen: indeksering og uthenting» gratis?

Ja – hele teksten i «RAG-arkitekturen: indeksering og uthenting» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av AI Engineering Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i AI Engineering Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «RAG-arkitekturen: indeksering og uthenting»?

Kartlegg de to fasene i RAG: den offline-baserte indekseringsfasen, som deler opp, embedder og lagrer dokumenter, og den online-baserte uthentingsfasen, som finner relevant kontekst for hver spørring. Du øver på AI Engineering Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med AI Engineering Academy?

Ingen tidligere erfaring er nødvendig. AI Engineering Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.

Hvor lang tid tar leksjonen «RAG-arkitekturen: indeksering og uthenting»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne AI Engineering Academy-leksjonen?

Ja. Alle AI Engineering Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Problemet RAG løser
  2. RAG-arkitekturen: indeksering og uthenting
  3. Utforme den utvidede prompten
  4. RAG versus finjustering: når bør De bruke hva?
← Tilbake til AI Engineering Academy