Implementación básica da RAG: titorial paso a paso

Este titorial para principiantes explica como funciona unha implementación básica da RAG, repasa as boas prácticas e guía paso a paso na construción dun sistema RAG funcional de liña de comandos con Python, ChromaDB e os modelos de embeddings e de linguaxe 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.

A RAG é un compoñente esencial da infraestrutura moderna baseada en LLM. Ao permitir que os modelos de IA accedan a datos externos, axuda a resolver problemas como as alucinacións e os datos de adestramento incompletos. Ademais, permite que as empresas utilicen bases de coñecemento internas que conteñen información confidencial ou propietaria.

A implementación da RAG pode ser moi complexa. Porén, un sistema RAG básico é sorprendentemente doado de construír. Para quen está a comezar, crear unha arquitectura sinxela pode axudar a entender como funciona a RAG no mundo real e servir como punto de partida para seguir experimentando.

Neste artigo, presentaremos unha implementación básica da RAG, repasaremos as boas prácticas e, a continuación, construiremos un sistema funcional con Python, ChromaDB e dous modelos de Perplexity para xerar embeddings e respostas.

Proceso básico de implementación da RAG: visión xeral

Un sistema RAG básico ten tres compoñentes principais: un orquestrador, unha fonte de información e un LLM. Nun fluxo de traballo típico, o usuario introduce un prompt e o orquestrador modifícao para buscar en calquera fonte de información conectada. Despois, a información recuperada engádese ao contexto de entrada orixinal e envíase ao LLM para que xere unha resposta.

Terás que ter en conta estes pasos ao construír a túa implementación da RAG:

  1. Indexación e embeddings: Nesta fase, os teus documentos convértense nunha fonte de información na que o orquestrador pode buscar. Unha das formas máis habituais de facelo é crear un índice vectorial que se poida consultar. Usaremos un modelo específico de Perplexity para converter os fragmentos dos teus documentos en embeddings. Despois, almacenaremos os embeddings con ChromaDB, unha popular base de datos vectorial de código aberto.
  2. Orquestración: É a aplicación que se sitúa entre a fonte de información externa (no noso caso, unha base de datos vectorial) e o LLM. Crearemos un script sinxelo de Python que fará de orquestrador.
  3. Recuperación e xeración: Cando un usuario fai unha pregunta a través da interface de chat, o orquestrador recóllea, executa unha busca vectorial e envía a entrada ampliada ao LLM. En lugar de crear unha interface dedicada, usaremos a liña de comandos para comunicarnos co noso orquestrador de Python.

Boas prácticas: que fai que unha implementación da RAG sexa eficaz?

Hai varios tipos de arquitectura RAG, e cada caso de uso require un enfoque diferente. Dito isto, hai unha serie de boas prácticas que se aplican en todos os casos e que garanten sistemas precisos, rendibles, escalables e fáciles de manter.

As seguintes boas prácticas aplícanse a todos os tipos de implementación da RAG:

  • Garante un pipeline de inxestión limpo: Os teus datos de orixe deberían estar o máis limpos posible antes da indexación. Tamén deberías usar un modelo de embeddings axeitado para o teu contido.
  • Usa a busca híbrida: Os mellores métodos de recuperación, xa sexa a busca en bases de datos vectoriais, a busca web mediante API, o percorrido de grafos de coñecemento, a busca por palabras clave ou outros métodos, varían segundo o caso de uso. Escoller a combinación axeitada é importante para minimizar os custos.
  • Avalía o teu sistema: Despois da implementación, deberías avaliar o sistema segundo tres medidas fundamentais: precisión, relevancia e latencia. A precisión refírese á corrección dos feitos; a relevancia, á adecuación da información recuperada ás consultas dos usuarios; e a latencia, á velocidade coa que se recupera a información.
  • Trata a información de forma segura: Se o teu sistema RAG se nutre de información interna privada, asegúrate de que estea almacenada de forma segura e de que o acceso á interface do sistema estea xestionado. Nunca se deben compartir as claves de API.

Implementación básica da RAG: titorial en 7 pasos

Neste titorial construiremos unha aplicación de liña de comandos que responde preguntas sobre un único documento. Usaremos Python, ChromaDB e a API de Perplexity.

