Grundlæggende RAG-implementering: en trin-for-trin-vejledning

Denne begyndervenlige vejledning forklarer, hvordan en grundlæggende RAG-implementering fungerer, gennemgår bedste praksis og viser, hvordan du bygger et fungerende kommandolinjebaseret RAG-system med Python, ChromaDB og Perplexitys embedding- og sprogmodeller.

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 central komponent i moderne LLM-baseret infrastruktur. Ved at give AI-modeller adgang til eksterne data hjælper det med at løse problemer som hallucinationer og ufuldstændige træningsdata. Det betyder også, at virksomheder kan bruge interne vidensbaser, der indeholder følsomme eller proprietære oplysninger.

RAG-implementering kan være meget kompleks. Et grundlæggende RAG-system er dog overraskende nemt at bygge. For begyndere kan det at opbygge en simpel arkitektur give indblik i, hvordan RAG fungerer i praksis, og være et springbræt til videre eksperimenter.

I denne artikel skitserer vi en grundlæggende RAG-implementering, gennemgår bedste praksis og bygger derefter et fungerende system med Python, ChromaDB og to Perplexity-modeller til embedding og generering af svar.

Grundlæggende RAG-implementering: et overblik over processen

Et grundlæggende RAG-system har tre hovedkomponenter: en orkestrator, en informationskilde og en LLM. I en typisk arbejdsproces indtaster en bruger en prompt, og orkestratoren tilpasser den for at søge i de tilknyttede informationskilder. De hentede oplysninger føjes derefter til den oprindelige inputkontekst og sendes til LLM'en, så der kan genereres et svar.

Du skal tage højde for disse trin, når du bygger din RAG-implementering:

  1. Indeksering og embedding: I denne fase konverteres dine dokumenter til en informationskilde, som orkestratoren kan søge i. En af de mest almindelige måder at gøre det på er at bygge et søgbart vektorindeks. Vi bruger en dedikeret model fra Perplexity til at konvertere dine dokumentstykker til embeddings. Derefter gemmer vi dem i ChromaDB, en populær open source-vektordatabase.
  2. Orkestrering: Det er den applikation, der befinder sig mellem den eksterne informationskilde (i vores tilfælde en vektordatabase) og LLM'en. Vi opretter et simpelt Python-script, der fungerer som orkestrator.
  3. Hentning og generering: Når en bruger stiller et spørgsmål via chatgrænsefladen, modtager orkestratoren det, kører en vektorsøgning og sender det udvidede input til LLM'en. Vi bruger kommandolinjen til at kommunikere med vores Python-orkestrator i stedet for at bygge en dedikeret frontend.

Bedste praksis: Hvad kendetegner en effektiv RAG-implementering?

Der findes flere typer RAG-arkitektur, og forskellige anvendelsestilfælde kræver forskellige tilgange. Når det er sagt, gælder en række principper for bedste praksis i alle tilfælde, og de sikrer nøjagtige, omkostningseffektive, skalerbare og vedligeholdelsesvenlige systemer.

Følgende bedste praksis gælder for alle typer RAG-implementering:

  • Sørg for en ren indlæsningspipeline: Dine kildedata bør være så rene som muligt før indeksering. Du bør også bruge en embeddingmodel, der passer til dit indhold.
  • Brug hybridsøgning: De bedste hentningsmetoder – hvad enten det er søgning i vektordatabaser, API-baseret websøgning, gennemløb af vidensgrafer, søgeordssøgning eller andre metoder – varierer afhængigt af anvendelsestilfældet. Det er vigtigt at vælge den rette kombination for at minimere omkostningerne.
  • Evaluer dit system: Efter implementeringen bør du evaluere ud fra tre centrale mål: nøjagtighed, relevans og latens. Nøjagtighed handler om faktuel korrekthed, relevans om, hvor anvendelige de hentede oplysninger er i forhold til brugernes forespørgsler, og latens om, hvor hurtigt oplysningerne hentes.
  • Behandl oplysninger sikkert: Hvis dit RAG-system trækker på private interne oplysninger, skal du sikre, at de opbevares sikkert, og at adgangen til systemets brugerflade styres. API-nøgler bør aldrig deles.

Grundlæggende RAG-implementering: vejledning i 7 trin

