Implementare RAG de bază: tutorial pas cu pas

Acest tutorial pentru începători explică modul în care funcționează o implementare RAG de bază, prezintă cele mai bune practici și vă ghidează în construirea unui sistem RAG funcțional în linie de comandă cu Python, ChromaDB și modelele de embedding și de limbaj ale Perplexity.

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 este o componentă esențială a infrastructurii moderne bazate pe LLM-uri. Permițând modelelor AI să acceseze date externe, contribuie la rezolvarea unor probleme precum halucinațiile și datele de antrenare incomplete. În plus, companiile pot folosi baze de cunoștințe interne care conțin informații sensibile sau proprietare.

Implementarea RAG poate fi foarte complexă. Totuși, un sistem RAG de bază este surprinzător de ușor de construit. Pentru începători, crearea unei arhitecturi simple poate clarifica modul în care funcționează RAG în practică și poate fi o rampă de lansare pentru experimente ulterioare.

În acest articol, vom schița o implementare RAG de bază, vom trece în revistă cele mai bune practici și apoi vom construi un sistem funcțional folosind Python, ChromaDB și două modele Perplexity pentru generarea embedding-urilor și a răspunsurilor.

Procesul de implementare RAG de bază: o prezentare generală

Un sistem RAG de bază are trei componente principale: un orchestrator, o sursă de informații și un LLM. Într-un flux de lucru tipic, utilizatorul introduce un prompt, iar orchestratorul îl modifică pentru a căuta în sursele de informații conectate. Informațiile regăsite sunt apoi adăugate la contextul de intrare inițial și trimise către LLM, pentru ca acesta să genereze un răspuns.

Va trebui să țineți cont de acești pași atunci când construiți implementarea RAG:

  1. Indexare și embedding: În această etapă, documentele dumneavoastră sunt convertite într-o sursă de informații în care orchestratorul poate căuta. Una dintre cele mai frecvente metode este construirea unui index vectorial care permite căutarea. Vom folosi un model dedicat de la Perplexity pentru a converti fragmentele documentelor în embedding-uri. Apoi vom stoca embedding-urile cu ChromaDB, o bază de date vectorială open-source populară.
  2. Orchestrare: Aceasta este aplicația care se află între sursa externă de informații (în cazul nostru, o bază de date vectorială) și LLM. Vom crea un script Python simplu care să joace rolul de orchestrator.
  3. Regăsire și generare: Când un utilizator pune o întrebare prin interfața de chat, orchestratorul o preia, rulează o căutare vectorială și transmite LLM-ului datele de intrare extinse. În loc să construim un frontend dedicat, vom folosi linia de comandă pentru a comunica cu orchestratorul nostru Python.

Cele mai bune practici: ce face ca o implementare RAG să fie eficientă?

Există mai multe tipuri de arhitectură RAG, iar fiecare caz de utilizare necesită o abordare diferită. Cu toate acestea, o serie de bune practici se aplică în toate cazurile și asigură sisteme precise, rentabile, scalabile și ușor de întreținut.

Următoarele bune practici se aplică tuturor tipurilor de implementare RAG:

  • Asigurați un pipeline de ingestie curat: Datele sursă ar trebui să fie cât mai curate înainte de indexare. De asemenea, ar trebui să folosiți un model de embedding potrivit pentru conținutul dumneavoastră.
  • Folosiți căutarea hibridă: Cele mai bune metode de regăsire, fie că este vorba de căutarea într-o bază de date vectorială, căutarea web prin API, parcurgerea unui graf de cunoștințe, căutarea după cuvinte-cheie sau alte metode, variază în funcție de cazul de utilizare. Alegerea combinației potrivite este importantă pentru reducerea costurilor.
  • Evaluați sistemul: După implementare, ar trebui să evaluați sistemul în funcție de trei criterii de bază: acuratețea, relevanța și latența. Acuratețea se referă la corectitudinea faptică, relevanța la măsura în care informațiile regăsite se potrivesc interogărilor utilizatorilor, iar latența la viteza cu care sunt regăsite informațiile.
  • Gestionați informațiile în mod securizat: Dacă sistemul RAG se bazează pe informații interne private, asigurați-vă că acestea sunt stocate în mod securizat și că accesul la interfața sistemului este gestionat. Cheile API nu trebuie partajate niciodată.

Implementare RAG de bază: tutorial în 7 pași

În acest tutorial, vom construi o aplicație în linie de comandă care răspunde la întrebări despre un singur document. Vom folosi Python, ChromaDB și API-ul Perplexity.