O obxectivo non é construír un sistema RAG listo para produción. Trátase, máis ben, dun titorial para principiantes deseñado para ilustrar como funciona na práctica unha arquitectura RAG rudimentaria.

Paso 1: Instala o software

Asegúrate de ter instalado o seguinte no teu ordenador e de dispor dunha conta da API de Perplexity:

  • Python: É a linguaxe de programación que usaremos para construír o orquestrador que coordinará o fluxo de datos entre o modelo de embeddings, ChromaDB e Perplexity.
  • Visual Studio: Usaremos Visual Studio como editor de código. Unha vez instalado, abre o panel Extensions e descarga a extensión Python.
  • Conta da API de Perplexity: Perplexity proporcionará tanto o modelo de embeddings (que converte os teus datos en embeddings vectoriais) como o LLM que procesará o prompt ampliado. Non necesitas unha conta de pago para isto, aínda que terá un pequeno custo en créditos.

Paso 2: Crea un cartafol para o proxecto

Comeza creando un cartafol chamado rag-project e ábreo en VS Code seleccionando File > Open Folder. Despois, abre o terminal integrado mediante Terminal > New Terminal.

Introduce o seguinte para crear un contorno virtual de Python:

bash
py -3.12 -m venv .venv

Agora tes que instalar catro bibliotecas de Python: perplexityai, chromadb, python-dotenv e numpy. O SDK de Perplexity permitirache conectarte aos modelos de Perplexity mediante a API. ChromaDB crea a base de datos vectorial e executarase localmente. Python-dotenv carga as túas claves de API de forma segura desde un ficheiro local, e NumPy é necesario durante o proceso de inxestión.

Instálaas co seguinte comando:

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

Paso 3: Configura os teus ficheiros

Engade os seguintes ficheiros a rag-project (a raíz do proxecto) seleccionando New File… na barra lateral Explorer:

  • index.py: É a aplicación que indexará o teu documento. Podes volver executala para engadir máis texto.
  • rag.py: É a aplicación de Python que fará de orquestrador.
  • document.txt: Conterá a documentación que converterás nunha base de datos vectorial.
  • .env: Aquí gardarás a túa clave da API de Perplexity.

En .env, introduce o seguinte:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

Substitúe your_api_key_here pola túa clave real. Para iso, terás que ir ao panel de inicio da consola da API de Perplexity e seleccionar Create an API key. Mantén esta clave de API en privado.

Usaremos dous modelos: pplx-embed-v1-0.6b de Perplexity, para xerar os embeddings da nosa documentación antes de subila a ChromaDB, e GPT-5.6-Luna (ao que se accede mediante a API de Perplexity), que procesará a información recuperada.

En document.txt, engade o texto que recuperará o teu pipeline RAG. Se non tes ningún documento á man, usa Songs of Innocence and of Experience, de William Blake, dispoñible no Project Gutenberg, para este titorial. É un texto curto, doado de entender e de dominio público.

Paso 4: Escribe o programa de indexación

Copia e pega o código seguinte en index.py. Cada subtítulo ofrece unha breve explicación do que fai o segmento de código que vén a continuación. Este é o programa que converterá o teu material de orixe en embeddings vectoriais e os almacenará nunha base de datos de ChromaDB usando o modelo de embeddings de Perplexity, pplx-embed-v1-0.6b, e a biblioteca NumPy.

Isto é o que fai o programa:

  1. Abre document.txt.
  2. Divide o texto en fragmentos superpostos.
  3. Envía eses fragmentos ao modelo de embeddings pplx-embed-v1-0.6b de Perplexity.
  4. Descodifica e normaliza os vectores devoltos usando NumPy, unha biblioteca de Python con funcións de cálculo.
  5. Almacena os vectores e os fragmentos orixinais nunha base de datos de ChromaDB (verás que aparece no directorio do teu proxecto).

Está configurado para non volver indexar o documento cada vez que o executes. Se engades información a document.txt, só xerará embeddings para o texto novo.

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

Paso 5. Escribe o orquestrador do sistema RAG

Copia e pega o código seguinte en rag.py. Trátase dunha implementación da RAG completa pero mínima que usa o SDK de Perplexity e a funcionalidade de base de datos vectorial local de ChromaDB.