I denne vejledning bygger vi en kommandolinjeapplikation, der besvarer spørgsmål om et enkelt dokument. Vi bruger Python, ChromaDB og Perplexity API.

Målet er ikke at bygge et produktionsklart RAG-system. Det er snarere en begyndervenlig vejledning, der skal illustrere, hvordan en enkel RAG-arkitektur fungerer i praksis.

Trin 1: Installer software

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

  • Python: Dette er det programmeringssprog, vi bruger til at bygge orkestratoren, der koordinerer dataflowet mellem embeddingmodellen, ChromaDB og Perplexity.
  • Visual Studio: Vi bruger Visual Studio som kodeeditor. Når det er installeret, skal du åbne panelet Extensions og downloade udvidelsen Python.
  • Perplexity API-konto: Perplexity leverer både embeddingmodellen (som omdanner dine data til vektor-embeddings) og den LLM, der behandler den udvidede prompt. Du behøver ikke en betalt konto for at gøre dette, selvom det vil koste nogle få kreditter.

Trin 2: Opret en projektmappe

Start med at oprette en mappe med navnet rag-project, og åbn den i VS Code ved at vælge File > Open Folder. Åbn derefter den integrerede terminal via Terminal > New Terminal.

Indtast følgende for at oprette et virtuelt Python-miljø:

bash
py -3.12 -m venv .venv

Du skal nu installere fire Python-biblioteker: perplexityai, chromadb, python-dotenv og numpy. Med Perplexity SDK kan du oprette forbindelse til Perplexitys modeller via API. ChromaDB bygger vektordatabasen og kører lokalt. Python-dotenv indlæser dine API-nøgler sikkert fra en lokal fil, og NumPy skal bruges under indlæsningsprocessen.

Installer dem med følgende kommando:

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

Trin 3: Konfigurer dine filer

Tilføj følgende filer til rag-project (projektets rodmappe) ved at vælge New File… i sidepanelet Explorer:

  • index.py: Dette er applikationen, der indekserer dit dokument. Du kan køre den igen for at tilføje mere tekst.
  • rag.py: Dette er Python-applikationen, der fungerer som orkestrator.
  • document.txt: Denne fil indeholder den dokumentation, du konverterer til en vektordatabase.
  • .env: Det er her, du opbevarer din Perplexity API-nøgle.

Indtast følgende i .env:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

Erstat your_api_key_here med din faktiske nøgle. For at få den skal du gå til startdashboardet i Perplexity API Console og vælge Create an API key. Hold denne API-nøgle privat.

Vi bruger to modeller: Perplexitys pplx-embed-v1-0.6b til at lave embeddings af vores dokumentation, før den uploades til ChromaDB, og GPT-5.6-Luna (tilgået via Perplexitys API), som behandler de hentede oplysninger.

I document.txt skal du tilføje den tekst, som din RAG-pipeline skal hente. Hvis du ikke har et dokument ved hånden, kan du bruge William Blakes Songs of Innocence and of Experience fra Project Gutenberg til denne vejledning. Det er en kort, letforståelig tekst, som ikke længere er beskyttet af ophavsret.

Trin 4: Skriv indekseringsprogrammet

Kopiér koden nedenfor, og indsæt den i index.py. Hver underoverskrift giver en kort forklaring på, hvad det efterfølgende kodesegment gør. Dette er programmet, der konverterer dit kildemateriale til vektor-embeddings og gemmer dem i en ChromaDB-database ved hjælp af Perplexitys embeddingmodel, pplx-embed-v1-0.6b, og NumPy-biblioteket.

Programmet gør følgende:

  1. Åbner document.txt.
  2. Opdeler teksten i overlappende tekststykker.
  3. Sender disse tekststykker til Perplexitys embeddingmodel pplx-embed-v1-0.6b.
  4. Afkoder og normaliserer de returnerede vektorer med NumPy, et Python-bibliotek med beregningsfunktioner.
  5. Gemmer vektorerne og de oprindelige tekststykker i en ChromaDB-database (du vil se den dukke op i din projektmappe).

Programmet er sat op, så det ikke genindekserer dit dokument, hver gang du kører det. Hvis du tilføjer flere oplysninger i document.txt, laver det kun embeddings af den nye tekst.

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())

Trin 5. Skriv orkestratoren til RAG-systemet

