Enkel RAG-implementering: en trinnvis veiledning

Denne nybegynnervennlige veiledningen forklarer hvordan en enkel RAG-implementering fungerer, går gjennom beste praksis og viser trinn for trinn hvordan du bygger et fungerende RAG-system for kommandolinjen med Python, ChromaDB og Perplexitys embedding- og språkmodeller.

Basic RAG implementation process diagram: the interface sends input to the orchestrator, which retrieves data from a vector database, builds the input context for the LLM, and returns the output to the interface.

RAG er en essensiell komponent i moderne LLM-basert infrastruktur. Ved å gi AI-modeller tilgang til eksterne data bidrar RAG til å løse problemer som hallusinasjoner og ufullstendige treningsdata. Det betyr også at bedrifter kan bruke interne kunnskapsbaser som inneholder sensitiv eller proprietær informasjon.

RAG-implementering kan være svært kompleks. Et enkelt RAG-system er likevel overraskende lett å bygge. For nybegynnere kan det å lage en enkel arkitektur vise hvordan RAG fungerer i praksis, og gi et springbrett for videre eksperimentering.

I denne artikkelen skisserer vi en enkel RAG-implementering, går gjennom beste praksis og bygger deretter et fungerende system med Python, ChromaDB og to Perplexity-modeller for embedding og svargenerering.

Prosessen bak en enkel RAG-implementering: en overordnet gjennomgang

Et enkelt RAG-system har tre hovedkomponenter: en orkestrator, en informasjonskilde og en LLM. I en typisk arbeidsflyt skriver brukeren inn en prompt, og orkestratoren tilpasser den for å søke i tilkoblede informasjonskilder. Den innhentede informasjonen legges deretter til i den opprinnelige inndatakonteksten og sendes til LLM-en slik at et svar kan genereres.

Du må ta høyde for disse trinnene når du bygger RAG-implementeringen din:

  1. Indeksering og embedding: I dette trinnet konverteres dokumentene dine til en informasjonskilde som orkestratoren kan søke i. En av de vanligste måtene å gjøre dette på er å bygge en søkbar vektorindeks. Vi bruker en dedikert modell fra Perplexity til å konvertere dokumentbitene dine til embeddinger. Deretter lagrer vi embeddingene med ChromaDB, en populær vektordatabase med åpen kildekode.
  2. Orkestrering: Dette er applikasjonen som ligger mellom den eksterne informasjonskilden (i vårt tilfelle en vektordatabase) og LLM-en. Vi lager et enkelt Python-skript som fungerer som orkestrator.
  3. Innhenting og generering: Når en bruker stiller et spørsmål via chattegrensesnittet, tar orkestratoren imot det, kjører et vektorsøk og sender de utvidede inndataene til LLM-en. Vi bruker kommandolinjen til å kommunisere med Python-orkestratoren i stedet for å bygge en egen frontend.

Beste praksis: Hva kjennetegner en effektiv RAG-implementering?

Det finnes flere typer RAG-arkitektur, og ulike brukstilfeller krever ulike tilnærminger. Likevel gjelder en rekke anbefalte fremgangsmåter uansett, og de bidrar til nøyaktige, kostnadseffektive, skalerbare og vedlikeholdbare systemer.

Følgende beste praksis gjelder for alle typer RAG-implementeringer:

  • Sørg for en ren inntakspipeline: Kildedataene dine bør være så rene som mulig før indeksering. Du bør også bruke en embeddingmodell som passer til innholdet ditt.
  • Bruk hybridsøk: Hvilke innhentingsmetoder som fungerer best – enten det er søk i vektordatabaser, API-basert nettsøk, traversering av kunnskapsgrafer, nøkkelordsøk eller andre metoder – varierer fra brukstilfelle til brukstilfelle. Å velge riktig kombinasjon er viktig for å holde kostnadene nede.
  • Evaluer systemet ditt: Etter implementeringen bør du evaluere systemet ut fra tre sentrale mål: nøyaktighet, relevans og latens. Nøyaktighet handler om faktisk korrekthet, relevans om hvor godt den innhentede informasjonen passer til brukernes spørsmål, og latens om hvor raskt informasjonen hentes.
  • Håndter informasjon sikkert: Hvis RAG-systemet ditt bruker privat intern informasjon, må du sørge for at den lagres sikkert, og at tilgangen til systemgrensesnittet styres. API-nøkler skal aldri deles.

Enkel RAG-implementering: veiledning i 7 trinn

I denne veiledningen bygger vi en kommandolinjeapplikasjon som svarer på spørsmål om ett enkelt dokument. Vi bruker Python, ChromaDB og Perplexity API.

