Sau 3 năm triển khai AI cho các doanh nghiệp, tôi thấy một pattern lặp đi lặp lại: team kỹ thuật dành 6 tháng fine-tuning model, tốn $200k compute budget, rồi phát hiện RAG với $500/tháng vector DB đạt kết quả tốt hơn. Fine-tuning có chỗ đứng, nhưng với 90% use case doanh nghiệp — Q&A trên tài liệu nội bộ, search sản phẩm, customer support — RAG thực dụng hơn, rẻ hơn, và quan trọng nhất: dễ cập nhật khi dữ liệu thay đổi.

Bài này không phải tutorial. Đây là những quyết định kiến trúc tôi đã phải đưa ra và trả giá khi sai.

Kiến Trúc RAG Cơ Bản

RAG gồm hai pipeline độc lập: indexing (chạy offline) và retrieval + generation (chạy real-time).

Indexing Pipeline

Documents → Chunking → Embedding → Vector Store

Đây là nơi phần lớn team mắc sai lầm. Họ xử lý indexing như một bước đơn giản, sau đó mất tuần debugging tại sao retrieval kém.

Retrieval + Generation Pipeline

User Query → Embed Query → ANN Search → Rerank → LLM Generation → Response

ANN (Approximate Nearest Neighbor) search trả về top-k candidates, reranker lọc lại theo relevance thực sự, LLM dùng context để trả lời. Mỗi bước đều có tuning parameters ảnh hưởng trực tiếp đến chất lượng cuối.

Một điều tôi học được: retrieval quality quyết định 70% chất lượng output cuối. LLM giỏi đến đâu cũng không tạo ra được thông tin mà retrieval không lấy về.

Chọn Vector Database

Đây là câu hỏi tôi nhận nhiều nhất. Câu trả lời thực dụng: bắt đầu với pgvector, upgrade khi thực sự cần.

So Sánh Thực Chiến

pgvectorQdrantWeaviatePinecone
SetupDễ (extension Postgres)Docker, đơn giảnPhức tạpManaged, zero ops
Scale~5M vectors~100M vectors~100M vectorsUnlimited
Chi phíGần như miễn phíOpen sourceOpen source$70-700+/tháng
FilteringTốt (SQL)Rất tốtTốtHạn chế
OperationalQuen thuộcCần họcCần họcZero maintenance

Pinecone phù hợp khi team không muốn manage infrastructure và scale cần thiết. Tôi thấy nhiều startup dùng Pinecone không phải vì performance tốt hơn mà vì không có DevOps bandwidth — đó là quyết định kinh doanh hợp lý.

Qdrant là lựa chọn tốt nhất nếu cần self-host với scale lớn. Sparse+dense hybrid search, filtering hiệu quả, Rust-based nên performance tốt.

pgvector bị underrated. Nếu bạn đã có Postgres, thêm pgvector mà không cần thêm infrastructure. Với dưới 1 triệu vectors và queries không quá phức tạp, hiệu năng đủ dùng.

-- Setup pgvector trong 2 dòng
CREATE EXTENSION vector;
ALTER TABLE documents ADD COLUMN embedding vector(1536);
 
-- Query
SELECT content, 1 - (embedding <=> query_embed) AS similarity
FROM documents
ORDER BY embedding <=> query_embed
LIMIT 10;

Tôi không recommend Weaviate cho team mới. GraphQL API, schema management phức tạp, documentation không nhất quán — overhead không xứng với lợi ích so với Qdrant.

Chunking Strategy

Đây là phần ít được nói đến nhất nhưng ảnh hưởng lớn nhất đến chất lượng. Tôi đã thấy RAG system tệ chỉ vì chunking strategy tệ.

Fixed-size Chunking — Đừng Dùng Mặc Định

# Cách naive - tránh dùng
text_splitter = CharacterTextSplitter(chunk_size=512, chunk_overlap=50)

512 tokens nghe có vẻ hợp lý nhưng cắt đứt context. Một câu bắt đầu ở cuối chunk, kết thúc ở đầu chunk tiếp theo — model nhận được half-context, trả lời sai.

Semantic Chunking — Tốt Hơn Đáng Kể

Chunk theo paragraph boundaries hoặc semantic similarity breakpoints. Mỗi chunk là một "unit of thought" hoàn chỉnh.

from langchain.text_splitter import RecursiveCharacterTextSplitter
 
# Tốt hơn: respect document structure
splitter = RecursiveCharacterTextSplitter(
    separators=["\n\n", "\n", ".", "!", "?"],
    chunk_size=800,
    chunk_overlap=100
)

Hierarchical Chunking — Production Standard

Lưu trữ cả small chunks (150-200 tokens) để search chính xác lẫn large chunks (1000-1500 tokens) để context đầy đủ khi generation. Search dùng small, generate dùng large.

Chunk size tối ưu phụ thuộc vào loại tài liệu:

  • Legal documents, contracts: 1000-1500 tokens (context quan trọng)
  • FAQ, support tickets: 200-300 tokens (câu trả lời ngắn, cụ thể)
  • Technical docs: 500-800 tokens

