Xây dựng Pipeline Retrieval-Augmented Generation (RAG) hoàn chỉnh
Bước này kết nối các thành phần đã chuẩn bị: Vector Store (PostgreSQL + pgvector), Embedding Model và LLM để tạo thành luồng xử lý RAG.
Tạo file chính của ứng dụng RAG
Tạo file Python chịu trách nhiệm khởi tạo Vector Store, nạp dữ liệu đã được embed từ Phần 4, và thiết lập chuỗi truy vấn (Retrieval Chain).
File: /app/rag_app.py
Nội dung hoàn chỉnh:
#!/usr/bin/env python3
import os
from langchain_community.vectorstores import PGVector
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.llms import HuggingFaceHub
from langchain.chains import RetrievalQA
from langchain_core.prompts import PromptTemplate
# 1. Cấu hình môi trường (giả định đã set trong .env)
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = os.getenv("DB_PORT", "5432")
DB_NAME = os.getenv("DB_NAME", "ai_db")
DB_USER = os.getenv("DB_USER", "postgres")
DB_PASSWORD = os.getenv("DB_PASSWORD", "your_secure_password")
COLLECTION_NAME = "rag_documents"
# 2. Khởi tạo Embedding Model (phải trùng với model dùng để tạo vector trong Phần 4)
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
# 3. Kết nối Vector Store (PostgreSQL)
# Lưu ý: Sử dụng connection_string chuẩn của PGVector
connection_string = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
vector_store = PGVector(
embeddings=embeddings,
collection_name=COLLECTION_NAME,
connection=connection_string
)
# 4. Cấu hình Prompt Template để RAG hoạt động
# Prompt này yêu cầu LLM trả lời dựa trên ngữ cảnh tìm được
template = """Dựa vào thông tin ngữ cảnh dưới đây, hãy trả lời câu hỏi một cách ngắn gọn và chính xác. Nếu không có thông tin, hãy nói rằng bạn không biết.
Ngữ cảnh: {context}
Câu hỏi: {question}
Trả lời:"""
QA_CHAIN_PROMPT = PromptTemplate.from_template(template)
# 5. Khởi tạo LLM (Ví dụ dùng HuggingFace Hub hoặc Local model tùy cấu hình Phần 3)
# Ở đây dùng HuggingFaceHub cho ví dụ demo, thay thế bằng model local nếu cần
llm = HuggingFaceHub(
repo_id="HuggingFaceH4/zephyr-7b-beta",
task="text-generation",
model_kwargs={"temperature": 0.7, "max_new_tokens": 512}
)
# 6. Xây dựng Retrieval Chain
qa = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vector_store.as_retriever(search_kwargs={"k": 3}), # Lấy 3 tài liệu tương tự nhất
return_source_documents=True,
chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
)
# 7. Hàm chính để xử lý câu hỏi
def ask_question(query: str) -> dict:
result = qa.invoke({"query": query})
return result
if __name__ == "__main__":
# Test nhanh khi chạy trực tiếp file
test_query = "Viết code Python làm gì?"
print(f"Đang xử lý câu hỏi: {test_query}")
response = ask_question(test_query)
print("Kết quả trả lời:")
print(response["result"])
print("\nTài liệu nguồn:")
for doc in response["source_documents"]:
print(f"- {doc.page_content[:50]}...")
Kết quả mong đợi: File được tạo thành công. Khi chạy thử, script sẽ kết nối vào PostgreSQL, truy xuất vector store và in ra câu trả lời dựa trên ngữ cảnh (nếu dữ liệu đã có sẵn).
Verify kết quả kết nối
Chạy lệnh sau để đảm bảo không có lỗi kết nối database hoặc lỗi import thư viện:
cd /app && python3 -c "from rag_app import ask_question; print('Kết nối Vector Store thành công')"
Kết quả mong đợi: Xuất hiện dòng chữ "Kết nối Vector Store thành công" mà không báo lỗi ImportError hay ConnectionError.
Cấu hình Vector Store để thực hiện tìm kiếm tương tự (Similarity Search)
Để RAG hoạt động hiệu quả, Vector Store phải được cấu hình để tìm kiếm các vector có khoảng cách Euclidean hoặc Cosine nhỏ nhất (tương tự nhất) với vector câu hỏi.
Cấu hình tham số tìm kiếm (Search Kwargs)
Trong code trên, tham số `search_kwargs={"k": 3}` đã được cấu hình. Điều này có nghĩa hệ thống sẽ tìm 3 tài liệu có điểm tương đồng cao nhất.
Để tối ưu hóa việc tìm kiếm, chúng ta cần đảm bảo PostgreSQL đã kích hoạt index HNSW cho cột vector. Nếu chưa làm ở Phần 2, hãy thực hiện ngay trong SQL:
psql -h localhost -U postgres -d ai_db -c "
CREATE INDEX IF NOT EXISTS rag_documents_idx ON langchain_pg_embedding USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
"
Kết quả mong đợi: PostgreSQL báo "INDEX CREATED". Việc này giúp tốc độ tìm kiếm vector (Similarity Search) tăng lên gấp hàng trăm lần so với quét toàn bộ bảng.
Test trực tiếp cơ chế Similarity Search
Tạo một script nhỏ để test khả năng tìm kiếm vector mà không cần gọi LLM, giúp xác minh logic Retrieval đang hoạt động đúng.
File: /app/test_retrieval.py
#!/usr/bin/env python3
import os
from langchain_community.vectorstores import PGVector
from langchain_community.embeddings import HuggingFaceEmbeddings
# Cấu hình giống hệt rag_app.py
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
connection_string = f"postgresql://postgres:your_secure_password@localhost:5432/ai_db"
vector_store = PGVector(
embeddings=embeddings,
collection_name="rag_documents",
connection=connection_string
)
# Thực hiện Similarity Search
query = "Làm thế nào để cài đặt PostgreSQL trên Ubuntu?"
docs = vector_store.similarity_search(query, k=3)
print(f"Đã tìm thấy {len(docs)} tài liệu tương tự cho câu hỏi: '{query}'")
for i, doc in enumerate(docs):
print(f"\n[Tài liệu {i+1}] Điểm tương đồng: {doc.metadata.get('score', 'N/A')}")
print(f"Nội dung: {doc.page_content[:100]}...")
Kết quả mong đợi: Script in ra 3 tài liệu có nội dung liên quan đến "cài đặt PostgreSQL" hoặc "Ubuntu", chứng tỏ cơ chế tìm kiếm vector hoạt động chính xác.
Tích hợp logic trả lời câu hỏi dựa trên ngữ cảnh tìm được
Bước cuối cùng là liên kết phần "Tìm kiếm" (Retrieval) với phần "Sinh" (Generation) thông qua Prompt Template để LLM hiểu ngữ cảnh và tạo câu trả lời.
Chạy ứng dụng RAG hoàn chỉnh
Chạy file chính `rag_app.py` để thực hiện luồng RAG đầy đủ: Query -> Embed -> Search -> Context -> LLM -> Answer.
cd /app && python3 rag_app.py
Kết quả mong đợi:
1. Hệ thống in ra thông báo "Đang xử lý câu hỏi: ..."
2. LLM trả về câu trả lời dựa trên dữ liệu trong database (không phải kiến thức chung).
3. In ra danh sách "Tài liệu nguồn" (source_documents) đã được sử dụng để tạo câu trả lời.
Verify logic trả lời có chính xác dựa trên ngữ cảnh không
Để chắc chắn LLM không "ảo giác" (hallucinate) hay trả lời bằng kiến thức chung, hãy đặt một câu hỏi rất cụ thể chỉ có trong dữ liệu bạn đã upload ở Phần 4.
Chạy lệnh tương tác để test nhiều câu hỏi:
cd /app && python3 -c "
from rag_app import ask_question
# Câu hỏi giả định: Có một đoạn văn bản về 'Cấu hình Nginx' trong dữ liệu của bạn
response = ask_question('Cấu hình Nginx cho proxy reverse như thế nào theo tài liệu?')
print('=== KẾT QUẢ RAG ===')
print('Câu trả lời:', response['result'])
print('Số tài liệu dùng:', len(response['source_documents']))
"
Kết quả mong đợi:
- Nếu có dữ liệu về Nginx: Trả lời chi tiết dựa trên đoạn văn bản đó.
- Nếu không có dữ liệu: Trả lời "Tôi không có thông tin về chủ đề này trong ngữ cảnh đã cung cấp" (nhờ Prompt Template đã cấu hình).
Log kiểm tra hiệu suất (Optional)
Để debug và xem thời gian phản hồi, thêm dòng logging vào file `rag_app.py` trước khi gọi `qa.invoke`:
import time
start_time = time.time()
result = qa.invoke({"query": query})
print(f"Thời gian xử lý: {time.time() - start_time:.2f} giây")
Kết quả mong đợi: Xuất hiện thời gian xử lý (thường dưới 5 giây nếu dùng cloud LLM hoặc local model mạnh). Nếu thời gian quá lâu, cần xem lại phần index HNSW ở mục trên.
Điều hướng series:
Mục lục: Series: Triển khai Database AI với LangChain, PostgreSQL và Ubuntu 24.04
« Phần 4: Chuẩn bị dữ liệu và tạo Embeddings cho RAG
Phần 6: Đóng gói và triển khai ứng dụng lên Ubuntu Server »