Implementació bàsica de RAG: un tutorial pas a pas

Aquest tutorial per a principiants explica com funciona una implementació bàsica de RAG, repassa les bones pràctiques i mostra pas a pas com construir un sistema RAG funcional de línia d’ordres amb Python, ChromaDB i els models d’embeddings i de llenguatge de 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.

La RAG és un component essencial de la infraestructura moderna basada en LLM. En permetre que els models d’IA accedeixin a dades externes, ajuda a resoldre problemes com les al·lucinacions i les dades d’entrenament incompletes. També permet que les empreses utilitzin bases de coneixement internes que contenen informació confidencial o de propietat.

La implementació de la RAG pot ser molt complexa. Tanmateix, un sistema RAG bàsic és sorprenentment fàcil de construir. Per als principiants, crear una arquitectura senzilla pot ajudar a entendre com funciona la RAG al món real i servir de punt de partida per continuar experimentant.

En aquest article, descriurem una implementació bàsica de RAG, repassarem les bones pràctiques i, a continuació, construirem un sistema funcional amb Python, ChromaDB i dos models de Perplexity per a la generació d’embeddings i de respostes.

Procés bàsic d’implementació de RAG: una visió general

Un sistema RAG bàsic té tres components principals: un orquestrador, una font d’informació i un LLM. En un flux de treball típic, l’usuari introdueix un prompt i l’orquestrador el modifica per cercar a les fonts d’informació connectades. Després, la informació recuperada s’afegeix al context d’entrada original i s’envia a l’LLM perquè generi una resposta.

Hauràs de tenir en compte aquests passos quan construeixis la teva implementació de RAG:

  1. Indexació i embeddings: En aquesta fase, els teus documents es converteixen en una font d’informació en què l’orquestrador pot cercar. Una de les maneres més habituals de fer-ho és construir un índex vectorial consultable. Utilitzarem un model dedicat de Perplexity per convertir els fragments dels teus documents en embeddings. Després, emmagatzemarem els embeddings amb ChromaDB, una base de dades vectorial de codi obert molt popular.
  2. Orquestració: És l’aplicació que se situa entre la font d’informació externa (en el nostre cas, una base de dades vectorial) i l’LLM. Crearem un script senzill de Python que farà d’orquestrador.
  3. Recuperació i generació: Quan un usuari fa una pregunta a través de la interfície de xat, l’orquestrador la rep, executa una cerca vectorial i envia l’entrada ampliada a l’LLM. En lloc de construir un frontend dedicat, utilitzarem la línia d’ordres per enviar prompts al nostre orquestrador de Python.

Bones pràctiques: què fa que una implementació de RAG sigui eficaç?

Hi ha diversos tipus d’arquitectura RAG, i cada cas d’ús requereix un enfocament diferent. Dit això, hi ha un seguit de bones pràctiques que s’apliquen en tots els casos i que garanteixen sistemes precisos, rendibles, escalables i fàcils de mantenir.

Les bones pràctiques següents s’apliquen a tots els tipus d’implementació de RAG:

  • Garanteix un pipeline d’ingesta net: Les dades d’origen han de ser tan netes com sigui possible abans de la indexació. També hauries d’utilitzar un model d’embeddings adequat al teu contingut.
  • Utilitza la cerca híbrida: Els millors mètodes de recuperació (ja sigui la cerca en bases de dades vectorials, la cerca web mitjançant API, el recorregut de grafs de coneixement, la cerca per paraules clau o altres mètodes) varien segons el cas d’ús. Triar la combinació adequada és important per minimitzar els costos.
  • Avalua el teu sistema: Després de la implementació, hauries d’avaluar-lo segons tres mesures fonamentals: precisió, rellevància i latència. La precisió fa referència a la correcció factual; la rellevància, a l’adequació de la informació recuperada a les consultes dels usuaris, i la latència, a la velocitat amb què es recupera la informació.
  • Tracta la informació de manera segura: Si el teu sistema RAG utilitza informació interna privada, assegura’t que s’emmagatzema de manera segura i que l’accés a la interfície del sistema està controlat. Les claus d’API no s’han de compartir mai.

Implementació bàsica de RAG: tutorial en 7 passos

En aquest tutorial, construirem una aplicació de línia d’ordres que respon preguntes sobre un únic document. Utilitzarem Python, ChromaDB i l’API de Perplexity.