Nguyên tắc: mỗi chunk phải có thể trả lời ít nhất một câu hỏi hoàn chỉnh.

Đánh Giá Chất Lượng RAG với RAGAS

Đây là vấn đề với hầu hết RAG project: không ai đo lường đúng. "Có vẻ tốt hơn" không phải metric.

RAGAS framework đo bốn chiều:

Faithfulness — LLM có trả lời dựa trên context không, hay hallucinate? Đây là metric quan trọng nhất cho enterprise.

Answer Relevancy — Answer có thực sự trả lời câu hỏi không?

Context Recall — Retrieval có lấy về đủ thông tin cần thiết không?

Context Precision — Trong những gì retrieved, bao nhiêu phần thực sự relevant?

from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_recall
 
results = evaluate(
    dataset=test_dataset,
    metrics=[faithfulness, answer_relevancy, context_recall]
)
print(results)
# {'faithfulness': 0.82, 'answer_relevancy': 0.79, 'context_recall': 0.71}

Benchmark tôi thường dùng để đánh giá system đủ tốt cho production:

  • Faithfulness > 0.85
  • Answer relevancy > 0.80
  • Context recall > 0.75

Nếu context recall thấp, vấn đề ở retrieval/chunking. Nếu faithfulness thấp, vấn đề ở LLM generation hoặc context quá nhiễu.

Tạo test dataset đúng cách: Đừng chỉ test với câu hỏi dễ. Thêm adversarial queries, ambiguous questions, và multi-hop reasoning questions.

Scaling RAG

Prototype đến 100K documents

Stack đơn giản nhất hoạt động được:

  • pgvector hoặc Qdrant single instance
  • OpenAI ada-002 embeddings
  • Chunking đơn giản với overlap
  • Không cần reranker

Chi phí điển hình: $200-500/tháng tùy query volume.

100K đến 10M documents

Đây là điểm nhiều team bị "scale shock". Vấn đề thực sự không phải vector search (ANN scale tốt) mà là:

  1. Embedding latency: Với 10M documents, re-embedding khi update model tốn 2-3 tuần. Cần async pipeline.
  2. Filtering overhead: Metadata filtering trên 10M records cần index strategy cẩn thận.
  3. Reranker cost: Cross-encoder reranker tốn gấp 5-10x so với ANN search.
# Async indexing pipeline với queue
async def index_document(doc_id: str, content: str):
    chunks = chunk_document(content)
    embeddings = await batch_embed(chunks)  # batch 100 chunks/request
    await vector_store.upsert(zip(chunks, embeddings))
    await queue.ack(doc_id)

Batch embedding quan trọng — gọi embedding API với 100 chunks/request thay vì 1 chunk/request giảm latency 80% và chi phí 50%.

10M documents trở lên

Ở scale này, bạn cần:

  • Sharding vector index theo domain/category
  • Hybrid search (dense + sparse/BM25) để handle keyword-specific queries
  • Caching layer cho frequent queries (Redis với TTL 1 giờ reduce 40-60% LLM calls)
# Hybrid search: kết hợp semantic + keyword
semantic_results = await vector_store.search(query_embedding, top_k=20)
keyword_results = await bm25_index.search(query_text, top_k=20)
reranked = await reranker.rank(query, semantic_results + keyword_results, top_k=5)

Đừng bỏ qua caching. Trong production, 30-40% queries là repeat hoặc near-duplicate. Cache embedding + retrieval results giảm latency và cost đáng kể.

Decision Matrix Chọn Kiến Trúc

Use CaseVector DBChunk SizeRerankerExpected Quality
Internal docs < 100Kpgvector500-800 tokensKhông cầnTốt
Customer support > 1M ticketsQdrant200-300 tokensCầnTốt nếu tune kỹ
Legal/compliance docsWeaviate hoặc Qdrant1000-1500 tokensCần thiếtPhụ thuộc data quality
Multi-tenant SaaSPinecone hoặc QdrantTheo use caseTùy budgetTốt
Real-time dataQdrant + streaming300-500 tokensTùy latency reqChấp nhận được

Yếu tố quyết định thực sự:

  1. Data update frequency: Dữ liệu thay đổi thường xuyên? Cần async indexing pipeline ngay từ đầu.
  2. Query complexity: Queries cần multi-hop reasoning cần hierarchical chunking và reranker.
  3. Latency requirement: < 500ms end-to-end? Cần aggressive caching và skip reranker hoặc dùng lightweight cross-encoder.
  4. Team bandwidth: Không có MLOps? Dùng managed solution (Pinecone) dù đắt hơn.

Kết Luận

RAG không phức tạp về mặt thuật toán, nhưng phức tạp về engineering khi scale. 80% dự án thất bại không phải vì chọn sai vector DB hay LLM — mà vì chunking tệ, thiếu evaluation framework, và không có monitoring.

Ba điều làm ngay nếu bạn đang build RAG: implement RAGAS evaluation trước khi lên production, dùng hierarchical chunking thay vì fixed-size, và cache aggressively. Những việc này không tốn nhiều effort nhưng cải thiện system đáng kể.

Scale sau khi có data. Đừng architect cho 100M documents khi bạn đang có 10K.