Scopul nu este să construim un sistem RAG pregătit pentru producție. Este, mai degrabă, un tutorial accesibil începătorilor, conceput pentru a ilustra modul în care funcționează în practică o arhitectură RAG rudimentară.

Pasul 1: Instalați software-ul

Asigurați-vă că aveți instalate pe computer următoarele și că aveți un cont pentru API-ul Perplexity:

  • Python: Acesta este limbajul de programare pe care îl vom folosi pentru a construi orchestratorul care va coordona fluxul de date dintre modelul de embedding, ChromaDB și Perplexity.
  • Visual Studio: Vom folosi Visual Studio ca editor de cod. După instalare, deschideți panoul Extensions și descărcați extensia Python.
  • Cont pentru API-ul Perplexity: Perplexity va furniza atât modelul de embedding (care transformă datele în embedding-uri vectoriale), cât și LLM-ul care va procesa promptul extins. Nu aveți nevoie de un cont plătit pentru asta, deși veți consuma un număr mic de credite.

Pasul 2: Creați un folder pentru proiect

Începeți prin a crea un folder numit rag-project și a-l deschide în VS Code, selectând File > Open Folder. Apoi deschideți terminalul integrat din Terminal > New Terminal.

Introduceți următoarea comandă pentru a crea un mediu virtual Python:

bash
py -3.12 -m venv .venv

Acum trebuie să instalați patru biblioteci Python: perplexityai, chromadb, python-dotenv și numpy. SDK-ul Perplexity vă permite să vă conectați la modelele Perplexity prin API. ChromaDB construiește baza de date vectorială și va rula local. Python-dotenv încarcă în siguranță cheile API dintr-un fișier local, iar NumPy este necesar în timpul procesului de ingestie.

Instalați-le cu următoarea comandă:

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

Pasul 3: Configurați fișierele

Adăugați următoarele fișiere în rag-project (rădăcina proiectului) selectând New File… în bara laterală Explorer:

  • index.py: Aceasta este aplicația care va indexa documentul. O puteți rula din nou pentru a adăuga text suplimentar.
  • rag.py: Aceasta este aplicația Python care va avea rolul de orchestrator.
  • document.txt: Acesta va conține documentația pe care o veți converti într-o bază de date vectorială.
  • .env: Aici veți păstra cheia API Perplexity.

În .env, introduceți următoarele:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

Înlocuiți your_api_key_here cu cheia dumneavoastră reală. Pentru aceasta, accesați tabloul de bord principal din Perplexity API Console și selectați Create an API key. Păstrați această cheie API confidențială.

Vom folosi două modele: pplx-embed-v1-0.6b de la Perplexity, pentru a genera embedding-urile documentației înainte de încărcarea în ChromaDB, și GPT-5.6-Luna (accesat prin API-ul Perplexity), care va procesa informațiile regăsite.

În document.txt, adăugați textul pe care îl va regăsi pipeline-ul RAG. Dacă nu aveți un document la îndemână, folosiți pentru acest tutorial Songs of Innocence and of Experience de William Blake, de pe Project Gutenberg. Este un text scurt, ușor de înțeles, aflat în domeniul public.

Pasul 4: Scrieți programul de indexare

Copiați și lipiți codul de mai jos în index.py. Fiecare subtitlu oferă o scurtă explicație a ceea ce face segmentul de cod care urmează. Acesta este programul care va converti materialul sursă în embedding-uri vectoriale și le va stoca într-o bază de date ChromaDB, folosind modelul de embedding al Perplexity, pplx-embed-v1-0.6b, și biblioteca NumPy.

Iată ce face programul:

  1. Deschide document.txt.
  2. Împarte textul în fragmente care se suprapun.
  3. Trimite fragmentele către modelul de embedding pplx-embed-v1-0.6b al Perplexity.
  4. Decodează și normalizează vectorii returnați folosind NumPy, o bibliotecă Python de funcții de calcul.
  5. Stochează vectorii și fragmentele originale într-o bază de date ChromaDB (o veți vedea apărând în directorul proiectului).

Programul este configurat astfel încât să nu reindexeze documentul de fiecare dată când îl rulați. Dacă adăugați informații în document.txt, va genera embedding-uri doar pentru textul nou.

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

Pasul 5. Scrieți orchestratorul pentru sistemul RAG

Copiați și lipiți codul de mai jos în rag.py. Este o implementare RAG completă, dar minimală, care folosește SDK-ul Perplexity și funcționalitatea de bază de date vectorială locală a ChromaDB.

