Alapszintű RAG-megvalósítás: oktatóanyag lépésről lépésre

Ez a kezdőbarát oktatóanyag elmagyarázza, hogyan működik egy alapszintű RAG-megvalósítás, áttekinti a bevált gyakorlatokat, és végigvezet egy működő parancssori RAG-rendszer felépítésén Python, ChromaDB, valamint a Perplexity beágyazási és nyelvi modelljeinek segítségével.

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.

A RAG a modern, LLM-alapú infrastruktúra elengedhetetlen eleme. Mivel lehetővé teszi, hogy az MI-modellek külső adatokhoz férjenek hozzá, segít kezelni az olyan problémákat, mint a hallucinációk és a hiányos tanítóadatok. Emellett a vállalatok olyan belső tudásbázisokat is használhatnak, amelyek érzékeny vagy védett információkat tartalmaznak.

A RAG megvalósítása nagyon összetett lehet. Egy alapszintű RAG-rendszert azonban meglepően könnyű felépíteni. Kezdők számára egy egyszerű architektúra létrehozása megmutathatja, hogyan működik a RAG a gyakorlatban, és ugródeszkát jelenthet a további kísérletezéshez.

Ebben a cikkben felvázolunk egy alapszintű RAG-megvalósítást, áttekintjük a bevált gyakorlatokat, majd felépítünk egy működő rendszert Python, ChromaDB és két Perplexity-modell segítségével: az egyik a beágyazást, a másik a válaszgenerálást végzi.

Az alapszintű RAG-megvalósítás folyamata: átfogó áttekintés

Egy alapszintű RAG-rendszer három fő összetevőből áll: egy orkesztrátorból, egy információforrásból és egy LLM-ből. Egy tipikus munkafolyamatban a felhasználó beír egy promptot, az orkesztrátor pedig úgy módosítja, hogy keresni tudjon vele a csatlakoztatott információforrásokban. A visszakeresett információkat a rendszer ezután hozzáadja az eredeti bemeneti kontextushoz, és elküldi az LLM-nek, hogy az választ generálhasson.

A RAG-megvalósítás felépítésekor a következő lépésekkel kell számolnod:

  1. Indexelés és beágyazás: Ebben a szakaszban a dokumentumaid olyan információforrássá alakulnak, amelyben az orkesztrátor keresni tud. Ennek egyik leggyakoribb módja egy kereshető vektorindex létrehozása. A Perplexity egy erre a célra szolgáló modelljével alakítjuk beágyazásokká a dokumentumrészleteket. A beágyazásokat ezután a ChromaDB-ben, egy népszerű, nyílt forráskódú vektoradatbázisban tároljuk.
  2. Orkesztráció: Ez az az alkalmazás, amely a külső információforrás (esetünkben egy vektoradatbázis) és az LLM között helyezkedik el. Egy egyszerű Python-szkriptet hozunk létre, amely orkesztrátorként működik.
  3. Visszakeresés és generálás: Amikor a felhasználó kérdést tesz fel a csevegőfelületen, az orkesztrátor fogadja azt, vektoros keresést futtat, majd a kibővített bemenetet továbbítja az LLM-nek. Külön frontend készítése helyett a parancssoron keresztül kommunikálunk a Python-orkesztrátorunkkal.

Bevált gyakorlatok: mitől lesz hatékony egy RAG-megvalósítás?

A RAG-architektúrának többféle típusa létezik, és az egyes felhasználási esetek eltérő megközelítést igényelnek. Ugyanakkor számos bevált gyakorlat minden esetben érvényes, és pontos, költséghatékony, skálázható és karbantartható rendszereket eredményez.

