Triển khai RAG cơ bản: Hướng dẫn từng bước
Hướng dẫn thân thiện với người mới bắt đầu này giải thích cách một bản triển khai RAG cơ bản hoạt động, trình bày các phương pháp hay nhất và hướng dẫn từng bước xây dựng một hệ thống RAG dòng lệnh hoạt động được với Python, ChromaDB cùng các mô hình embedding và mô hình ngôn ngữ của Perplexity.

RAG là một thành phần thiết yếu của hạ tầng hiện đại dựa trên LLM. Bằng cách cho phép các mô hình AI truy cập dữ liệu bên ngoài, RAG giúp giải quyết những vấn đề như ảo giác và dữ liệu huấn luyện không đầy đủ. Điều đó cũng có nghĩa là các công ty có thể sử dụng những cơ sở tri thức nội bộ chứa thông tin nhạy cảm hoặc độc quyền.
Việc triển khai RAG có thể rất phức tạp. Tuy nhiên, một hệ thống RAG cơ bản lại dễ xây dựng đến bất ngờ. Với người mới bắt đầu, việc tạo một kiến trúc đơn giản có thể giúp làm rõ cách RAG vận hành trong thực tế và tạo bàn đạp cho những thử nghiệm sâu hơn.
Trong bài viết này, chúng ta sẽ phác thảo một quy trình triển khai RAG cơ bản, điểm qua các phương pháp hay nhất, rồi xây dựng một hệ thống hoạt động được bằng Python, ChromaDB và hai mô hình của Perplexity để tạo embedding và tạo câu trả lời.
Quy trình triển khai RAG cơ bản: Tổng quan cấp cao
Một hệ thống RAG cơ bản có ba thành phần chính: bộ điều phối, nguồn thông tin và LLM. Trong một quy trình làm việc điển hình, người dùng nhập prompt, sau đó bộ điều phối điều chỉnh prompt đó để tìm kiếm trong mọi nguồn thông tin được kết nối. Thông tin truy xuất được sau đó được thêm vào ngữ cảnh đầu vào ban đầu và gửi đến LLM để tạo câu trả lời.
Khi xây dựng bản triển khai RAG của mình, bạn cần tính đến các bước sau:
- Lập chỉ mục và tạo embedding: Ở giai đoạn này, tài liệu của bạn được chuyển đổi thành một nguồn thông tin mà bộ điều phối có thể tìm kiếm. Một trong những cách phổ biến nhất để làm điều này là xây dựng một chỉ mục vector có thể tìm kiếm. Chúng ta sẽ dùng một mô hình chuyên dụng của Perplexity để chuyển các đoạn tài liệu của bạn thành embedding. Sau đó, chúng ta sẽ lưu trữ các embedding bằng ChromaDB, một cơ sở dữ liệu vector mã nguồn mở phổ biến.
- Điều phối: Đây là ứng dụng nằm giữa nguồn thông tin bên ngoài (trong trường hợp của chúng ta là một cơ sở dữ liệu vector) và LLM. Chúng ta sẽ tạo một script Python đơn giản để đóng vai trò bộ điều phối.
- Truy xuất và tạo câu trả lời: Khi người dùng đặt câu hỏi qua giao diện trò chuyện, bộ điều phối sẽ tiếp nhận câu hỏi, chạy tìm kiếm vector và đưa đầu vào đã mở rộng đến LLM. Thay vì xây dựng một giao diện người dùng riêng, chúng ta sẽ dùng dòng lệnh để gửi prompt và giao tiếp với bộ điều phối Python.
Các phương pháp hay nhất: Điều gì làm nên một bản triển khai RAG hiệu quả?
Có nhiều loại kiến trúc RAG, và mỗi trường hợp sử dụng lại đòi hỏi một cách tiếp cận khác nhau. Dù vậy, một số phương pháp hay nhất áp dụng cho mọi trường hợp và giúp đảm bảo hệ thống chính xác, tiết kiệm chi phí, có khả năng mở rộng và dễ bảo trì.
Các phương pháp hay nhất sau đây áp dụng cho mọi loại hình triển khai RAG:
- Đảm bảo pipeline nạp dữ liệu sạch: Dữ liệu nguồn của bạn cần càng sạch càng tốt trước khi lập chỉ mục. Bạn cũng nên dùng mô hình embedding phù hợp với nội dung của mình.
- Sử dụng tìm kiếm kết hợp: Phương pháp truy xuất tốt nhất, dù là tìm kiếm trong cơ sở dữ liệu vector, tìm kiếm web qua API, duyệt đồ thị tri thức, tìm kiếm theo từ khóa hay các phương pháp khác, sẽ khác nhau tùy trường hợp sử dụng. Chọn đúng tổ hợp phương pháp là điều quan trọng để giảm thiểu chi phí.
- Đánh giá hệ thống: Sau khi triển khai, bạn nên đánh giá theo ba thước đo cốt lõi: độ chính xác, mức độ liên quan và độ trễ. Độ chính xác là mức độ đúng đắn về mặt dữ kiện; mức độ liên quan là mức độ phù hợp của thông tin truy xuất được với truy vấn của người dùng; còn độ trễ là tốc độ truy xuất thông tin.
- Xử lý thông tin an toàn: Nếu hệ thống RAG của bạn sử dụng thông tin nội bộ riêng tư, hãy đảm bảo thông tin đó được lưu trữ an toàn và quyền truy cập vào giao diện hệ thống được quản lý chặt chẽ. Tuyệt đối không chia sẻ khóa API.
Triển khai RAG cơ bản: Hướng dẫn 7 bước
Trong hướng dẫn này, chúng ta sẽ xây dựng một ứng dụng dòng lệnh trả lời các câu hỏi về một tài liệu duy nhất. Chúng ta sẽ dùng Python, ChromaDB và Perplexity API.
Mục tiêu không phải là xây dựng một hệ thống RAG sẵn sàng cho môi trường sản xuất. Thay vào đó, đây là một hướng dẫn thân thiện với người mới bắt đầu, nhằm minh họa cách một kiến trúc RAG sơ khai vận hành trong thực tế.
Bước 1: Cài đặt phần mềm
Hãy đảm bảo máy tính của bạn đã cài đặt các phần mềm sau và bạn đã có tài khoản Perplexity API:
- Python: Đây là ngôn ngữ lập trình chúng ta sẽ dùng để xây dựng bộ điều phối, có nhiệm vụ điều phối luồng dữ liệu giữa mô hình embedding, ChromaDB và Perplexity.
- Visual Studio: Chúng ta sẽ dùng Visual Studio làm trình soạn thảo mã. Sau khi cài đặt, hãy mở bảng Extensions và tải xuống tiện ích mở rộng Python.
- Tài khoản Perplexity API: Perplexity sẽ cung cấp cả mô hình embedding (chuyển dữ liệu của bạn thành vector embedding) lẫn LLM sẽ xử lý prompt đã mở rộng. Bạn không cần tài khoản trả phí để thực hiện việc này, dù sẽ tốn một ít tín dụng.
Bước 2: Tạo thư mục dự án
Bắt đầu bằng cách tạo một thư mục có tên rag-project và mở thư mục này trong VS Code bằng cách chọn File > Open Folder. Sau đó, mở terminal tích hợp qua Terminal > New Terminal.
Nhập lệnh sau để tạo môi trường ảo Python:
py -3.12 -m venv .venvBây giờ, bạn cần cài đặt bốn thư viện Python: perplexityai, chromadb, python-dotenv và numpy. Perplexity SDK cho phép bạn kết nối với các mô hình của Perplexity qua API. ChromaDB xây dựng cơ sở dữ liệu vector và sẽ chạy cục bộ. Python-dotenv tải khóa API của bạn một cách an toàn từ một tệp cục bộ, còn NumPy cần thiết trong quá trình nạp dữ liệu.
Cài đặt các thư viện này bằng lệnh sau:
.\.venv\Scripts\python.exe -m pip install perplexityai chromadb python-dotenv numpyBước 3: Thiết lập các tệp
Thêm các tệp sau vào rag-project (thư mục gốc của dự án) bằng cách chọn New File… trong thanh bên Explorer:
- index.py: Đây là ứng dụng sẽ lập chỉ mục tài liệu của bạn. Bạn có thể chạy lại ứng dụng này để thêm văn bản mới.
- rag.py: Đây là ứng dụng Python sẽ đóng vai trò bộ điều phối.
- document.txt: Tệp này sẽ chứa tài liệu mà bạn sẽ chuyển đổi thành cơ sở dữ liệu vector.
- .env: Đây là nơi bạn lưu khóa Perplexity API.
Trong .env, nhập nội dung sau:
PERPLEXITY_API_KEY=your_api_key_hereThay your_api_key_here bằng khóa thực của bạn. Để lấy khóa, hãy truy cập trang tổng quan chính của Perplexity API Console và chọn Create an API key. Hãy giữ bí mật khóa API này.
Chúng ta sẽ dùng hai mô hình: pplx-embed-v1-0.6b của Perplexity để tạo embedding cho tài liệu trước khi tải lên ChromaDB, và GPT-5.6-Luna (truy cập qua API của Perplexity) để xử lý thông tin truy xuất được.
Trong document.txt, hãy thêm văn bản mà pipeline RAG của bạn sẽ truy xuất. Nếu chưa có sẵn tài liệu, bạn có thể dùng tập thơ Songs of Innocence and of Experience của William Blake từ Project Gutenberg cho hướng dẫn này. Đây là một văn bản ngắn, dễ hiểu và thuộc phạm vi công cộng.
Bước 4: Viết chương trình lập chỉ mục
Sao chép và dán đoạn mã bên dưới vào index.py. Mỗi tiêu đề phụ giải thích ngắn gọn chức năng của đoạn mã theo sau. Đây là chương trình sẽ chuyển đổi tài liệu nguồn của bạn thành vector embedding và lưu trữ chúng trong cơ sở dữ liệu ChromaDB, sử dụng mô hình embedding pplx-embed-v1-0.6b của Perplexity cùng thư viện NumPy.
Chương trình thực hiện những việc sau:
- Mở document.txt.
- Chia văn bản thành các đoạn chồng lấn nhau.
- Gửi các đoạn đó đến mô hình embedding pplx-embed-v1-0.6b của Perplexity.
- Giải mã và chuẩn hóa các vector trả về bằng NumPy, một thư viện Python gồm các hàm tính toán.
- Lưu trữ các vector và các đoạn gốc trong cơ sở dữ liệu ChromaDB (bạn sẽ thấy cơ sở dữ liệu này xuất hiện trong thư mục dự án).
Chương trình được thiết lập để không lập chỉ mục lại tài liệu mỗi lần bạn chạy. Nếu bạn bổ sung thông tin vào document.txt, chương trình sẽ chỉ tạo embedding cho phần văn bản mới.
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())Bước 5. Viết bộ điều phối cho hệ thống RAG
Sao chép và dán đoạn mã bên dưới vào rag.py. Đây là một bản triển khai RAG hoàn chỉnh nhưng tối giản, sử dụng Perplexity SDK và chức năng cơ sở dữ liệu vector cục bộ của ChromaDB.
Khi bạn chạy chương trình, những việc sau sẽ diễn ra:
- Kết nối với Perplexity và cơ sở dữ liệu ChromaDB mới của bạn.
- Kiểm tra xem cơ sở dữ liệu có chứa tài liệu đã được lập chỉ mục hay không.
- Nhận câu hỏi từ người dùng.
- Gửi câu hỏi đến mô hình embedding của Perplexity.
- Giải mã và chuẩn hóa embedding thu được bằng NumPy.
- Tìm trong ChromaDB ba đoạn tài liệu có độ tương đồng cao nhất.
- Tạo một prompt chứa các đoạn truy xuất được và câu hỏi của người dùng.
- Gửi prompt đến GPT-5.6 Luna thông qua Agent API của Perplexity.
- Hiển thị câu trả lời và chờ câu hỏi tiếp theo.
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)Bước 6: Chạy các ứng dụng
Trong terminal PowerShell của VS Code, nhập lệnh sau để tạo embedding cho nội dung trong document.txt:
.\.venv\Scripts\python.exe index.pyNếu tài liệu đã được lập chỉ mục thành công, bạn sẽ thấy kết quả tương tự như sau:
Indexing complete!
Unchanged chunks: 0
New or modified chunks: 58
Removed chunks: 0
Total stored chunks: 58Bây giờ là lúc chạy ứng dụng điều phối chính. Nhập lệnh sau:
.\.venv\Scripts\python.exe rag.pyNếu chạy thành công, bạn sẽ thấy nội dung sau:
Connected to existing ChromaDB database.
Stored chunks: 58
Ask a question (or type exit): Giờ đây, bạn có thể đặt những câu hỏi được trả lời dựa trên cơ sở dữ liệu vector bạn đã tạo, chẳng hạn như:
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.Bước 7: Hiểu rõ những gì bạn đã xây dựng
Hệ thống bạn vừa xây dựng gồm hai pipeline cốt lõi có trong mọi kiến trúc RAG: pipeline lập chỉ mục (hay “nạp dữ liệu”) và pipeline truy xuất và tạo câu trả lời.
Bản triển khai RAG cơ bản của bạn giờ đây có thể thực hiện tất cả những việc sau:
- Lưu trữ vector: Các embedding được tạo từ tệp tri thức của bạn và lưu trong cơ sở dữ liệu ChromaDB ở môi trường cục bộ.
- Lập chỉ mục tăng dần: Các đoạn tài liệu không thay đổi sẽ không được tạo embedding lại, và bạn có thể cập nhật tài liệu bất cứ khi nào muốn.
- Truy xuất ngữ nghĩa: ChromaDB truy xuất các đoạn dựa trên độ tương đồng vector (do bộ điều phối khởi tạo).
- Tạo câu trả lời có căn cứ: LLM nhận các đoạn văn bản truy xuất được làm ngữ cảnh và đưa chúng vào câu trả lời.
- Lập chỉ mục và truy vấn độc lập: Bạn có thể đặt câu hỏi mà không cần xử lý lại toàn bộ văn bản trong tệp tri thức.
- Kết nối mô hình qua API: Mọi câu trả lời đều được tạo bằng GPT-5.6 Luna thông qua Perplexity, thay vì một mô hình chạy trên máy của bạn.
Các bước tiếp theo: Cách phát triển bản triển khai này xa hơn
Mục tiêu của hướng dẫn này là xây dựng một bản triển khai cơ bản, đơn giản. Hướng dẫn đề cập đến một số thành phần cốt lõi của kiến trúc RAG có trong các hệ thống hiện đại. Nếu muốn tiến xa hơn, bạn có nhiều cách để phát triển dựa trên nền tảng này.
Dưới đây là một số ý tưởng để mở rộng phạm vi bản triển khai của bạn:
- Thử nghiệm với LangChain: LangChain là một trong những framework điều phối mã nguồn mở phổ biến nhất. Framework này có các thành phần dựng sẵn để xây dựng ứng dụng có khả năng chạy các pipeline RAG phức tạp.
- Mở rộng chức năng tìm kiếm: Kết hợp tìm kiếm vector dày đặc với tìm kiếm từ vựng (theo từ khóa) như BM25. Bổ sung tìm kiếm web qua Perplexity Search API cũng giúp câu trả lời dựa trên dữ liệu mới nhất.
- Thêm mô hình xếp hạng lại: Tích hợp một mô hình xếp hạng lại để xếp hạng các đoạn văn bản truy xuất được so với prompt ban đầu và chọn ra những đoạn liên quan nhất để đưa vào ngữ cảnh đầu vào cung cấp cho LLM.
- Xây dựng hệ thống RAG thích ứng: RAG thích ứng sử dụng LLM hoặc bộ phân loại (một thành phần trong bộ điều phối) để xác định phương pháp truy xuất cần dùng trong một hệ thống RAG dạng mô-đun. Xây dựng một hệ thống như vậy là cách hay để khám phá các bản triển khai RAG tinh vi hơn.
Dù tất cả những điều này nghe có vẻ phức tạp, hãy nhớ rằng các nền tảng AI phổ biến như Perplexity có thể hỗ trợ lập trình nâng cao. Các nền tảng này rất phù hợp cho dự án cá nhân và thử nghiệm, đồng thời chi phí có thể được giữ ở mức tương đối thấp.
Lưu ý: Hướng dẫn này sử dụng GPT-5.6 Luna. Các mô hình khả dụng có thể thay đổi, vì vậy bạn có thể cần cập nhật lệnh gọi API.