Isto é o que ocorrerá cando executes o programa:

  1. Conéctase a Perplexity e á túa nova base de datos de ChromaDB.
  2. Comproba que a base de datos contén documentos indexados.
  3. Acepta unha pregunta do usuario.
  4. Envía a pregunta ao modelo de embeddings de Perplexity.
  5. Descodifica e normaliza o embedding resultante usando NumPy.
  6. Busca en ChromaDB os tres fragmentos de documento máis similares.
  7. Constrúe un prompt que contén os fragmentos recuperados e a pregunta do usuario.
  8. Envía o prompt a GPT-5.6 Luna mediante a Agent API de Perplexity.
  9. Mostra a resposta e agarda outra 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)

Paso 6: Executa as aplicacións

No terminal de PowerShell de VS Code, introduce o seguinte para xerar os embeddings do contido de document.txt:

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

Se o documento se indexou correctamente, deberías ver unha saída semellante a esta:

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

Agora toca executar a aplicación principal do orquestrador. Introduce este comando:

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

Se se executa correctamente, verás o seguinte:

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

Agora podes facer preguntas que se responderán a partir da base de datos vectorial que creaches, como por exemplo:

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.

Paso 7: Comprende o que construíches

O sistema que construíches contén os dous pipelines fundamentais presentes en toda arquitectura RAG: un pipeline de indexación, ou de “inxestión”, e un pipeline de recuperación e xeración.

A túa implementación básica da RAG fai agora todo o seguinte:

  • Almacenamento vectorial: Os embeddings gárdanse na túa base de datos de ChromaDB, no teu contorno local, a partir do teu ficheiro de coñecemento.
  • Indexación incremental: Os fragmentos do documento que non cambian non se volven procesar para xerar embeddings, e podes actualizar o documento sempre que queiras.
  • Recuperación semántica: ChromaDB recupera fragmentos en función da similitude vectorial (por iniciativa do orquestrador).
  • Xeración de respostas fundamentadas: O LLM recibe as pasaxes recuperadas como contexto e incorpóraas nunha resposta.
  • Indexación e consulta independentes: Podes facer preguntas sen volver procesar todo o texto que contén o teu ficheiro de coñecemento.
  • Conexión co modelo mediante API: Todas as respostas xéranse con GPT-5.6 Luna a través de Perplexity, en lugar de cun modelo que se executa na túa máquina.

Próximos pasos: como levar esta implementación máis lonxe

O obxectivo deste titorial é construír unha implementación básica e sinxela. Abrangue algúns dos compoñentes fundamentais da arquitectura RAG presentes nos sistemas modernos. Se queres ir máis alá, hai varias formas de ampliala.

Aquí tes algunhas ideas para ampliar o alcance da túa implementación:

  • Experimenta con LangChain: LangChain é un dos frameworks de orquestración de código aberto máis populares. Contén compoñentes predefinidos para crear aplicacións capaces de executar pipelines RAG complexos.
  • Amplía a funcionalidade de busca: Combina a busca vectorial densa coa busca léxica (por palabras clave), como BM25. Engadir a busca web mediante a Perplexity Search API tamén che permitiría fundamentar as respostas en datos actuais.
  • Engade un modelo de reclasificación: Integra un modelo de reclasificación que ordene as pasaxes recuperadas en función do prompt inicial e seleccione as máis relevantes para incluílas no contexto de entrada que se lle proporciona ao LLM.
  • Constrúe un sistema RAG adaptativo: A RAG adaptativa usa un LLM ou un clasificador (un compoñente dentro do orquestrador) para determinar que método de recuperación empregar nun sistema RAG modular. Construír un é unha boa forma de explorar implementacións da RAG máis sofisticadas.

Aínda que todo isto pode parecer complexo, ten en conta que as principais plataformas de IA, como Perplexity, poden ofrecer axuda avanzada coa programación. Son excelentes para proxectos persoais e para experimentar, e os custos pódense manter relativamente baixos.

Nota: Este titorial usa GPT-5.6 Luna. A dispoñibilidade dos modelos pode cambiar, polo que é posible que teñas que actualizar a chamada á API.