Xây backend chatbot AI cho website trên VPS

Khách truy cập website của bạn đang tăng, và họ bắt đầu hỏi những câu lặp đi lặp lại: "đơn hàng của tôi đang ở đâu?", "chính sách đổi trả thế nào?". Bạn muốn một chatbot AI trả lời tự động, nhưng không muốn phụ thuộc hoàn toàn vào dịch vụ đám mây nước ngoài với chi phí khó lường và độ trễ cao. Bài viết này hướng dẫn bạn xây backend chatbot AI hoàn chỉnh trên VPS Linux, từ khâu chọn kiến trúc, viết API proxy, tích hợp vector store cho RAG đến giới hạn tốc độ. Kết quả là một hệ thống chạy ổn định, chi phí dự đoán được, dễ mở rộng khi website phát triển.
Yêu cầu trước khi bắt đầu
- Một VPS Linux, tối thiểu 2 GB RAM và 2 vCPU. Bài này dùng VPS Linux Ubuntu 24.04, nhưng lệnh cũng chạy tốt trên Debian 12 hoặc AlmaLinux 9.
- Quyền root hoặc user sudo.
- Một domain trỏ về IP VPS để cấu hình HTTPS (bắt buộc nếu website của bạn dùng HTTPS, trình duyệt sẽ chặn gọi API HTTP từ trang HTTPS).
- Một API key từ nhà cung cấp LLM. Có thể dùng OpenAI, Anthropic, hoặc tự host với Ollama nếu bạn muốn dữ liệu không rời khỏi máy chủ. Tham khảo bài tự host API LLM với Ollama trên VPS nếu chọn hướng này.
Vì sao backend chatbot không chỉ là gọi API
Nhiều người mới bắt đầu nghĩ chatbot AI chỉ đơn giản là gọi API từ JavaScript trên trình duyệt. Cách này nhanh nhưng nhanh chóng vỡ khi website có traffic thật. API key nằm trong mã nguồn frontend là lỗ hổng bảo mật nghiêm trọng, bất kỳ ai xem source cũng lấy được và dùng chùa. Chưa kể trình duyệt gọi thẳng tới provider LLM sẽ dính lỗi CORS, và bạn không có chỗ để kiểm soát nội dung trả lời, lọc prompt injection, hay giới hạn số request mỗi user.
Backend đóng vai trò lớp trung gian giải quyết toàn bộ vấn đề đó: giữ API key an toàn, kiểm tra xác thực user, áp dụng rate limit, thêm ngữ cảnh từ database nội bộ trước khi gửi prompt lên model. Ngoài ra, backend còn là nơi riêng biệt bạn có thể log toàn bộ hội thoại để kiểm tra chất lượng, tinh chỉnh prompt sau này.
Kiến trúc tổng quan của backend chatbot
Một backend chatbot AI thực chiến gồm bốn thành phần chính: API proxy trung gian, vector store phục vụ RAG, hàng đợi xử lý cho request dài, và lớp giới hạn tốc độ. Với VPS một node, bạn có thể chạy tất cả bằng Docker Compose, mỗi service một container, dễ quản lý và nâng cấp.
| Thành phần | Vai trò | Công nghệ gợi ý |
|---|---|---|
| API Gateway / Proxy | Nhận request từ frontend, giữ API key, chuyển tiếp tới LLM | Node.js + Express hoặc FastAPI (Python) |
| Vector store | Lưu embedding nội dung website, tìm kiếm ngữ cảnh cho RAG | PostgreSQL + pgvector (tối ưu cho VPS 2 GB RAM) |
| Queue | Xử lý request tạo câu trả lời dài, tránh timeout | Redis + BullMQ (Node.js) hoặc Celery (Python) |
| Rate limiter | Chặn spam, giới hạn request mỗi user | Redis + middleware rate-limit |
Nếu bạn chưa có nhiều kinh nghiệm Docker, bài quản lý container với Portainer trên VPS sẽ giúp bạn theo dõi và vận hành các container này dễ dàng hơn nhiều so với dùng lệnh thuần.
Bước 1 - Cài đặt môi trường cơ bản
Bắt đầu bằng việc cập nhật hệ thống và cài Docker cùng Docker Compose plugin. Trên Ubuntu 24.04, các lệnh sau đưa bạn lên một môi trường sạch:
apt update && apt upgrade -y
apt install -y docker.io docker-compose-v2 git ufwBật Docker chạy cùng hệ thống và mở firewall trước khi cài thứ gì khác:
systemctl enable --now docker
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enableKiểm tra nhanh bằng lệnh docker --version, kết quả phải hiện bản Docker 27.x trở lên (bản mới nhất năm 2026 là 28.x). Nếu firewall bật mà bạn quên mở port SSH, bạn sẽ tự khoá mình ngoài máy chủ, nên kiểm tra kỹ trước khi gõ ufw enable.
Bước 2 - Cài PostgreSQL với pgvector cho RAG
RAG (Retrieval-Augmented Generation) là kỹ thuật đưa nội dung từ database của bạn vào prompt trước khi gửi cho LLM, giúp chatbot trả lời dựa trên dữ liệu thật của website thay vì kiến thức chung. Với VPS 2 GB RAM, PostgreSQL + pgvector là lựa chọn tối ưu hơn hẳn các vector database chuyên dụng như Qdrant hay Weaviate vì không cần thêm một service riêng.
Tạo file docker-compose.yml trong thư mục ~/chatbot-backend:
mkdir -p ~/chatbot-backend && cd ~/chatbot-backend
nano docker-compose.ymlDán nội dung sau vào file, dùng image pgvector chính thức của cộng đồng vì image PostgreSQL gốc không kèm extension này:
services:
db:
image: pgvector/pgvector:pg16
restart: unless-stopped
environment:
POSTGRES_USER: chatbot
POSTGRES_PASSWORD: matkhau_manh
POSTGRES_DB: chatbot
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "127.0.0.1:5432:5432"
volumes:
pgdata:Chỉ bind tới 127.0.0.1 vì database không cần lộ ra ngoài, chỉ backend nội bộ cần kết nối. Chạy lệnh sau để khởi động:
docker compose up -dVerify bằng cách kết nối và kích hoạt extension:
docker exec -it chatbot-backend-db-1 psql -U chatbot -d chatbot -c "CREATE EXTENSION IF NOT EXISTS vector;"Không có lỗi nghĩa là pgvector đã sẵn sàng. Lưu ý tên container có thể khác nếu bạn đặt tên thư mục khác, dùng docker ps để xem tên chính xác.
Bước 3 - Viết API proxy bằng Node.js
API proxy là trái tim của backend. Nó nhận request từ frontend, kiểm tra rate limit, tìm ngữ cảnh trong vector store, ghép prompt, gọi LLM và trả kết quả về. Dưới đây là cấu trúc thư mục:
mkdir -p ~/chatbot-backend/api/src
cd ~/chatbot-backend/api
npm init -y
npm install express cors dotenv pg @dqbd/tiktoken openai ioredis express-rate-limitFile index.js chính, thiết kế cho kiến trúc tách biệt giữa proxy và logic:
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use(cors({ origin: process.env.ALLOWED_ORIGIN }));
app.use(express.json());
// Endpoint /api/chat - nhận tin nhắn từ frontend
app.post('/api/chat', async (req, res) => {
const { message, sessionId } = req.body;
if (!message || message.length > 2000) {
return res.status(400).json({ error: 'Message invalid' });
}
// Logic xử lý: thêm vào queue, hoặc gọi trực tiếp LLM
// Chi tiết ở bước dưới
res.json({ reply: 'Xử lý xong' });
});
// Endpoint /api/health - kiểm tra sức khỏe backend
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', time: new Date().toISOString() });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`API running on port ${PORT}`));File .env lưu biến môi trường, KHÔNG commit lên Git:
PORT=3000
DATABASE_URL=postgres://chatbot:matkhau_manh@db:5432/chatbot
OPENAI_API_KEY=sk-...
ALLOWED_ORIGIN=https://yourwebsite.comPhần xử lý chính khi nhận tin nhắn: đầu tiên tìm ngữ cảnh từ vector store bằng câu hỏi của user, sau đó ghép vào system prompt, rồi gọi LLM. Nhờ đó câu trả lời của chatbot dựa trên nội dung website của bạn, không phải kiến thức chung chung. Bài tự host RAG trên VPS với vector DB và LLM đi sâu hơn vào kỹ thuật RAG nếu bạn cần.
Bước 4 - Thêm vector store và pipeline nhập dữ liệu
Để chatbot trả lời đúng nội dung website, bạn cần "dạy" nó bằng cách nhúng nội dung thành vector và lưu vào pgvector. Quá trình này gồm: crawl nội dung, cắt thành đoạn nhỏ, tạo embedding, lưu vào database. Bạn có thể chạy script Python sau mỗi lần cập nhật nội dung website.
pip install openai psycopg2-binary beautifulsoup4 requests
Script ingest.py đọc trang web, cắt nội dung thành các đoạn 500 ký tự, tạo embedding rồi lưu vào PostgreSQL:
import os, requests
from bs4 import BeautifulSoup
from openai import OpenAI
import psycopg2
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
conn = psycopg2.connect(os.environ["DATABASE_URL"])
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS documents (id SERIAL PRIMARY KEY, content TEXT, embedding vector(1536))")
url = "https://yourwebsite.com/ve-chung-toi"
html = requests.get(url).text
text = BeautifulSoup(html, "html.parser").get_text()
chunks = [text[i:i+500] for i in range(0, len(text), 500)]
for chunk in chunks:
emb = client.embeddings.create(model="text-embedding-3-small", input=chunk).data[0].embedding
cur.execute("INSERT INTO documents (content, embedding) VALUES (%s, %s)", (chunk, emb))
conn.commit()Khi user hỏi, backend tạo embedding cho câu hỏi, tìm 5 đoạn gần nhất bằng phép tính khoảng cách cosine, đưa vào prompt. Lệnh SQL tra cứu như sau:
SELECT content FROM documents ORDER BY embedding <-> $1::vector LIMIT 5;Toán tử <-> trong pgvector tính khoảng cách cosine. Kết quả trả về là các đoạn nội dung liên quan nhất, giúp LLM trả lời chính xác về sản phẩm, chính sách của website bạn.
Bước 5 - Xử lý bất đồng bộ với queue và rate limit
Gọi API LLM có thể mất 5-20 giây cho câu trả lời dài. Nếu request đồng bộ, user chờ lâu và dễ bị timeout. Giải pháp là đưa request vào hàng đợi Redis, worker xử lý nền, frontend poll hoặc nhận qua WebSocket. Kiến trúc này cũng giúp bạn kiểm soát số request đồng thời tới LLM, tránh vượt quota của provider.
Rate limit là lớp bảo vệ bắt buộc. Chatbot công khai trên website sẽ bị bot quét và spam, mỗi request tốn tiền gọi API. Dùng middleware sau cho Express:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 phút
max: 10, // tối đa 10 request mỗi IP mỗi phút
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Quá nhiều request, thử lại sau 1 phút' }
});
app.use('/api/chat', limiter);Mức 10 request/phút/IP hợp lý cho chatbot thông thường. Nếu website của bạn đông user thật, dùng express-rate-limit kết hợp Redis để chia sẻ trạng thái giữa nhiều instance, nhưng với VPS 1 node thì lưu trong memory là đủ. Bạn cũng nên thêm cơ chế xác thực đơn giản bằng token riêng cho từng phiên hội thoại, tránh kẻ xấu tự gửi request giả mạo.
Bước 6 - Cấu hình Nginx reverse proxy và HTTPS
Backend chạy ở port 3000, nhưng bạn không nên expose trực tiếp ra internet. Dùng Nginx làm reverse proxy, đồng thời cấu hình HTTPS bằng Certbot. Đây cũng là nơi bạn thêm gzip, giới hạn kích thước request body, và chặn các request đáng ngờ trước khi chúng tới backend.
apt install -y nginx certbot python3-certbot-nginx
Tạo file cấu hình /etc/nginx/sites-available/chatbot:
server {
listen 80;
server_name api.yourwebsite.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 120s;
}
client_max_body_size 5m;
}Bật site và cấp chứng chỉ SSL:
ln -s /etc/nginx/sites-available/chatbot /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
certbot --nginx -d api.yourwebsite.comNếu bạn muốn tự động gia hạn SSL, bài tự động cấp và gia hạn SSL wildcard với acme.sh hướng dẫn chi tiết hơn cho nhiều domain cùng lúc. Sau khi có HTTPS, frontend có thể gọi https://api.yourwebsite.com/api/chat an toàn.
Xử lý lỗi thường gặp
1. Backend báo lỗi ECONNREFUSED khi kết nối database. Nguyên nhân thường do container chưa khởi động hoặc sai tên service trong DATABASE_URL. Kiểm tra bằng docker compose ps, đảm bảo service db ở trạng thái Up. Trong docker-compose, tên host là tên service, không phải localhost.
2. Lỗi CORS khi gọi từ frontend. Domain gọi API phải khớp chính xác giá trị ALLOWED_ORIGIN trong file .env, bao gồm cả https://. Lỗi này xảy ra khi website dùng www nhưng bạn chỉ khai báo domain không www, hoặc ngược lại.
3. Request tới LLM bị timeout. Nginx đã set proxy_read_timeout 120s nhưng bạn cần tăng timeout ở phía Node.js bằng req.setTimeout(60000) hoặc cấu hình trong thư viện OpenAI. Nếu vẫn thường xuyên timeout, chuyển sang kiến trúc queue như đã nói ở trên, trả về ngay "đang xử lý" rồi gửi kết quả qua WebSocket.
Kiểm tra log nhanh khi có lỗi: docker compose logs -f api xem log backend, journalctl -u nginx -f xem log Nginx. Hai lệnh này giải quyết được 80% sự cố vận hành chatbot trên VPS.
Câu hỏi thường gặp
VPS cấu hình bao nhiêu là đủ để chạy backend chatbot AI?
VPS 2 GB RAM và 2 vCPU đủ cho backend chatbot phục vụ 50-100 user đồng thời, gồm PostgreSQL + pgvector, Redis, Node.js API và Nginx. Nếu bạn tự host LLM bằng Ollama thì cần tối thiểu 8 GB RAM, tuỳ kích thước model.
Có nhất thiết phải dùng vector store cho chatbot không?
Không, nếu chatbot chỉ trả lời câu hỏi chung thì gọi thẳng API LLM là đủ. Nhưng nếu bạn muốn chatbot trả lời đúng về sản phẩm, chính sách, quy trình của website thì RAG với vector store gần như bắt buộc, nếu không LLM sẽ bịa thông tin.
Chi phí vận hành backend chatbot trên VPS là bao nhiêu?
Chi phí gồm hai phần: tiền thuê VPS (từ khoảng 189.000đ/tháng) và tiền API LLM tính theo số token sử dụng. Với website nhỏ khoảng 1000 câu hỏi/tháng, chi phí API khoảng 2-5 USD, phụ thuộc model. Xem bảng giá VPS để chọn gói phù hợp.
Nên gọi API LLM trực tiếp từ frontend hay qua backend?
Luôn qua backend. Gọi trực tiếp từ frontend lộ API key trong source code, ai cũng lấy được và dùng chùa, đồng thời không có cách nào áp rate limit hay kiểm soát nội dung.
Làm sao để chatbot không bị lạm dụng gây tốn tiền?
Áp dụng rate limit theo IP và theo session, giới hạn độ dài tin nhắn đầu vào, thêm xác thực token cho phiên hội thoại, và đặt ngân sách tối đa trong tài khoản của nhà cung cấp LLM để tự động dừng khi vượt mức.
Bài viết liên quan
- Tự host API LLM với Ollama trên VPS
- Tự host RAG trên VPS với vector DB và LLM
- Xây chatbot RAG bằng n8n và vector store trên VPS riêng
- Tự dùng AI chatbot riêng với Ollama và Open WebUI trên VPS


