Khi xây dựng chatbot thông minh cho doanh nghiệp, việc đọc và hiểu tài liệu dài là bài toán cốt lõi. Bài viết này sẽ hướng dẫn bạn từng bước cách triển khai RAG (Retrieval-Augmented Generation) với kỹ thuật chia nhỏ tài liệu hiệu quả, hoàn toàn không cần kinh nghiệm lập trình trước đó.
RAG là gì và tại sao cần chia nhỏ tài liệu?
Khi bạn có một tài liệu 500 trang (báo cáo tài chính, văn học, tài liệu pháp lý), không thể đưa toàn bộ vào một lần gọi API được. RAG hoạt động như một thư viện thông minh: nó tìm đoạn văn bản liên quan nhất với câu hỏi của bạn, rồi gửi đoạn đó đến AI để tạo câu trả lời.
Ba phương pháp chia nhỏ phổ biến nhất
- Chia theo ký tự cố định (Fixed-size chunking): Đơn giản nhất, phù hợp tài liệu đồng nhất. Nhược điểm: có thể cắt giữa câu.
- Chia theo đoạn văn (Semantic chunking): Thông minh hơn, giữ nguyên ngữ cảnh. Độ trễ cao hơn.
- Chia theo cấu trúc tài liệu (Recursive chunking): Tốt nhất cho tài liệu có cấu trúc rõ ràng như markdown, HTML, PDF.
Bắt đầu từ con số không - Triển khai RAG với HolySheep AI
Tôi đã thử nhiều nhà cung cấp API và chọn HolySheep AI vì: độ trễ dưới 50ms, tỷ giá ¥1 = $1 (tiết kiệm 85%+ so với các nền tảng khác), hỗ trợ WeChat/Alipay cho người dùng Việt Nam.
Yêu cầu chuẩn bị
- Tài khoản HolySheep AI (đăng ký miễn phí, nhận tín dụng dùng thử)
- Python 3.8+ cài sẵn trong máy
- Tài liệu cần xử lý (txt, pdf, docx đều được)
Bước 1: Cài đặt thư viện cần thiết
Mở terminal (Command Prompt trên Windows) và chạy:
pip install openai faiss-cpu tiktoken langchain langchain-community pypdf python-docx
Bước 2: Tạo hàm chia nhỏ tài liệu thông minh
Tôi sẽ chia sẻ code hoàn chỉnh mà mình đã sử dụng trong dự án thực tế:
import os
import re
from typing import List, Dict
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader
class DocumentChunker:
"""Chia nhỏ tài liệu với chiến lược tối ưu cho RAG"""
def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=["\n\n", "\n", ". ", " ", ""],
length_function=len,
)
def load_document(self, file_path: str) -> List:
"""Tải tài liệu dựa trên định dạng"""
if file_path.endswith('.pdf'):
loader = PyPDFLoader(file_path)
elif file_path.endswith('.docx'):
loader = Docx2txtLoader(file_path)
else:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
return [{"page_content": content, "metadata": {"source": file_path}}]
return loader.load()
def split_documents(self, documents: List) -> List:
"""Chia nhỏ tài liệu thành các chunk có ngữ cảnh"""
chunks = self.splitter.split_documents(documents)
# Thêm metadata về vị trí chunk
for idx, chunk in enumerate(chunks):
chunk.metadata["chunk_id"] = idx
chunk.metadata["chunk_size"] = len(chunk.page_content)
return chunks
def process_file(self, file_path: str) -> List:
"""Xử lý một file hoàn chỉnh"""
docs = self.load_document(file_path)
return self.split_documents(docs)
Sử dụng
chunker = DocumentChunker(chunk_size=500, chunk_overlap=50)
chunks = chunker.process_file("tai_lieu_mau.txt")
print(f"Đã chia thành {len(chunks)} đoạn nhỏ")
Bước 3: Tạo vector embeddings và lưu trữ
Đây là bước quan trọng nhất - chuyển text thành số để máy tính có thể tìm kiếm:
from openai import OpenAI
Kết nối HolySheep AI - base_url bắt buộc
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY", # Thay bằng key của bạn
base_url="https://api.holysheep.ai/v1" # KHÔNG dùng api.openai.com
)
def create_embeddings(texts: List[str], model: str = "text-embedding-3-small") -> List[List[float]]:
"""Tạo vector embeddings cho danh sách văn bản"""
embeddings = []
batch_size = 100
for i in range(0, len(texts), batch_size):
batch = texts[i:i + batch_size]
response = client.embeddings.create(
model=model,
input=batch
)
embeddings.extend([item.embedding for item in response.data])
print(f"Đã xử lý {min(i + batch_size, len(texts))}/{len(texts)} văn bản")
return embeddings
def store_vectors_in_faiss(chunks: List, embeddings: List[List[float]]):
"""Lưu trữ vectors trong FAISS để tìm kiếm nhanh"""
import faiss
import numpy as np
embedding_matrix = np.array(embeddings).astype('float32')
dimension = embedding_matrix.shape[1]
# Sử dụng IndexFlatIP cho similarity search
index = faiss.IndexFlatIP(dimension)
index.add(embedding_matrix)
# Lưu index
faiss.write_index(index, "vector_store.index")
print(f"Đã lưu {index.ntotal} vectors vào vector_store.index")
return index
Chạy thực tế
texts = [chunk.page_content for chunk in chunks]
embeddings = create_embeddings(texts)
index = store_vectors_in_faiss(chunks, embeddings)
Chi phí thực tế với HolySheep: Model text-embedding-3-small chỉ $0.02/1 triệu ký tự (khoảng 0.00002 USD cho tài liệu 1000 trang). So với OpenAI $0.13/1M tokens - tiết kiệm hơn 85%.
Bước 4: Tìm kiếm và trả lời câu hỏi
import faiss
import numpy as np
def retrieve_relevant_chunks(query: str, chunks: List, index, top_k: int = 3) -> List[str]:
"""Tìm các đoạn liên quan nhất với câu hỏi"""
# Tạo embedding cho câu hỏi
response = client.embeddings.create(
model="text-embedding-3-small",
input=[query]
)
query_embedding = np.array([response.data[0].embedding]).astype('float32')
# Tìm top-k chunks gần nhất
distances, indices = index.search(query_embedding, top_k)
relevant_chunks = []
for idx, dist in zip(indices[0], distances[0]):
if idx < len(chunks):
relevant_chunks.append({
"text": chunks[idx].page_content,
"distance": float(dist),
"source": chunks[idx].metadata.get("source", "unknown")
})
return relevant_chunks
def answer_question(question: str, chunks: List, index) -> str:
"""Trả lời câu hỏi sử dụng RAG"""
# Bước 1: Tìm context liên quan
relevant = retrieve_relevant_chunks(question, chunks, index, top_k=3)
if not relevant:
return "Không tìm thấy thông tin liên quan trong tài liệu."
# Bước 2: Tạo prompt với context
context_text = "\n\n".join([f"[Đoạn {i+1}]: {item['text']}" for i, item in enumerate(relevant)])
prompt = f"""Dựa trên các đoạn trích từ tài liệu dưới đây, hãy trả lời câu hỏi một cách chính xác.
--- TÀI LIỆU ---
{context_text}
--- CÂU HỎI ---
{question}
--- YÊU CẦU ---
1. Trả lời bằng tiếng Việt
2. Trích dẫn nguồn nếu có thể
3. Nếu không chắc chắn, nói rõ bạn không biết
"""
# Bước 3: Gọi AI với context
response = client.chat.completions.create(
model="gpt-4.1", # Hoặc "claude-sonnet-4.5", "gemini-2.5-flash"
messages=[
{"role": "system", "content": "Bạn là trợ lý AI hữu ích, trả lời dựa trên context được cung cấp."},
{"role": "user", "content": prompt}
],
temperature=0.3,
max_tokens=1000
)
return response.choices[0].message.content
Chạy demo
câu_hỏi = "Nội dung chính của tài liệu là gì?"
câu_trả_lời = answer_question(câu_hỏi, chunks, index)
print(câu_trả_lời)
Cấu hình tối ưu cho từng loại tài liệu
| Loại tài liệu | Chunk size | Overlap | Model embedding |
|---|---|---|---|
| Tài liệu pháp lý, hợp đồng | 800-1000 | 100 | text-embedding-3-small |
| Báo cáo tài chính | 500-700 | 50 | text-embedding-3-small |
| Văn bản học thuật | 600-800 | 80 | text-embedding-3-large |
| Chat/Q&A đơn giản | 300-500 | 30 | text-embedding-3-small |
So sánh chi phí các model AI
HolySheep cung cấp nhiều model với mức giá khác nhau, phù hợp cho từng use case:
- DeepSeek V3.2: $0.42/1M tokens - Tốt nhất cho chi phí, đủ dùng cho hầu hết task
- Gemini 2.5 Flash: $2.50/1M tokens - Cân bằng giữa giá và chất lượng
- GPT-4.1: $8/1M tokens - Chất lượng cao, phù hợp task phức tạp
- Claude Sonnet 4.5: $15/1M tokens - Premium choice cho reasoning phức tạp
Mẹo tiết kiệm: Với chatbot FAQ đơn giản, dùng DeepSeek V3.2 + text-embedding-3-small, chi phí chỉ $0.44/1M tokens cho cả embedding + generation.
Lỗi thường gặp và cách khắc phục
1. Lỗi "Invalid API Key" hoặc "Authentication Error"
Nguyên nhân: API key không đúng hoặc chưa copy đủ ký tự.
# Sai - thiếu ký tự hoặc có khoảng trắng thừa
client = OpenAI(api_key=" YOUR_HOLYSHEEP_API_KEY ", base_url="...")
Đúng - không có khoảng trắng
client = OpenAI(
api_key="sk-holysheep-xxxxxxxxxxxx", # Copy chính xác từ dashboard
base_url="https://api.holysheep.ai/v1"
)
Cách khắc phục: Vào HolySheep Dashboard → API Keys → Tạo key mới → Copy toàn bộ chuỗi không có khoảng trắng đầu/cuối.
2. Lỗi "Connection timeout" hoặc "Request failed"
Nguyên nhân: Network chặn kết nối hoặc base_url sai.
# Sai - dùng nhầm domain của OpenAI
client = OpenAI(api_key="...", base_url="https://api.openai.com/v1")
Đúng - bắt buộc dùng domain của HolySheep
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1" # Phải chính xác
)
Thêm timeout để tránh hanging
response = client.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": "Hello"}],
timeout=30 # 30 giây
)
Cách khắc phục: Kiểm tra lại base_url, đảm bảo không có trailing slash. Nếu dùng proxy VPN, thử tắt và kiểm tra lại.
3. Kết quả tìm kiếm không liên quan
Nguyên nhân: Chunk size quá lớn hoặc quá nhỏ, overlap không phù hợp.
# Sai - chunk quá nhỏ, mất ngữ cảnh
chunker = DocumentChunker(chunk_size=100, chunk_overlap=10)
Sai - chunk quá lớn, nhiễu thông tin
chunker = DocumentChunker(chunk_size=2000, chunk_overlap=200)
Đúng - test và điều chỉnh theo loại tài liệu
chunker = DocumentChunker(
chunk_size=500, # Thử 300, 500, 800 để so sánh
chunk_overlap=50 # 10-20% của chunk_size
)
Nếu vẫn không tốt, thử hybrid search
def hybrid_search(query: str, chunks: List, index, top_k: int = 5):
# Kết hợp vector search + keyword search
vector_results = retrieve_relevant_chunks(query, chunks, index, top_k)
# Thêm bộ lọc keyword để cải thiện precision
keyword_filtered = [r for r in vector_results if query.lower() in r['text'].lower()]
return keyword_filtered[:top_k]
4. Memory Error khi embedding document lớn
Nguyên nhân: Đưa quá nhiều text vào một lần gọi API.
# Sai - batch quá lớn
response = client.embeddings.create(model="...", input=all_texts) # 10000+ texts
Đúng - xử lý theo batch nhỏ
def create_embeddings_safe(texts: List[str], batch_size: int = 50) -> List:
embeddings = []
for i in range(0, len(texts), batch_size):
batch = texts[i:i + batch_size]
try:
response = client.embeddings.create(
model="text-embedding-3-small",
input=batch
)
embeddings.extend([item.embedding for item in response.data])
print(f"✓ Batch {i//batch_size + 1}: {len(batch)} texts")
except Exception as e:
print(f"Lỗi batch {i//batch_size + 1}: {e}")
# Retry với batch nhỏ hơn
for text in batch:
resp = client.embeddings.create(model="...", input=[text])
embeddings.append(resp.data[0].embedding)
return embeddings
Kết luận
Triển khai RAG cho tài liệu lớn không khó như bạn nghĩ. Điểm mấu chốt nằm ở chiến lược chia chunk phù hợp với loại tài liệu và lựa chọn model tối ưu chi phí. Với HolySheep AI, bạn có thể bắt đầu với chi phí cực thấp, chỉ từ $0.42/1M tokens với DeepSeek V3.2.
Từ kinh nghiệm thực chiến của tôi: bắt đầu với chunk_size=500, overlap=50 cho hầu hết tài liệu, sau đó điều chỉnh dựa trên kết quả. Đừng quên lưu lại history của các lần thử để so sánh và tối ưu.