Ứng dụng RAG chạy hoàn toàn trên máy: nhập tài liệu, lập chỉ mục và trả lời như chat dựa trên kho dữ liệu nội bộ. Web server/API dùng Node.js; Ollama chạy mô hình local; SQLite lưu metadata và FAISS tìm vector.
Mặc định hiện tại dùng Qwen3 8B và bật toàn bộ pipeline chất lượng cao: phân loại AI, tách sub-query, hybrid search, LLM rerank và tool-call tra cứu sâu.
- Nhập nhiều tài liệu TXT, MD, CSV, JSON, DOCX và PDF có lớp text.
- Tự lưu file, tách nội dung thành các đoạn nhỏ (chunks), tạo embedding và lưu FAISS index.
- Hiểu cấu trúc Markdown, heading,
Điều,Khoản,Mục,Chương,Phụ lụcvà cặp FAQ hỏi/đáp. - Trả lời theo các đoạn tài liệu được tìm thấy và đưa trích dẫn
[N]. - Stream câu trả lời theo thời gian thực qua SSE.
- Hiển thị tiến trình RAG và có Nhật ký AI để kiểm tra prompt, context, rerank, tool-call và output đang stream.
Toàn bộ dữ liệu, vector index và model chạy local. Không có tài liệu nào được gửi tới Cloudflare hay API AI bên ngoài.
Yêu cầu: Node.js, Python 3 và Ollama.
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements-local.txt
npm install
# Mô hình trả lời/chạy agent
ollama pull qwen3:8b
# Mô hình tạo embedding cho FAISS
ollama pull qwen3-embedding:0.6bChạy web:
npm startTài liệu upload
└─ Đọc text / phân loại / chunk theo cấu trúc
└─ qwen3-embedding:0.6b tạo vector
├─ SQLite: tài liệu, chunks, FTS5 keyword index
└─ FAISS: semantic vector index
Câu hỏi khách hàng
└─ Qwen3 8B tách sub-query
└─ Embedding + FAISS + FTS5 hybrid search
└─ Qwen3 8B rerank các đoạn phù hợp
└─ Qwen3 8B quyết định tool-call tra cứu sâu (nếu cần)
└─ Qwen3 8B stream câu trả lời + trích dẫn
| Thành phần | Vai trò | Tốc độ tương đối |
|---|---|---|
qwen3-embedding:0.6b |
Biến text thành vector để tìm nghĩa tương đồng | Nhanh, nhẹ |
| FAISS | Tìm các chunks gần câu hỏi nhất trong kho lớn | Rất nhanh |
| SQLite FTS5 | Tìm theo từ khóa chính xác, ví dụ mã hợp đồng hoặc tên sản phẩm | Rất nhanh |
| Qwen3 8B | Phân loại AI, tách câu hỏi, rerank, gọi tool, sinh câu trả lời | Chậm nhất |
| SSE | Đẩy trạng thái và text trả lời dần về giao diện | Realtime |
- File được đưa vào hàng đợi để tránh nhiều file cùng tranh tài nguyên máy.
- Server đọc text từ file. PDF scan không có lớp text cần OCR trước khi upload.
- AI/rule phân loại thành
faq,contract,guidehoặcgeneral. - Text được chia theo heading hoặc Điều/Khoản. Chunk dài được cắt có overlap để tránh mất ngữ cảnh tại ranh giới.
- Từng chunk được embedding theo batch, lưu vào SQLite rồi thêm vào FAISS.
- Khi hoàn tất, tài liệu có thể được truy vấn ngay.
Nhiều file được xử lý lần lượt, không phải song song. Điều này giữ máy ít bị nghẽn RAM/CPU, nhưng tổng thời gian tăng theo số chunks.
Thời gian index gần đúng tỉ lệ với số chunk:
thời gian import ≈ đọc file + phân loại + (số chunks / EMBED_BATCH_SIZE) × thời gian embedding + ghi FAISS
Ví dụ tài liệu dài tạo 500 chunks sẽ cần embedding 500 đoạn. Đây là công việc chạy một lần khi nhập; sau đó chat không phải đọc lại toàn bộ file.
Các thông số mặc định giảm số lần gọi embedding bằng CHUNK_SIZE=1800 và EMBED_BATCH_SIZE=48. Tăng chunk quá lớn có thể làm retrieval kém chính xác hơn; tăng batch quá cao có thể làm máy thiếu RAM.
Khi bật toàn bộ chức năng, một câu hỏi có thể đi qua các bước sau:
- Tách sub-query: AI tách câu hỏi có nhiều ý thành tối đa 4 truy vấn độc lập.
- Hybrid retrieval: mỗi truy vấn được embedding; FAISS tìm theo ngữ nghĩa và FTS5 tìm theo từ khóa.
- Rerank: Qwen3 8B đọc các ứng viên và xếp lại đoạn nào trả lời trực tiếp nhất.
- Tool-call: AI quyết định có cần tra cứu sâu thêm một tài liệu cụ thể không.
- Generation: Qwen3 8B chỉ nhận 5 chunks đã chọn làm context và stream câu trả lời bằng Markdown, kèm
[N]tương ứng nguồn.
Nút Nhật ký AI ở sidebar cho biết chính xác prompt/context nào được gửi vào các bước trên và text model đang trả về. Nhật ký không hiển thị suy luận ẩn của model.
FAISS và SQLite rất nhanh. Điểm tốn thời gian là Qwen3 8B phải đọc prompt và sinh text.
Ở full mode, một câu hỏi thường gọi Qwen3 8B 3–4 lượt liên tiếp:
tách sub-query → rerank → quyết định tool → trả lời cuối
Rerank còn đưa nhiều chunks vào prompt, nên model phải đọc lượng context lớn. Vì vậy full mode phù hợp kiểm thử chất lượng, câu hỏi hợp đồng phức tạp hoặc màn hình quản trị; không phù hợp để trả lời mọi FAQ của khách trong vài giây.
File .env đang được đặt như sau:
PORT=3000
OLLAMA_BASE_URL=http://127.0.0.1:11434
CHAT_MODEL=qwen3:8b
EMBEDDING_MODEL=qwen3-embedding:0.6b
CLASSIFY_WITH_AI=true
SUBQUERY_ENABLED=true
RERANK_ENABLED=true
TOOL_CALL_ENABLED=true
EMBED_BATCH_SIZE=48
CHUNK_SIZE=1800
CHUNK_OVERLAP=140
RETRIEVAL_CANDIDATES=16
CONTEXT_CHUNKS=5Sau khi sửa .env, cần khởi động lại npm start.
Khách hàng không nên chờ full pipeline. Mục tiêu nên là trả lời trong khoảng 1–3 giây cho câu FAQ thông thường.
Để chuyển sang fast mode, đặt:
CLASSIFY_WITH_AI=false
SUBQUERY_ENABLED=false
RERANK_ENABLED=false
TOOL_CALL_ENABLED=false
CONTEXT_CHUNKS=5Fast mode vẫn giữ:
- embedding model;
- FAISS semantic search;
- FTS5 keyword search;
- trích dẫn nguồn;
- một lượt Qwen3 8B để viết câu trả lời cuối.
Vì vậy độ chính xác tìm tài liệu vẫn tốt cho FAQ, còn thời gian phản hồi giảm mạnh vì không gọi 8B nhiều lần.
Chiến lược thực tế cho kho khoảng 10.000 FAQ:
- Câu FAQ phổ biến: cache câu trả lời hoặc trả trực tiếp từ FAQ chunk tốt nhất.
- Câu hỏi bình thường: fast mode.
- Hợp đồng/dữ liệu nhiều điều khoản: bật rerank hoặc tool-call theo điều kiện, không bật cho mọi câu hỏi.
- Nhập dữ liệu theo lô ngoài giờ; không index tài liệu lúc khách đang chờ trả lời.
Prompt hiện yêu cầu model chỉ trả lời theo CONTEXT; nếu không đủ thông tin phải nói chưa tìm thấy trong tài liệu. Tuy nhiên RAG local với model 8B không thể cam kết tuyệt đối không bịa.
Với dữ liệu pháp lý hoặc chính sách quan trọng, nên áp dụng thêm các luật ở tầng ứng dụng:
- Không có nguồn thì không trả lời nội dung cụ thể.
- Mỗi ý quan trọng phải có citation
[N]. - Kiểm tra citation có thật sự thuộc nguồn được chọn trước khi gửi khách.
- Khi cần, trả nguyên văn Điều/Khoản liên quan kèm diễn giải ngắn.
- Cho phép khách chuyển sang nhân viên khi confidence thấp.
data/
files/ File gốc đã upload
rag.sqlite Metadata, chunks, embedding và FTS5
faiss.index Chỉ mục vector FAISS
data/ và .venv/ đã được bỏ qua khỏi Git. Sao lưu thư mục data/ để giữ lại kho tài liệu đã index.
- PDF scan cần OCR trước khi nhập.
- Hàng đợi index chạy tuần tự.
- Full pipeline dùng cùng Qwen3 8B cho nhiều tác vụ nên chậm trên laptop.
- Nhật ký AI có thể chứa các đoạn tài liệu được đưa vào context; chỉ nên dùng ở môi trường nội bộ.
Hướng nâng cấp phù hợp nhất là tách model: embedding nhỏ, cross-encoder reranker nhỏ chuyên dụng và Qwen3 8B chỉ cho câu trả lời cuối.
Dự án phát hành theo giấy phép MIT. Mọi người được tự do sử dụng, sao chép, sửa đổi, phân phối và dùng thương mại, với điều kiện giữ lại thông báo bản quyền và giấy phép.