Kopiér koden nedenfor, og indsæt den i rag.py. Det er en komplet, men minimal RAG-implementering, der bruger Perplexity SDK og ChromaDB's lokale vektordatabasefunktionalitet.

Her er, hvad der sker, når du kører programmet:

  1. Opretter forbindelse til Perplexity og din nye ChromaDB-database.
  2. Kontrollerer, at databasen indeholder indekserede dokumenter.
  3. Modtager et spørgsmål fra brugeren.
  4. Sender spørgsmålet til Perplexitys embeddingmodel.
  5. Afkoder og normaliserer den resulterende embedding med NumPy.
  6. Søger i ChromaDB efter de tre dokumentstykker, der ligner mest.
  7. Opbygger en prompt, der indeholder de hentede tekststykker og brugerens spørgsmål.
  8. Sender prompten til GPT-5.6 Luna via Perplexitys Agent API.
  9. Viser svaret og venter på et nyt spørgsmå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)

Trin 6: Kør applikationerne

Indtast følgende i PowerShell-terminalen i VS Code for at lave embeddings af indholdet i document.txt:

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

Hvis dit dokument er blevet indekseret korrekt, bør du se et output, der ligner dette:

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

Nu er det tid til at køre den primære orkestratorapplikation. Indtast denne kommando:

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

Hvis den kører korrekt, vil du se følgende:

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

Du kan nu stille spørgsmål, der trækker på den vektordatabase, du har oprettet, 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.

Trin 7: Forstå, hvad du har bygget

Det system, du har bygget, indeholder de to centrale pipelines, som findes i enhver RAG-arkitektur: en indekseringspipeline, også kaldet »ingestion«-pipeline, og en pipeline til hentning og generering.

Din grundlæggende RAG-implementering gør nu alt følgende:

  • Vektorlagring: Embeddings baseret på din vidensfil gemmes i din ChromaDB-database i dit lokale miljø.
  • Inkrementel indeksering: Der laves ikke embeddings af uændrede dokumentstykker igen, og du kan opdatere dokumentet, når du vil.
  • Semantisk hentning: ChromaDB henter tekststykker baseret på vektorlighed (igangsat af orkestratoren).
  • Forankret svargenerering: LLM'en modtager de hentede passager som kontekst og indarbejder dem i et svar.
  • Uafhængig indeksering og forespørgsel: Du kan stille spørgsmål uden at genbehandle al teksten i din vidensfil.
  • API-baseret modelforbindelse: Alle svar genereres med GPT-5.6 Luna via Perplexity i stedet for med en model, der kører på din maskine.

Næste skridt: Sådan videreudvikler du implementeringen

Formålet med denne vejledning er at bygge en enkel, grundlæggende implementering. Den dækker nogle af de centrale RAG-arkitekturkomponenter, der findes i moderne systemer. Hvis du vil gå videre, kan du bygge videre på den på forskellige måder.

Her er nogle idéer til at udvide omfanget af din implementering:

  • Eksperimentér med LangChain: LangChain er et af de mest populære open source-frameworks til orkestrering. Det indeholder færdigbyggede komponenter til at bygge apps, der kan køre komplekse RAG-pipelines.
  • Udvid søgefunktionaliteten: Kombiner tæt vektorsøgning med leksikalsk søgning (søgeordssøgning) som BM25. Ved også at tilføje websøgning via Perplexity Search API kan du forankre svarene i aktuelle data.
  • Tilføj en reranking-model: Integrer en reranking-model, der rangerer de hentede passager i forhold til den oprindelige prompt og udvælger de mest relevante, så de kan indgå i den inputkontekst, som LLM'en får.
  • Byg et adaptivt RAG-system: Adaptiv RAG bruger enten en LLM eller en klassifikator (en komponent i orkestratoren) til at afgøre, hvilken hentningsmetode der skal bruges i et modulært RAG-system. At bygge et sådant system er en god måde at udforske mere avancerede RAG-implementeringer på.

Selvom alt dette kan lyde komplekst, skal du huske, at udbredte AI-platforme som Perplexity kan give avanceret hjælp til kodning. De er fremragende til personlige projekter og eksperimenter, og omkostningerne kan holdes relativt lave.

Bemærk: Denne vejledning bruger GPT-5.6 Luna. Udvalget af tilgængelige modeller kan ændre sig, så du skal muligvis opdatere API-kaldet.