Målet er ikke å bygge et produksjonsklart RAG-system. Dette er snarere en nybegynnervennlig veiledning som skal vise hvordan en enkel RAG-arkitektur fungerer i praksis.

Trinn 1: Installer programvare

Sørg for at følgende er installert på datamaskinen din, og at du har en Perplexity API-konto:

  • Python: Dette er programmeringsspråket vi bruker til å bygge orkestratoren, som skal koordinere dataflyten mellom embeddingmodellen, ChromaDB og Perplexity.
  • Visual Studio: Vi bruker Visual Studio som koderedigeringsprogram. Når det er installert, åpner du Extensions-panelet og laster ned Python-utvidelsen.
  • Perplexity API-konto: Perplexity leverer både embeddingmodellen (som gjør dataene dine om til vektorembeddinger) og LLM-en som skal behandle den utvidede prompten. Du trenger ikke en betalt konto for dette, men det vil gå med en liten mengde kreditter.

Trinn 2: Opprett en prosjektmappe

Start med å opprette en mappe med navnet rag-project og åpne den i VS Code ved å velge File > Open Folder. Åpne deretter den integrerte terminalen via Terminal > New Terminal.

Skriv inn følgende for å opprette et virtuelt Python-miljø:

bash
py -3.12 -m venv .venv

Nå må du installere fire Python-biblioteker: perplexityai, chromadb, python-dotenv og numpy. Med Perplexity SDK kan du koble deg til Perplexitys modeller via API. ChromaDB bygger vektordatabasen og kjører lokalt. Python-dotenv laster API-nøklene dine sikkert fra en lokal fil, og NumPy trengs under inntaksprosessen.

Installer disse med følgende kommando:

bash
.\.venv\Scripts\python.exe -m pip install perplexityai chromadb python-dotenv numpy

Trinn 3: Konfigurer filene dine

Legg til følgende filer i rag-project (prosjektroten) ved å velge New File… i Explorer-sidefeltet:

  • index.py: Dette er applikasjonen som skal indeksere dokumentet ditt. Du kan kjøre den på nytt for å legge til mer tekst.
  • rag.py: Dette er Python-applikasjonen som skal fungere som orkestrator.
  • document.txt: Denne filen skal inneholde dokumentasjonen du skal konvertere til en vektordatabase.
  • .env: Her oppbevarer du API-nøkkelen din for Perplexity.

Skriv inn følgende i .env:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

Erstatt your_api_key_here med den faktiske nøkkelen din. For å få en nøkkel går du til startsiden i Perplexity API Console og velger Create an API key. Hold denne API-nøkkelen privat.

Vi bruker to modeller: Perplexitys pplx-embed-v1-0.6b til å lage embeddinger av dokumentasjonen før den lastes opp til ChromaDB, og GPT-5.6-Luna (via Perplexitys API), som skal behandle den innhentede informasjonen.

I document.txt legger du inn teksten som RAG-pipelinen skal hente informasjon fra. Hvis du ikke har et dokument for hånden, kan du bruke William Blakes Songs of Innocence and of Experience fra Project Gutenberg i denne veiledningen. Det er en kort og lettfattelig tekst som har falt i det fri.

Trinn 4: Skriv indekseringsprogrammet

Kopier og lim inn koden nedenfor i index.py. Hver underoverskrift gir en kort forklaring av hva det påfølgende kodesegmentet gjør. Dette er programmet som konverterer kildematerialet ditt til vektorembeddinger og lagrer dem i en ChromaDB-database ved hjelp av Perplexitys embeddingmodell, pplx-embed-v1-0.6b, og NumPy-biblioteket.

Dette gjør programmet:

  1. Åpner document.txt.
  2. Deler teksten opp i overlappende biter.
  3. Sender disse bitene til Perplexitys embeddingmodell pplx-embed-v1-0.6b.
  4. Dekoder og normaliserer de returnerte vektorene med NumPy, et Python-bibliotek med beregningsfunksjoner.
  5. Lagrer vektorene og de opprinnelige bitene i en ChromaDB-database (du vil se den dukke opp i prosjektmappen din).

Programmet er satt opp slik at det ikke indekserer dokumentet ditt på nytt hver gang du kjører det. Hvis du legger til mer informasjon i document.txt, lager det bare embeddinger av den nye teksten.