Az alábbi bevált gyakorlatok a RAG-megvalósítás minden típusára érvényesek:

  • Gondoskodj a tiszta adatbetöltési folyamatról: A forrásadataidnak az indexelés előtt a lehető legtisztábbnak kell lenniük. Emellett a tartalmadhoz illő beágyazási modellt érdemes használnod.
  • Használj hibrid keresést: A legjobb visszakeresési módszerek – legyen szó vektoradatbázisban végzett keresésről, API-alapú webes keresésről, tudásgráf-bejárásról, kulcsszavas keresésről vagy más módszerekről – felhasználási esetenként eltérnek. A költségek minimalizálása szempontjából fontos a megfelelő kombináció kiválasztása.
  • Értékeld a rendszeredet: A megvalósítás után három alapvető mérőszám mentén érdemes értékelni: pontosság, relevancia és késleltetés. A pontosság a tényszerű helyességet jelenti, a relevancia azt, hogy a visszakeresett információ mennyire felel meg a felhasználói lekérdezéseknek, a késleltetés pedig azt, hogy milyen gyorsan történik az információk visszakeresése.
  • Kezeld biztonságosan az információkat: Ha a RAG-rendszered privát belső információkra támaszkodik, gondoskodj azok biztonságos tárolásáról és a rendszer felületéhez való hozzáférés szabályozásáról. Az API-kulcsokat soha ne oszd meg senkivel.

Alapszintű RAG-megvalósítás: oktatóanyag 7 lépésben

Ebben az oktatóanyagban egy parancssori alkalmazást készítünk, amely egyetlen dokumentummal kapcsolatos kérdésekre válaszol. Ehhez Pythont, ChromaDB-t és a Perplexity API-t használjuk.

A cél nem egy éles használatra kész RAG-rendszer felépítése. Ez inkább egy kezdőbarát oktatóanyag, amely azt szemlélteti, hogyan működik a gyakorlatban egy kezdetleges RAG-architektúra.

1. lépés: Szoftverek telepítése

Győződj meg róla, hogy az alábbiak telepítve vannak a számítógépeden, és rendelkezel Perplexity API-fiókkal:

  • Python: Ezen a programozási nyelven készítjük el az orkesztrátort, amely koordinálja az adatáramlást a beágyazási modell, a ChromaDB és a Perplexity között.
  • Visual Studio: Kódszerkesztőként a Visual Studiót használjuk. A telepítés után nyisd meg az Extensions panelt, és töltsd le a Python bővítményt.
  • Perplexity API-fiók: A Perplexity biztosítja a beágyazási modellt (amely vektoros beágyazásokká alakítja az adataidat) és azt az LLM-et is, amely a kibővített promptot feldolgozza. Ehhez nincs szükséged fizetős fiókra, bár némi kreditköltséggel jár.

2. lépés: Projektmappa létrehozása

Először hozz létre egy rag-project nevű mappát, és nyisd meg a VS Code-ban a File > Open Folder menüpont kiválasztásával. Ezután nyisd meg az integrált terminált a Terminal > New Terminal menüpont segítségével.

Virtuális Python-környezet létrehozásához írd be a következőt:

bash
py -3.12 -m venv .venv

Most négy Python-könyvtárat kell telepítened: perplexityai, chromadb, python-dotenv és numpy. A Perplexity SDK segítségével API-n keresztül csatlakozhatsz a Perplexity modelljeihez. A ChromaDB hozza létre a vektoradatbázist, és helyileg fut. A Python-dotenv biztonságosan betölti az API-kulcsaidat egy helyi fájlból, a NumPy pedig az adatbetöltési folyamathoz szükséges.

Telepítsd ezeket a következő paranccsal:

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

3. lépés: A fájlok előkészítése

Add hozzá az alábbi fájlokat a rag-project mappához (a projekt gyökeréhez) úgy, hogy az Explorer oldalsávon kiválasztod a New File… lehetőséget:

  • index.py: Ez az az alkalmazás, amely indexeli a dokumentumodat. További szöveg hozzáadásához újra lefuttathatod.
  • rag.py: Ez az a Python-alkalmazás, amely orkesztrátorként működik.
  • document.txt: Ez tartalmazza azt a dokumentációt, amelyet vektoradatbázissá alakítasz.
  • .env: Itt tárolod a Perplexity API-kulcsodat.

