Xây chatbot RAG bằng n8n và vector store trên VPS riêng

Bạn có một đống tài liệu nội bộ, tài liệu kỹ thuật, hoặc FAQ mà muốn biến thành một chatbot thông minh, trả lời chính xác dựa trên dữ liệu của bạn? Kiến trúc RAG (Retrieval-Augmented Generation) kết hợp với n8n và một vector store là giải pháp. Bài viết này sẽ hướng dẫn bạn từng bước xây dựng một chatbot RAG hoàn chỉnh, chạy trên VPS riêng, đảm bảo dữ liệu của bạn không rời khỏi máy chủ của bạn. Bạn sẽ cần một VPS với Docker và một chút kiên nhẫn.
Yêu cầu trước khi bắt đầu
- Một VPS chạy Ubuntu 24.04 LTS hoặc Debian 12, với tối thiểu 4GB RAM và 2 vCPU (khuyến nghị 8GB RAM cho production). Bạn có thể thuê VPS Linux tại thueVPS với cấu hình VNx4 hoặc VNx8.
- Docker và Docker Compose đã được cài đặt.
- Một domain (tùy chọn) để truy cập n8n và Qdrant qua HTTPS.
- Nguyên tắc cơ bản về JSON và API.
Vì sao nên xây chatbot RAG thay vì dùng pure LLM?
Mô hình LLM thuần (như GPT-4, Claude) có kiến thức cắt tại một thời điểm. Nếu bạn hỏi về một chính sách nội bộ được cập nhật hôm qua, LLM sẽ không biết hoặc sẽ "bịa" ra câu trả lời. RAG giải quyết vấn đề này: nó chuyển câu hỏi thành vector, tìm kiếm các đoạn văn bản liên quan trong vector store, rồi đưa các đoạn đó vào prompt cho LLM. Kết quả là câu trả lời dựa trên dữ liệu thực tế, có trích dẫn nguồn.
Một lợi ích lớn khác: chủ sở hữu dữ liệu. Tất cả dữ liệu và vector hóa đều ở trên VPS của bạn, không gửi lên cloud của bên thứ ba. Điều này quan trọng với dữ liệu nhạy cảm.
Với n8n, bạn sẽ làm chủ luồng xử lý. Bạn có thể tùy chỉnh mọi thứ: từ cách chunk văn bản, model embedding, đến prompt template. Không bị khóa vào bất kỳ nền tảng nào.
Kiến trúc tổng quan và các thành phần
Hệ thống RAG của chúng ta gồm 3 thành phần chính:
- n8n: Công cụ automation, đóng vai trò orchestrator. Chứa các workflow để nhập dữ liệu, tạo vector, và trả lời câu hỏi.
- Vector Store (Qdrant): Cơ sở dữ liệu vector. Nơi lưu trữ các embedding của tài liệu. Qdrant nổi bật nhờ tốc độ, hiệu năng cao và dễ self-host. Chúng ta sẽ dùng Qdrant.
- LLM (qua API hoặc self-host): Model sinh câu trả lời. Bạn có thể dùng OpenAI API, Anthropic Claude, hoặc tự host một model nhỏ như Llama 3.2 3B qua Ollama. Lưu ý: nếu tự host LLM, VPS 4GB RAM sẽ không đủ; cần ít nhất 16GB RAM và GPU (hoặc dùng CPU với lượng token nhỏ).
Luồng hoạt động: User hỏi → Workflow n8n nhận câu hỏi → Embedding câu hỏi thành vector → Query vector store → Lấy top-k đoạn văn bản liên quan → Ghép các đoạn đó vào prompt → Gửi prompt đến LLM → Nhận câu trả lời → Gửi lại user.
Bước 1 - Cài đặt n8n và Qdrant trên VPS
Chúng ta sẽ dùng Docker Compose để chạy cả n8n và Qdrant trong cùng một network. Tạo thư mục dự án và file docker-compose.yml:
mkdir ~/rag-chatbot && cd ~/rag-chatbot
nano docker-compose.yml
Nội dung file:
version: '3.8'
services:
qdrant:
image: qdrant/qdrant:latest
container_name: qdrant
restart: unless-stopped
ports:
- "6333:6333"
- "6334:6334"
volumes:
- ./qdrant_storage:/qdrant/storage
environment:
QDRANT__SERVICE__GRPC_PORT: 6334
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
volumes:
- ./n8n_data:/home/node/.n8n
- ./local_files:/files
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=your_strong_password
- N8N_HOST=your_domain_or_ip:5678
- N8N_PROTOCOL=http
- WEBHOOK_URL=http://your_domain_or_ip:5678/
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=168
Giải thích: Qdrant mở cổng 6333 (REST API) và 6334 (gRPC). n8n mở cổng 5678. Bạn phải thay đổi N8N_BASIC_AUTH_PASSWORD bằng một mật khẩu mạnh. Nếu có domain, hãy thêm cấu hình Nginx reverse proxy + Let's Encrypt SSL như bài viết cấu hình Nginx reverse proxy và SSL cho n8n.
Khởi động:
docker compose up -d
Kiểm tra: docker compose ps thấy 2 container trạng thái Up. Truy cập http://IP_VPS:5678 để vào n8n.
Bước 2 - Cấu hình Qdrant và tạo collection
Qdrant đã chạy. Ta cần tạo một collection để lưu vector. Dùng curl hoặc gửi POST request tới API.
curl -X PUT 'http://localhost:6333/collections/my_docs' \
-H 'Content-Type: application/json' \
-d '{
"vectors": {
"size": 1536,
"distance": "Cosine"
}
}'
Giải thích: size: 1536 là kích thước vector của model embedding "text-embedding-ada-002" (OpenAI). Nếu bạn dùng model embedding khác (ví dụ từ Mistral hoặc self-host), hãy thay đổi con số này. distance: Cosine là metric tính độ tương đồng, phù hợp cho text.
Kiểm tra collection đã được tạo:
curl 'http://localhost:6333/collections/my_docs' | jq .
Output mong đợi: "status": "ok".
Bước 3 - Tạo workflow nhập dữ liệu (Indexing Pipeline)
Đăng nhập vào n8n. Tạo một workflow mới. Đây là pipeline để chuyển tài liệu (ví dụ file .txt hoặc .pdf) thành vector và lưu vào Qdrant.
- Node 1: Read Binary File (hoặc HTTP Request) - Để đọc file từ thư mục
/filestrên container. Tạo node kiểuRead Binary Files, đường dẫn/files/document.txt. - Node 2: Extract Document Text - Nếu file là .txt, bạn có thể dùng node
Extract From File. Nếu là .pdf, cần dùng nodeHTML Extracthoặc code để parse. - Node 3: Text Splitter (Chunking) - Quan trọng. Thêm node
Codevới JavaScript để chia nhỏ văn bản thành các chunk. Ví dụ: mỗi chunk ~500 ký tự, overlap 50 ký tự. - Node 4: Embedding - Dùng node
OpenAIvới actionCreate Embedding. Modeltext-embedding-ada-002. Output là các vector (mảng số). Bạn cần có OpenAI API key, lưu vào Credentials. - Node 5: Qdrant Upsert - Dùng node
HTTP Requestgửi POST đếnhttp://qdrant:6333/collections/my_docs/points?wait=true. Body là JSON chứa vector và payload (đoạn văn bản gốc, metadata).
Mẫu code cho node Text Splitter (Code):
const text = $input.first().json["data"];
const chunkSize = 500;
const overlap = 50;
const chunks = [];
let i = 0;
while (i < text.length) {
const end = Math.min(i + chunkSize, text.length);
chunks.push(text.slice(i, end));
i += chunkSize - overlap;
}
return chunks.map((chunk, idx) => ({ json: { text: chunk, index: idx } }));
Đối với node Qdrant Upsert, body mẫu:
{
"points": [
{
"id": {{ $json["index"] }},
"vector": {{ JSON.stringify($node["Embedding"].json["data"]) }},
"payload": {
"text": "{{ $json["text"] }}",
"source": "document.txt"
}
}
]
}
Workflow này chỉ cần chạy một lần cho mỗi tài liệu mới. Bạn có thể kích hoạt nó bằng Webhook hoặc Manual Execution.
Bước 4 - Tạo workflow hỏi đáp (Query Pipeline)
Tạo workflow thứ hai, đây là chatbot. Workflow này sẽ lắng nghe câu hỏi từ người dùng.
- Node 1: Webhook - Nhận câu hỏi từ user qua POST request hoặc Telegram bot. Để đơn giản, dùng node
Webhookvới path/ask. - Node 2: Embedding câu hỏi - Dùng lại node
OpenAIđể embedding câu hỏi của user. - Node 3: Qdrant Search - Gửi POST đến
http://qdrant:6333/collections/my_docs/points/searchvới vector câu hỏi. Lấy top 3-5 kết quả (top 3 là đủ). - Node 4: Xây dựng Prompt - Node
Codeghép các đoạn văn bản lấy được từ Qdrant thành một prompt. Prompt mẫu:
Sử dụng các đoạn văn bản dưới đây để trả lời câu hỏi của người dùng.
Nếu không có thông tin, hãy nói rằng bạn không biết.
Không sử dụng kiến thức bên ngoài các đoạn văn bản đã cho.
Đoạn văn bản:
{% for item in $json["result"] %}
- {{ item.payload.text }}
{% endfor %}
Câu hỏi: {{ $json["question"] }}
- Node 5: LLM - Gửi prompt tới LLM. Dùng node
OpenAIvới modelgpt-4o-mini(rẻ, nhanh) hoặcgpt-4o. Lưu ý: Nếu bạn muốn tự chủ hoàn toàn, hãy cài Ollama và Open WebUI trên VPS, trỏ nodeHTTP Requesttới API của Ollama. - Node 6: Response - Output kết quả về webhook.
Bước 5 - Kiểm tra và tinh chỉnh
Sau khi hai workflow được kích hoạt (active), test bằng cách gửi POST request tới webhook http://IP_VPS:5678/webhook/ask với body JSON:
{
"question": "Chính sách bảo mật dữ liệu của công ty là gì?"
}
Nếu dữ liệu bạn đã nhập có chứa nội dung về chính sách bảo mật, chatbot sẽ trả lời dựa trên các đoạn văn bản đó. Lưu ý quan trọng:
- Chunk size quyết định độ chính xác. Chunk nhỏ (200-300 ký tự) cho kết quả chính xác hơn nhưng thiếu ngữ cảnh. Chunk lớn (1000 ký tự) ngược lại. Hãy tùy chỉnh cho phù hợp với tài liệu của bạn.
- Số lượng chunk trả về (top-k): 3-5 là đủ. Quá nhiều sẽ làm prompt bị loãng, LLM dễ bị phân tâm.
- Model embedding
text-embedding-3-smallcủa OpenAI mới hơn, cho chất lượng tốt hơn, kích thước vector 512 (hoặc 1536). Hãy dùng model này nếu có API key.
Xử lý lỗi thường gặp
- Lỗi "Connection refused" khi n8n gọi Qdrant: Kiểm tra container name có đúng là
qdrantkhông. Trong node HTTP Request, URL phải làhttp://qdrant:6333(không phải localhost). Nếu vẫn lỗi, kiểm tra network của docker compose. - Lỗi "Cannot find collection" khi search: Đảm bảo bạn đã tạo collection với đúng kích thước vector (size). Nếu không chắc, dùng
curlđể kiểm tra collection info. - Embedding lỗi do API key: Vào Credentials trong n8n, kiểm tra OpenAI API key còn hạn và có quyền truy cập model embedding.
- Chatbot trả lời chung chung, không dùng dữ liệu: Prompt của bạn có thể không đủ chặt. Hãy thêm "Chỉ trả lời dựa trên các đoạn văn bản sau. Nếu không có, nói 'Tôi không có thông tin về vấn đề này'."
- Out of Memory (RAM): Qdrant và n8n đều nhẹ, nhưng LLM (nếu tự host) sẽ ngốn RAM. Nếu VPS 4GB RAM, chỉ dùng API, không tự host LLM.
Câu hỏi thường gặp
Tôi có thể dùng vector store nào khác ngoài Qdrant không?
Có. n8n hỗ trợ nhiều vector store thông qua các node cộng đồng và HTTP Request. Bạn có thể dùng Pinecone (SaaS), Weaviate, hay Chroma. Tuy nhiên, Qdrant là lựa chọn dễ self-host nhất. Pinecone không cần tự host nhưng có chi phí theo dung lượng.
Tôi có cần phải biết lập trình để xây dựng workflow không?
Không bắt buộc. n8n cho phép kéo thả các node. Tuy nhiên, bạn sẽ cần viết một chút JavaScript/JSON cho các node Code và HTTP Request để tùy chỉnh. Bài hướng dẫn này đã cung cấp các đoạn code sẵn, bạn chỉ cần copy và sửa cho phù hợp.
Chi phí vận hành chatbot RAG này là bao nhiêu?
Chi phí gồm: thuê VPS (từ 189.000đ/tháng cho VPS 2GB RAM, nhưng khuyến nghị 4-8GB RAM cho sản phẩm), và phí API của LLM (OpenAI: rất rẻ, ~$0.15 cho 1 triệu token đầu vào). Nếu bạn dùng model tự host (qua Ollama), chỉ tốn điện và tài nguyên VPS, không có phí API.
Làm sao để chatbot có thể xử lý tài liệu PDF?
n8n không có node mặc định để parse PDF. Bạn có thể dùng node HTTP Request gọi API của một dịch vụ parse PDF (ví dụ: PDF.co), hoặc dùng một trong các node cộng đồng. Cách khác: chuyển PDF sang text trước bằng công cụ pdftotext trên VPS rồi đọc file .txt.
Tôi có thể tích hợp chatbot này vào website WordPress không?
Có. Workflow webhook có thể trả về JSON. Bạn viết một plugin WordPress nhỏ, hoặc dùng một plugin chatbot có sẵn để gọi đến webhook n8n của bạn. Đọc thêm về 10 workflow n8n hữu ích webmaster nên biết và tự host.
Bài viết liên quan
- Tự dựng AI chatbot riêng với Ollama và Open WebUI trên VPS
- Cài n8n trên VPS Ubuntu 24.04 bằng Docker Compose từ A-Z
- Cấu hình Nginx reverse proxy và SSL Let’s Encrypt cho n8n
- n8n là gì và khi nào nên tự host thay vì dùng bản cloud