python
from pathlib import Path from collections import defaultdict, deque from hashlib import sha256 import base64 import numpy as np import chromadb from dotenv import load_dotenv from perplexity import Perplexity # 1. Connect to Perplexity and ChromaDB load_dotenv() ai = Perplexity() db = chromadb.PersistentClient(path="./rag_database_perplexity") collection = db.get_or_create_collection( name="documents", metadata={"hnsw:space": "cosine"} ) # Convert Perplexity's encoded embeddings into numerical vectors def decode_embedding(encoded): vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32) norm = np.linalg.norm(vector) if norm == 0: raise ValueError("Received a zero-length embedding vector.") return (vector / norm).tolist() # 2. Read the document and split it into chunks text = Path("document.txt").read_text(encoding="utf-8") words = text.split() chunk_size = 180 overlap = 30 step = chunk_size - overlap chunks = [" ".join(words[i:i + chunk_size]) for i in range(0, len(words), step)] if not chunks: raise ValueError("document.txt is empty.") # 3. Retrieve the chunks already stored in ChromaDB existing = collection.get(include=["documents"]) stored_chunks = defaultdict(deque) existing_ids = set(existing["ids"]) for chunk_id, document in zip(existing["ids"], existing["documents"]): stored_chunks[document].append(chunk_id) # 4. Identify unchanged and new chunks new_chunks = [] new_ids = [] kept_ids = set() unchanged = 0 for chunk in chunks: if stored_chunks[chunk]: chunk_id = stored_chunks[chunk].popleft() kept_ids.add(chunk_id) unchanged += 1 else: chunk_hash = sha256(chunk.encode("utf-8")).hexdigest() chunk_id = f"chunk-{chunk_hash}" suffix = 1 while chunk_id in existing_ids or chunk_id in kept_ids or chunk_id in new_ids: chunk_id = f"chunk-{chunk_hash}-{suffix}" suffix += 1 new_chunks.append(chunk) new_ids.append(chunk_id) kept_ids.add(chunk_id) # 5. Generate embeddings in batches of up to 100 chunks batch_size = 100 for i in range(0, len(new_chunks), batch_size): batch_chunks = new_chunks[i:i + batch_size] batch_ids = new_ids[i:i + batch_size] embeddings = ai.embeddings.create( model="pplx-embed-v1-0.6b", input=batch_chunks, encoding_format="base64_int8" ) vectors = [decode_embedding(item.embedding) for item in embeddings.data] # 6. Store each batch in ChromaDB collection.upsert( ids=batch_ids, documents=batch_chunks, embeddings=vectors ) print(f"Indexed batch {(i // batch_size) + 1}") # 7. Remove outdated chunks old_ids = [chunk_id for chunk_id in existing["ids"] if chunk_id not in kept_ids] if old_ids: collection.delete(ids=old_ids) # 8. Display indexing results print("Indexing complete!") print("Unchanged chunks:", unchanged) print("New or modified chunks:", len(new_chunks)) print("Removed chunks:", len(old_ids)) print("Total stored chunks:", collection.count())

Trinn 5. Skriv orkestratoren for RAG-systemet

Kopier og lim inn koden nedenfor i rag.py. Dette er en komplett, men minimal RAG-implementering som bruker Perplexity SDK og ChromaDBs funksjonalitet for lokal vektordatabase.

Dette skjer når du kjører programmet:

  1. Kobler til Perplexity og den nye ChromaDB-databasen din.
  2. Kontrollerer at databasen inneholder indekserte dokumenter.
  3. Tar imot et spørsmål fra brukeren.
  4. Sender spørsmålet til Perplexitys embeddingmodell.
  5. Dekoder og normaliserer den resulterende embeddingen med NumPy.
  6. Søker i ChromaDB etter de tre dokumentbitene som ligner mest.
  7. Bygger en prompt som inneholder de innhentede bitene og brukerens spørsmål.
  8. Sender prompten til GPT-5.6 Luna via Perplexitys Agent API.
  9. Viser svaret og venter på neste spørsmål.
python
import base64 import numpy as np import chromadb from dotenv import load_dotenv from perplexity import Perplexity # 1. Connect to Perplexity and the existing ChromaDB database load_dotenv() ai = Perplexity() db = chromadb.PersistentClient(path="./rag_database_perplexity") collection = db.get_collection("documents") # Convert Perplexity's encoded embeddings into numerical vectors def decode_embedding(encoded): vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32) norm = np.linalg.norm(vector) if norm == 0: raise ValueError("Received a zero-length embedding vector.") return (vector / norm).tolist() # 2. Check that the database contains indexed documents count = collection.count() if count == 0: raise ValueError("The database is empty. Run index.py first.") print("Connected to existing ChromaDB database.") print("Stored chunks:", count) # 3. Accept questions from the user while True: question = input("\nAsk a question (or type exit): ") if question.lower().strip() == "exit": break if not question.strip(): continue # 4. Convert the question into a Perplexity embedding question_response = ai.embeddings.create( model="pplx-embed-v1-0.6b", input=[question], encoding_format="base64_int8" ) question_vector = decode_embedding(question_response.data[0].embedding) # 5. Retrieve the most similar document chunks from ChromaDB results = collection.query( query_embeddings=[question_vector], n_results=min(3, count) ) context = "\n\n".join(results["documents"][0]) # 6. Construct a prompt and send it to the fixed LLM response = ai.responses.create( model="openai/gpt-5.6-luna", instructions=( "Answer using only the supplied document excerpts. " "Do not use external information. " "If the answer is not present, say you don't know." ), input=f"Document excerpts:\n{context}\n\nQuestion: {question}" ) # 7. Display the answer print("\nAnswer:\n", response.output_text)

