Kiến trúc chatbot KB hiện đại mặc định
Tìm kiếm KB truyền thống trả về các liên kết; người dùng tự đọc chúng. RAG biến điều đó thành câu trả lời hội thoại một lần kèm trích dẫn. Làm tốt, người dùng nhận được câu trả lời trong 2 giây thay vì phải đào qua 5 tài liệu. Làm tệ, chatbot bịa đặt và CTO của bạn cấm dự án trong một năm. Trang này giải thích sự khác biệt.
Stack production khả dụng tối thiểu
- Vector store: pgvector nếu bạn đã có Postgres, Pinecone hoặc Qdrant cho dịch vụ được quản lý
- Embeddings: text-embedding-3-large, gọi trực tiếp đến OpenAI — Kunavo không cung cấp embeddings, và dù thế nào thì lệnh gọi embedding cũng là một request riêng với lệnh gọi generation
- Retrieval: hybrid (vector + BM25 với hợp nhất thứ hạng tương hỗ)
- Generation: Claude Sonnet 5 với cache_control trên system prompt
- UI: phản hồi dạng streaming, hiển thị trích dẫn, phương án dự phòng "Tôi không biết"
from openai import OpenAI
client = OpenAI(api_key="sk-kn-...", base_url="https://api.kunavo.com/v1")
def chat(question: str, history: list[dict]) -> dict:
chunks = hybrid_retrieve(question, k=5) # vector + BM25
context = "\n\n---\n\n".join(
f"[doc:{c['id']}] {c['text']}" for c in chunks
)
resp = client.chat.completions.create(
model="claude-sonnet-5",
messages=[
{"role": "system", "content": [{
"type": "text",
"text": (
"Answer only from Context. Cite [doc:N] for each claim. "
"If Context doesn't answer, say 'I don't have that.' "
"Be concise, no throat-clearing."
),
"cache_control": {"type": "ephemeral"},
}]},
*history,
{"role": "user", "content": f"# Context\n{context}\n\n# Q\n{question}"},
],
max_tokens=600,
)
answer = resp.choices[0].message.content
cited_ids = parse_citations(answer) # extract [doc:N] references
return {"answer": answer, "sources": [c for c in chunks if c["id"] in cited_ids]}Chi phí ở quy mô production
- Lập chỉ mục ban đầu: khoảng $0,25 cho 5.000 tài liệu, mỗi tài liệu 500 token
- 1.000 truy vấn/ngày: khoảng $210/tháng khi bật caching
- 10.000 truy vấn/ngày: khoảng $2.100/tháng
- Nếu dùng Haiku 4.5 thay vì Sonnet: rẻ hơn khoảng 4 lần, chất lượng câu trả lời khoảng 85%
Phân tích kiến trúc đầy đủ trong hướng dẫn triển khai RAG. Ghi chú tinh chỉnh theo ngôn ngữ trong phân tích chuyên sâu RAG tiếng Nhật và hướng dẫn RAG tiếng Tây Ban Nha.
Ba mẫu thực sự ngăn ảo giác
- Trích dẫn mọi khẳng định: các thẻ
[doc:42]trong đầu ra của model. Nếu id được trích dẫn không nằm trong tập đã truy xuất, đó là nội dung bịa đặt — chặn và ghi log - Từ chối rõ ràng trong system prompt: "Nếu Context không trả lời được, hãy nói 'Tôi không có thông tin đó.'" Nếu thiếu điều này, model sẽ tự điền bằng kiến thức thế giới
- Giới hạn đầu ra 600 token: câu trả lời ngắn thường chính xác hơn. Đầu ra dài là nơi các chi tiết bịa thêm dễ lọt vào
Nên phát hành gì trong tuần 1 so với tuần 4
| Tuần | Mốc quan trọng |
|---|---|
| 1 | 100 tài liệu, một chiến lược chunking, tìm kiếm vector cơ bản, bộ đánh giá 10 câu hỏi, recall@5 khoảng 70% |
| 2 | Toàn bộ corpus, retrieval hybrid, bộ đánh giá 100 câu hỏi, bắt buộc có trích dẫn, beta nội bộ |
| 3 | Tinh chỉnh chunking dựa trên các câu hỏi thất bại, phát hành cho người dùng nội bộ, đo CSAT |
| 4 | Giám sát + dashboard chi phí, giới hạn chi tiêu hằng ngày, beta công khai hoặc ra mắt production |
Bắt đầu tại /app/signup
Khoản nạp $10 đủ cho việc lập chỉ mục ban đầu khoảng 100K tài liệu và 1.500 truy vấn thử nghiệm — quá đủ cho một prototype hoạt động, và số dư không bao giờ hết hạn. Sau đó đọc hướng dẫn RAG đầy đủ để tìm các mẫu production.
Câu hỏi thường gặp
Một truy vấn chatbot RAG tốn bao nhiêu?
Khoảng $0,007 mỗi truy vấn với Claude Sonnet 4.6 khi bật bộ nhớ đệm cho lời nhắc, hoặc khoảng $0,001 với Claude Haiku 4.5 — mô hình này cho chất lượng khoảng 85% với 1/7 chi phí.
Làm thế nào để ngăn chatbot RAG tạo ra thông tin ảo giác?
Ba cách xử lý phần lớn vấn đề: yêu cầu mô hình trích dẫn ID [doc:N] cho mọi khẳng định, đưa hướng dẫn từ chối rõ ràng vào system prompt và giới hạn câu trả lời bằng max_tokens=600. Sau đó, xác minh mọi ID được trích dẫn có trong tập kết quả truy xuất trước khi hiển thị câu trả lời.
Nên dùng kích thước đoạn nào cho chỉ mục RAG?
1.000–1.500 ký tự mỗi đoạn, chồng lấn 100–200 ký tự, được tách bằng RecursiveCharacterTextSplitter và nhúng bằng text-embedding-3-large.
Chỉ vector search có đủ cho retrieval RAG không?
Không. Truy xuất kết hợp độ tương đồng vector với tìm kiếm từ khóa BM25, rồi hợp nhất hai kết quả bằng reciprocal rank fusion và lấy 5 đoạn đứng đầu.