A .env fájlba írd be a következőt:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

Cseréld le a your_api_key_here szöveget a saját kulcsodra. Ehhez nyisd meg a Perplexity API Console kezdő irányítópultját, és válaszd a Create an API key lehetőséget. Ezt az API-kulcsot tartsd titokban.

Két modellt fogunk használni: a Perplexity pplx-embed-v1-0.6b modelljét, amellyel a dokumentációt beágyazzuk, mielőtt feltöltenénk a ChromaDB-be, valamint a GPT-5.6-Luna modellt (a Perplexity API-ján keresztül), amely a visszakeresett információkat dolgozza fel.

A document.txt fájlba írd be azt a szöveget, amelyet a RAG-folyamatod vissza fog keresni. Ha nincs kéznél megfelelő dokumentum, ehhez az oktatóanyaghoz használd William Blake Songs of Innocence and of Experience című művét a Project Gutenberg oldaláról. Ez egy rövid, könnyen érthető, közkincsnek számító szöveg.

4. lépés: Az indexelő program megírása

Másold be az alábbi kódot az index.py fájlba. Minden alcím röviden elmagyarázza, mit csinál az utána következő kódrészlet. Ez a program alakítja vektoros beágyazásokká a forrásanyagodat, és tárolja azokat egy ChromaDB-adatbázisban a Perplexity pplx-embed-v1-0.6b beágyazási modelljének és a NumPy könyvtárnak a segítségével.

A program a következőket végzi el:

  1. Megnyitja a document.txt fájlt.
  2. Egymással átfedő részletekre bontja a szöveget.
  3. Elküldi ezeket a részleteket a Perplexity pplx-embed-v1-0.6b beágyazási modelljének.
  4. Dekódolja és normalizálja a visszakapott vektorokat a NumPy, egy számítási függvényeket tartalmazó Python-könyvtár segítségével.
  5. Egy ChromaDB-adatbázisban tárolja a vektorokat és az eredeti részleteket (ez meg is jelenik a projektkönyvtáradban).

Úgy van beállítva, hogy ne indexelje újra a dokumentumodat minden futtatáskor. Ha bővíted a document.txt tartalmát, csak az új szöveget ágyazza be.

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

5. lépés: A RAG-rendszer orkesztrátorának megírása

Másold be az alábbi kódot a rag.py fájlba. Ez egy teljes, de minimális RAG-megvalósítás, amely a Perplexity SDK-t és a ChromaDB helyi vektoradatbázis-funkcióit használja.

A program futtatásakor a következők történnek:

  1. Csatlakozik a Perplexityhez és az új ChromaDB-adatbázisodhoz.
  2. Ellenőrzi, hogy az adatbázis tartalmaz-e indexelt dokumentumokat.
  3. Fogadja a felhasználó kérdését.
  4. Elküldi a kérdést a Perplexity beágyazási modelljének.
  5. Dekódolja és normalizálja a kapott beágyazást a NumPy segítségével.
  6. Megkeresi a ChromaDB-ben a három leginkább hasonló dokumentumrészletet.
  7. Összeállít egy promptot, amely a visszakeresett részleteket és a felhasználó kérdését tartalmazza.
  8. Elküldi a promptot a GPT-5.6 Luna modellnek a Perplexity Agent API-ján keresztül.
  9. Megjeleníti a választ, és várja a következő kérdést.
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)

6. lépés: Az alkalmazások futtatása

A document.txt tartalmának beágyazásához írd be a következőt a VS Code PowerShell-termináljában:

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

Ha a dokumentum indexelése sikeres volt, ehhez hasonló kimenetet kell látnod:

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

Most itt az ideje, hogy elindítsd a fő orkesztrátoralkalmazást. Írd be ezt a parancsot:

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