Trinn 6: Kjør applikasjonene

Skriv inn følgende i PowerShell-terminalen i VS Code for å lage embeddinger av innholdet i document.txt:

bash
.\.venv\Scripts\python.exe index.py

Hvis dokumentet er indeksert uten problemer, bør du se utdata som ligner på dette:

plaintext
Indexing complete! Unchanged chunks: 0 New or modified chunks: 58 Removed chunks: 0 Total stored chunks: 58

Nå er det på tide å kjøre hovedapplikasjonen, orkestratoren. Skriv inn denne kommandoen:

bash
.\.venv\Scripts\python.exe rag.py

Hvis den kjører som den skal, ser du følgende:

plaintext
Connected to existing ChromaDB database. Stored chunks: 58 Ask a question (or type exit):

Nå kan du stille spørsmål som henter svar fra vektordatabasen du har laget, for eksempel:

plaintext
Ask a question (or type exit): What animals does William Blake talk about? Answer: The excerpts mention sheep, lambs, birds, beasts, wolves, tigers, and a lion.

Trinn 7: Forstå hva du har bygget

Systemet du har bygget, inneholder de to kjernepipelinene som finnes i alle RAG-arkitekturer: en indekserings- eller «inntaks»-pipeline og en pipeline for innhenting og generering.

Den enkle RAG-implementeringen din gjør nå alt dette:

  • Vektorlagring: Embeddinger basert på kunnskapsfilen din lagres i ChromaDB-databasen i ditt lokale miljø.
  • Inkrementell indeksering: Det lages ikke nye embeddinger av uendrede dokumentbiter, og du kan oppdatere dokumentet når du vil.
  • Semantisk innhenting: ChromaDB henter biter basert på vektorlikhet (initiert av orkestratoren).
  • Forankret svargenerering: LLM-en mottar innhentede tekstutdrag som kontekst og bruker dem i svaret.
  • Uavhengig indeksering og spørring: Du kan stille spørsmål uten å behandle all teksten i kunnskapsfilen din på nytt.
  • API-basert modelltilkobling: Alle svar genereres med GPT-5.6 Luna via Perplexity i stedet for med en modell som kjører på maskinen din.

Neste steg: Slik videreutvikler du implementeringen

Målet med denne veiledningen er å bygge en enkel og grunnleggende implementering. Den dekker noen av de sentrale komponentene i RAG-arkitekturen som finnes i moderne systemer. Hvis du vil gå lenger, kan du bygge videre på den på flere måter.

Her er noen ideer til hvordan du kan utvide omfanget av implementeringen din:

  • Eksperimenter med LangChain: LangChain er et av de mest populære orkestreringsrammeverkene med åpen kildekode. Det inneholder ferdigbygde komponenter for å bygge apper som kan kjøre komplekse RAG-pipelines.
  • Utvid søkefunksjonaliteten: Kombiner tett vektorsøk med leksikalsk søk (nøkkelordsøk), for eksempel BM25. Ved å legge til nettsøk via Perplexity Search API kan du også forankre svarene i oppdaterte data.
  • Legg til en omrangeringsmodell: Integrer en omrangeringsmodell som rangerer de innhentede tekstutdragene opp mot den opprinnelige prompten og velger ut de mest relevante for inndatakonteksten som sendes til LLM-en.
  • Bygg et adaptivt RAG-system: Adaptiv RAG bruker enten en LLM eller en klassifikator (en komponent i orkestratoren) til å avgjøre hvilken innhentingsmetode som skal brukes i et modulært RAG-system. Å bygge et slikt system er en god måte å utforske mer avanserte RAG-implementeringer på.

Selv om alt dette kan høres komplisert ut, er det verdt å huske at utbredte AI-plattformer som Perplexity kan gi avansert hjelp med koding. De egner seg utmerket til personlige prosjekter og eksperimentering, og kostnadene kan holdes relativt lave.

Merk: Denne veiledningen bruker GPT-5.6 Luna. Modelltilgjengeligheten kan endre seg, så du må kanskje oppdatere API-kallet.