L’objectiu no és construir un sistema RAG preparat per a producció. Es tracta, més aviat, d’un tutorial per a principiants dissenyat per il·lustrar com funciona a la pràctica una arquitectura RAG rudimentària.

Pas 1: instal·la el programari

Assegura’t que tens instal·lats els elements següents a l’ordinador i que disposes d’un compte de l’API de Perplexity:

  • Python: És el llenguatge de programació que farem servir per construir l’orquestrador que coordinarà el flux de dades entre el model d’embeddings, ChromaDB i Perplexity.
  • Visual Studio: Utilitzarem Visual Studio com a editor de codi. Un cop instal·lat, obre el tauler Extensions i baixa l’extensió Python.
  • Compte de l’API de Perplexity: Perplexity proporcionarà tant el model d’embeddings (que converteix les teves dades en embeddings vectorials) com l’LLM que processarà el prompt ampliat. No cal un compte de pagament per fer-ho, tot i que hi haurà un petit cost en crèdits.

Pas 2: crea una carpeta de projecte

Comença creant una carpeta anomenada rag-project i obrint-la a VS Code amb File > Open Folder. A continuació, obre el terminal integrat mitjançant Terminal > New Terminal.

Introdueix el següent per crear un entorn virtual de Python:

bash
py -3.12 -m venv .venv

Ara has d’instal·lar quatre biblioteques de Python: perplexityai, chromadb, python-dotenv i numpy. L’SDK de Perplexity et permetrà connectar-te als models de Perplexity mitjançant l’API. ChromaDB crea la base de dades vectorial i s’executarà localment. Python-dotenv carrega les teves claus d’API de manera segura des d’un fitxer local, i NumPy és necessari durant el procés d’ingesta.

Instal·la-les amb l’ordre següent:

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

Pas 3: configura els fitxers

Afegeix els fitxers següents a rag-project (l’arrel del projecte) seleccionant New File… a la barra lateral de l’Explorer:

  • index.py: És l’aplicació que indexarà el teu document. Pots tornar-la a executar per afegir-hi més text.
  • rag.py: És l’aplicació de Python que farà d’orquestrador.
  • document.txt: Contindrà la documentació que convertiràs en una base de dades vectorial.
  • .env: Aquí és on desaràs la teva clau de l’API de Perplexity.

A .env, introdueix el següent:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

Substitueix your_api_key_here per la teva clau real. Per fer-ho, hauràs d’anar al tauler principal de la consola de l’API de Perplexity i seleccionar Create an API key. No comparteixis aquesta clau d’API amb ningú.

Utilitzarem dos models: pplx-embed-v1-0.6b de Perplexity, per generar els embeddings de la nostra documentació abans de carregar-la a ChromaDB, i GPT-5.6-Luna (al qual accedim a través de l’API de Perplexity), que processarà la informació recuperada.

A document.txt, afegeix el text que recuperarà el teu pipeline de RAG. Si no tens cap document a mà, per a aquest tutorial pots utilitzar Songs of Innocence and of Experience, de William Blake, disponible al Project Gutenberg. És un text breu, fàcil d’entendre i de domini públic.

Pas 4: escriu el programa d’indexació

Copia i enganxa el codi següent a index.py. Cada subtítol explica breument què fa el segment de codi que el segueix. Aquest és el programa que convertirà el teu material d’origen en embeddings vectorials i els emmagatzemarà en una base de dades de ChromaDB mitjançant el model d’embeddings de Perplexity, pplx-embed-v1-0.6b, i la biblioteca NumPy.

Això és el que fa el programa:

  1. Obre document.txt.
  2. Divideix el text en fragments que se superposen.
  3. Envia aquests fragments al model d’embeddings pplx-embed-v1-0.6b de Perplexity.
  4. Descodifica i normalitza els vectors retornats amb NumPy, una biblioteca de Python de funcions de càlcul.
  5. Emmagatzema els vectors i els fragments originals en una base de dades de ChromaDB (la veuràs aparèixer al directori del projecte).

S’ha configurat perquè no torni a indexar el document cada vegada que l’executis. Si afegeixes informació a document.txt, només generarà els embeddings del text 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())

Pas 5. Escriu l’orquestrador del sistema RAG

