Bạn deploy một chatbot AI cho công ty.
Khách hàng hỏi: “Chính sách hoàn tiền của chúng ta là gì?” AI tự tin trả lời với một chính sách bịa ra. Khách hàng phàn nàn. Bạn mất khách.
Đây được gọi là ảo giác (hallucination) — tội nguyên thủy của các Mô hình Ngôn ngữ Lớn. Giải pháp là RAG (Retrieval Augmented Generation).
Đây là Hướng dẫn chuyên sâu để xây dựng hệ thống AI production không nói dối.
Phần 1: Nền tảng (Mô hình tư duy)
Thực tập sinh Quá tự tin
Một LLM (như GPT-4, Claude, Gemini) là thực tập sinh xuất sắc đã đọc toàn bộ internet đến một thời điểm nhất định. Họ rất giỏi lý luận, viết lách và tóm tắt.
Vấn đề: Họ không có quyền truy cập vào tài liệu nội bộ, trang Notion, hay dữ liệu khách hàng tuần trước của bạn. Khi được hỏi, họ tự tin bịa ra câu trả lời thay vì nói “Tôi không biết.” Đây là hallucination.
RAG = Thủ thư Thông minh
RAG trao cho thực tập sinh một thủ thư có thể tìm kiếm tài liệu liên quan trước khi họ trả lời.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| User: "Giá dịch vụ Enterprise là bao nhiêu?"
│
▼
[1. Retriever]: Tìm kiếm kho tài liệu của bạn
│ → tìm thấy bang_gia_2026.pdf, enterprise_FAQ.md
▼
[2. Augment]: Nhúng tài liệu vào prompt của LLM:
"Đây là thông tin liên quan: [nội dung bang_gia_2026.pdf]...
Bây giờ hãy trả lời: Giá Enterprise là bao nhiêu?"
│
▼
[3. Generate]: LLM trả lời DỰA TRÊN tài liệu thực
│
▼
Kết quả: "Gói Enterprise bắt đầu từ $999/tháng (Nguồn: bang_gia_2026.pdf)"
|
Kết quả: Câu trả lời có cơ sở. Có nguồn trích dẫn. Không hallucination.
Phần 2: Điều tra (Các thành phần RAG Pipeline)
1. Vấn đề Chunking (Chia nhỏ tài liệu)
Bạn không thể nhét file PDF 500 trang thẳng vào LLM (giới hạn context window). Bạn phải chunk nó trước.
1
2
3
4
5
6
7
8
9
10
| from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=512, # ~512 token mỗi chunk
chunk_overlap=50, # 50 token chồng lấn để tránh cắt ngang câu
separators=["\n\n", "\n", ". ", " "] # Cắt tại ranh giới tự nhiên
)
chunks = splitter.split_text(document_text)
# Kết quả: ["Chính sách hoàn tiền quy định...", "Với gói Enterprise...", ...]
|
Chunking là yếu tố ảnh hưởng nhiều nhất đến chất lượng RAG. Quá lớn → context không liên quan làm nhiễu câu trả lời. Quá nhỏ → mất ý nghĩa cấp đoạn văn.
2. Embedding (Chuyển văn bản thành toán học)
Mỗi chunk được chuyển thành một vector — danh sách số đại diện cho ý nghĩa ngữ nghĩa của nó.
1
2
3
4
5
6
7
8
9
10
| from openai import OpenAI
client = OpenAI()
def embed(text: str) -> list[float]:
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding # [0.23, -0.87, 0.12, ...] (1536 chiều)
|
Điểm mấu chốt: “Chó” và “Cún” sẽ có vector tương tự. “Chó” và “Cơ sở dữ liệu” sẽ có vector rất khác. Đây là độ tương đồng ngữ nghĩa.
3. Vector Database (Lưu trữ và Tìm kiếm Embedding)
Bạn không thể tìm kiếm qua 1 triệu embedding bằng câu WHERE đơn giản. Bạn cần Vector DB.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import chromadb
client = chromadb.Client()
collection = client.create_collection("tai_lieu_cong_ty")
# Index tài liệu
collection.add(
documents=chunks,
embeddings=[embed(c) for c in chunks],
ids=[f"chunk_{i}" for i in range(len(chunks))]
)
# Query: tìm chunk liên quan nhất cho câu hỏi
results = collection.query(
query_embeddings=[embed("Chính sách hoàn tiền là gì?")],
n_results=3 # Top 3 chunk tương đồng nhất
)
|
Phần 3: Chẩn đoán (Khi RAG thất bại)
| Vấn đề | Triệu chứng | Cách sửa |
|---|
| Chunking tệ | Câu trả lời dùng sai phần tài liệu | Điều chỉnh chunk_size + thêm metadata (số trang, tiêu đề mục) |
| Model embedding sai | Retrieval tìm chunk không liên quan | Dùng model chuyên domain hoặc text-embedding-3-large |
| Tràn context window | LLM bỏ qua chunk sau (“Lost in the Middle”) | Giảm n_results. Rerank. Đặt chunk quan trọng nhất đầu tiên. |
| Knowledge base lỗi thời | Tài liệu được cập nhật nhưng chưa re-index | Xây pipeline ingestion tự động khi tài liệu thay đổi |
| LLM bỏ qua context | Vẫn hallucinate dù retrieval tốt | Củng cố system prompt: “Chỉ trả lời từ context. Không có thì nói ‘Tôi không có thông tin này’.” |
Phần 4: Giải pháp (RAG Stack cho Production)
Bộ Tối giản (Python)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| from openai import OpenAI
import chromadb
client = OpenAI()
db = chromadb.Client()
collection = db.get_collection("tai_lieu_cong_ty")
SYSTEM_PROMPT = """Bạn là trợ lý của Công ty Acme.
Chỉ trả lời câu hỏi dựa trên context được cung cấp.
Nếu câu trả lời không có trong context, hãy nói "Tôi không có thông tin này."
Luôn trích dẫn nguồn."""
def rag_query(question: str) -> str:
# 1. Tìm chunk liên quan
results = collection.query(
query_embeddings=[embed(question)],
n_results=3
)
context = "\n\n---\n\n".join(results["documents"][0])
# 2. Bổ sung context vào prompt
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Context:\n{context}\n\nCâu hỏi: {question}"}
]
# 3. Tạo câu trả lời có cơ sở
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
temperature=0 # Nhiệt độ thấp = thực tế hơn, ít sáng tạo hơn
)
return response.choices[0].message.content
|
Nâng cao: Reranking
Sau khi retrieval, dùng Cross-Encoder để xếp hạng lại chunk theo độ liên quan:
1
2
3
4
5
6
7
8
9
| from sentence_transformers import CrossEncoder
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
def rerank(question: str, chunks: list[str], top_k: int = 2) -> list[str]:
pairs = [(question, chunk) for chunk in chunks]
scores = reranker.predict(pairs)
ranked = sorted(zip(scores, chunks), reverse=True)
return [chunk for _, chunk in ranked[:top_k]]
|
Mô hình tư duy chốt hạ
1
2
3
4
5
6
7
8
9
10
| LLM đơn thuần -> Thực tập sinh Quá tự tin. Thông minh nhưng bịa chuyện.
RAG -> Thực tập sinh + Thủ thư. Có cơ sở, có nguồn trích dẫn, chính xác.
Chunking -> Chia thư viện thành các flashcard.
Embedding -> Chuyển nội dung flashcard thành tọa độ bản đồ.
Vector DB -> Cái bản đồ. "Tìm tất cả tọa độ gần câu hỏi này."
Reranking -> Chuyên gia thứ hai đọc 10 ứng viên và chọn ra 2 tốt nhất.
temperature=0 -> "Hãy là luật sư. Chỉ nói sự thật."
temperature=1 -> "Hãy là nhà thơ. Hãy sáng tạo."
|
RAG không phải tùy chọn cho sản phẩm AI production. Không có nó, bạn đang ship một AI tự tin bịa chuyện về chính công ty của bạn.