Iată ce se va întâmpla când rulați programul:

  1. Se conectează la Perplexity și la noua bază de date ChromaDB.
  2. Verifică dacă baza de date conține documente indexate.
  3. Acceptă o întrebare de la utilizator.
  4. Trimite întrebarea către modelul de embedding al Perplexity.
  5. Decodează și normalizează embedding-ul rezultat folosind NumPy.
  6. Caută în ChromaDB cele trei fragmente de document cele mai similare.
  7. Construiește un prompt care conține fragmentele regăsite și întrebarea utilizatorului.
  8. Trimite promptul către GPT-5.6 Luna prin Agent API de la Perplexity.
  9. Afișează răspunsul și așteaptă o altă întrebare.
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)

Pasul 6: Rulați aplicațiile

În terminalul PowerShell din VS Code, introduceți următoarea comandă pentru a genera embedding-uri pentru conținutul din document.txt:

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

Dacă documentul a fost indexat cu succes, ar trebui să vedeți un rezultat similar cu acesta:

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

Acum este momentul să rulați aplicația principală de orchestrare. Introduceți această comandă:

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

Dacă rulează cu succes, veți vedea următoarele:

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

Acum puteți pune întrebări la care răspunsurile vor fi extrase din baza de date vectorială pe care ați creat-o, cum ar fi:

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.

Pasul 7: Înțelegeți ce ați construit

Sistemul pe care l-ați construit conține cele două pipeline-uri de bază prezente în orice arhitectură RAG: un pipeline de indexare (sau „ingestie”) și un pipeline de regăsire și generare.

Implementarea RAG de bază realizează acum următoarele:

  • Stocare vectorială: Embedding-urile generate pe baza fișierului de cunoștințe sunt salvate în baza de date ChromaDB din mediul local.
  • Indexare incrementală: Pentru fragmentele de document nemodificate nu se mai generează din nou embedding-uri, iar documentul poate fi actualizat oricând doriți.
  • Regăsire semantică: ChromaDB regăsește fragmentele pe baza similarității vectoriale (la inițiativa orchestratorului).
  • Generarea de răspunsuri fundamentate: LLM-ul primește pasajele regăsite drept context și le integrează într-un răspuns.
  • Indexare și interogare independente: Puteți pune întrebări fără a reprocesa tot textul din fișierul de cunoștințe.
  • Conectare la model prin API: Toate răspunsurile sunt generate cu GPT-5.6 Luna prin Perplexity, nu de un model care rulează pe computerul dumneavoastră.

Pașii următori: cum puteți dezvolta această implementare

Scopul acestui tutorial este de a construi o implementare simplă, de bază. Aceasta acoperă câteva dintre componentele esențiale ale arhitecturii RAG prezente în sistemele moderne. Dacă doriți să mergeți mai departe, există diverse moduri în care o puteți dezvolta.

Iată câteva idei pentru extinderea implementării:

  • Experimentați cu LangChain: LangChain este unul dintre cele mai populare framework-uri open-source de orchestrare. Conține componente predefinite pentru construirea de aplicații capabile să ruleze pipeline-uri RAG complexe.
  • Extindeți funcționalitatea de căutare: Combinați căutarea vectorială densă cu căutarea lexicală (după cuvinte-cheie), cum ar fi BM25. Adăugarea căutării web prin Perplexity Search API v-ar permite, de asemenea, să fundamentați răspunsurile pe date actuale.
  • Adăugați un model de reranking: Integrați un model de reranking care ordonează pasajele regăsite în funcție de promptul inițial și le selectează pe cele mai relevante pentru a fi incluse în contextul de intrare furnizat LLM-ului.
  • Construiți un sistem RAG adaptiv: RAG-ul adaptiv folosește fie un LLM, fie un clasificator (o componentă din orchestrator) pentru a determina ce metodă de regăsire să fie utilizată într-un sistem RAG modular. Construirea unui astfel de sistem este o modalitate bună de a explora implementări RAG mai sofisticate.

Deși toate acestea pot părea complexe, rețineți că platformele AI consacrate, precum Perplexity, pot oferi asistență avansată la programare. Sunt excelente pentru proiecte personale și experimente, iar costurile pot fi menținute relativ scăzute.

Notă: Acest tutorial folosește GPT-5.6 Luna. Disponibilitatea modelelor se poate schimba, așa că este posibil să fie nevoie să actualizați apelul API.