Sikeres futás esetén a következőt fogod látni:

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

Most már feltehetsz olyan kérdéseket, amelyek megválaszolásához a rendszer a létrehozott vektoradatbázisból merít, például:

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.

7. lépés: A felépített rendszer megértése

Az általad felépített rendszer azt a két alapvető folyamatot tartalmazza, amely minden RAG-architektúrában megtalálható: egy indexelési, más néven „adatbetöltési” folyamatot, valamint egy visszakeresési és generálási folyamatot.

Az alapszintű RAG-megvalósításod most már az alábbiakra képes:

  • Vektortárolás: A tudásfájlod alapján létrehozott beágyazásokat a helyi környezetedben lévő ChromaDB-adatbázis tárolja.
  • Inkrementális indexelés: A rendszer nem ágyazza be újra a változatlan dokumentumrészleteket, a dokumentumot pedig bármikor frissítheted.
  • Szemantikus visszakeresés: A ChromaDB vektoros hasonlóság alapján keresi vissza a részleteket (az orkesztrátor kezdeményezésére).
  • Megalapozott válaszgenerálás: Az LLM kontextusként megkapja a visszakeresett szövegrészeket, és beépíti őket a válaszba.
  • Független indexelés és lekérdezés: Kérdéseket tehetsz fel anélkül, hogy újra fel kellene dolgozni a tudásfájlodban lévő teljes szöveget.
  • API-alapú modellkapcsolat: Minden választ a GPT-5.6 Luna modell generál a Perplexityn keresztül, nem pedig egy a gépeden futó modell.

Következő lépések: hogyan fejlesztheted tovább ezt a megvalósítást?

Ennek az oktatóanyagnak a célja egy egyszerű, alapszintű megvalósítás felépítése. Bemutatja a modern rendszerekben megtalálható RAG-architektúra néhány alapvető összetevőjét. Ha tovább szeretnél lépni, többféleképpen is építhetsz rá.

Íme néhány ötlet a megvalósításod hatókörének bővítéséhez:

  • Kísérletezz a LangChainnel: A LangChain az egyik legnépszerűbb nyílt forráskódú orkesztrációs keretrendszer. Előre elkészített összetevőket kínál olyan alkalmazások létrehozásához, amelyek összetett RAG-folyamatokat képesek futtatni.
  • Bővítsd a keresési funkciókat: Kombináld a sűrű vektoros keresést lexikális (kulcsszavas) kereséssel, például a BM25-tel. Ha a Perplexity Search API-n keresztül webes keresést is hozzáadsz, a válaszokat naprakész adatokra is alapozhatod.
  • Adj hozzá egy újrarangsoroló modellt: Integrálj egy újrarangsoroló modellt, amely a kezdeti prompthoz viszonyítva rangsorolja a visszakeresett szövegrészeket, és kiválasztja közülük a legrelevánsabbakat, hogy azok bekerüljenek az LLM-nek átadott bemeneti kontextusba.
  • Építs adaptív RAG-rendszert: Az adaptív RAG egy LLM vagy egy osztályozó (az orkesztrátoron belüli összetevő) segítségével határozza meg, hogy egy moduláris RAG-rendszerben melyik visszakeresési módszert kell használni. Egy ilyen rendszer felépítése jó módja a kifinomultabb RAG-megvalósítások megismerésének.

Bár mindez összetettnek tűnhet, ne feledd, hogy az olyan elterjedt MI-platformok, mint a Perplexity, fejlett segítséget nyújthatnak a kódoláshoz. Kiválóan alkalmasak személyes projektekhez és kísérletezéshez, a költségek pedig viszonylag alacsonyan tarthatók.

Megjegyzés: Ez az oktatóanyag a GPT-5.6 Luna modellt használja. A modellek elérhetősége változhat, ezért előfordulhat, hogy frissítened kell az API-hívást.