Copia i enganxa el codi següent a rag.py. És una implementació de RAG completa però mínima que utilitza l’SDK de Perplexity i la funcionalitat de base de dades vectorial local de ChromaDB.

Això és el que passarà quan executis el programa:

  1. Es connecta a Perplexity i a la teva nova base de dades de ChromaDB.
  2. Comprova que la base de dades conté documents indexats.
  3. Accepta una pregunta de l’usuari.
  4. Envia la pregunta al model d’embeddings de Perplexity.
  5. Descodifica i normalitza l’embedding resultant amb NumPy.
  6. Cerca a ChromaDB els tres fragments de document més semblants.
  7. Construeix un prompt que conté els fragments recuperats i la pregunta de l’usuari.
  8. Envia el prompt a GPT-5.6 Luna a través de l’Agent API de Perplexity.
  9. Mostra la resposta i espera una altra pregunta.
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)

Pas 6: executa les aplicacions

Al terminal de PowerShell de VS Code, introdueix el següent per generar els embeddings del contingut de document.txt:

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

Si el document s’ha indexat correctament, hauries de veure una sortida semblant a aquesta:

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

Ara és el moment d’executar l’aplicació principal de l’orquestrador. Introdueix aquesta ordre:

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

Si s’executa correctament, veuràs el següent:

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

Ara pots fer preguntes que extreguin informació de la base de dades vectorial que has creat, com ara:

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.

Pas 7: entén què has construït

El sistema que has construït conté els dos pipelines fonamentals presents en tota arquitectura RAG: un pipeline d’indexació, o «d’ingesta», i un pipeline de recuperació i generació.

Ara, la teva implementació bàsica de RAG fa tot això:

  • Emmagatzematge vectorial: Els embeddings es desen a la base de dades de ChromaDB del teu entorn local a partir del teu fitxer de coneixement.
  • Indexació incremental: Els fragments de document que no han canviat no es tornen a convertir en embeddings, i pots actualitzar el document sempre que vulguis.
  • Recuperació semàntica: ChromaDB recupera fragments en funció de la similitud vectorial (a petició de l’orquestrador).
  • Generació de respostes fonamentades: L’LLM rep els passatges recuperats com a context i els incorpora a la resposta.
  • Indexació i consultes independents: Pots fer preguntes sense tornar a processar tot el text del teu fitxer de coneixement.
  • Connexió amb el model mitjançant API: Totes les respostes es generen amb GPT-5.6 Luna a través de Perplexity, en lloc d’un model que s’executi a la teva màquina.

Pròxims passos: com portar aquesta implementació més enllà

L’objectiu d’aquest tutorial és construir una implementació bàsica i senzilla. Inclou alguns dels components fonamentals de l’arquitectura RAG presents en els sistemes moderns. Si vols anar més enllà, hi ha diverses maneres d’ampliar-la.

Aquí tens algunes idees per ampliar l’abast de la teva implementació:

  • Experimenta amb LangChain: LangChain és un dels frameworks d’orquestració de codi obert més populars. Inclou components predefinits per crear aplicacions capaces d’executar pipelines de RAG complexos.
  • Amplia la funcionalitat de cerca: Combina la cerca vectorial densa amb la cerca lèxica (per paraules clau), com ara BM25. Afegir la cerca web mitjançant la Search API de Perplexity també et permetria fonamentar les respostes en dades actuals.
  • Afegeix un model de reordenació: Integra un model de reordenació (reranking) que classifiqui els passatges recuperats en funció del prompt inicial i seleccioni els més rellevants per incloure’ls en el context d’entrada que es proporciona a l’LLM.
  • Construeix un sistema RAG adaptatiu: La RAG adaptativa utilitza un LLM o un classificador (un component de l’orquestrador) per determinar quin mètode de recuperació s’ha de fer servir en un sistema RAG modular. Construir-ne un és una bona manera d’explorar implementacions de RAG més sofisticades.

Tot i que tot això pot semblar complex, tingues en compte que les principals plataformes d’IA, com Perplexity, poden oferir assistència avançada en programació. Són excel·lents per a projectes personals i per experimentar, i els costos es poden mantenir relativament baixos.

Nota: aquest tutorial utilitza GPT-5.6 Luna. La disponibilitat dels models pot canviar, de manera que potser hauràs d’actualitzar la crida a l’API.