- Một người dùng trên Hacker News đặt câu hỏi mọi người đang triển khai Retrieval-Augmented Generation (RAG) trong môi trường cục bộ như thế nào
- Có xu hướng khá rõ rằng với RAG cục bộ, ngay cả khi không có vector DB, vẫn có thể vận hành tốt chỉ bằng các cách tìm kiếm văn bản như SQLite FTS5, BM25, grep
- Với tìm kiếm mã nguồn, có nhiều chia sẻ kinh nghiệm rằng embedding chậm và nhiều nhiễu, và cũng có nhiều ý kiến cho rằng cách dựa trên từ khóa như BM25 + trigram tốt hơn
- Ngay cả khi cần tìm kiếm vector, vẫn có nhiều trường hợp giải quyết bằng cấu hình gọn nhẹ như Postgres + pgvector, lưu vector BLOB trong SQLite, hoặc nạp FAISS vào bộ nhớ
- Có thể cải thiện chất lượng tìm kiếm bằng các tổ hợp như tìm kiếm lai kết hợp BM25 + vector, RRF (Reciprocal Rank Fusion), reranking, mở rộng multi-query
- Thay vì cố định theo kiểu “RAG = vector DB”, xu hướng là chọn theo tìm kiếm đơn giản → lai → kiểu agent tùy loại tài liệu, quy mô và gánh nặng vận hành
Kết luận chung ở bước tìm kiếm
- Thay vì mặc định vector DB hay graph là bắt buộc, nhiều người chọn cách bắt đầu đơn giản theo hạ tầng sẵn có, loại tệp và yêu cầu hiệu năng
- Cũng có ý kiến cho rằng cách để agent truy vấn trực tiếp filesystem hoặc API thì dễ thiết lập và bảo trì hơn, nhưng có thể hơi chậm
- Nhận thức rằng “thứ RAG đưa cho LLM là những mẩu văn bản ngắn từ kết quả tìm kiếm” đã khiến trọng tâm tối ưu hiệu năng chuyển sang chất lượng truy xuất
- Về “định nghĩa RAG”, có cả ý kiến cho rằng chỉ cần retrieval + generation là đã là RAG dù không có vector DB, và cũng có ý kiến rằng thông thường người ta vẫn dùng từ này với giả định có vector DB
Mô hình embedding và tìm kiếm vector
- Mô hình embedding mdbr-leaf-ir do MongoDB phát triển chạy hoàn toàn trên CPU và đứng đầu nhiều bảng xếp hạng trong nhóm mô hình cùng kích thước
- Trên máy chủ tiêu chuẩn 2vCPU có thể xử lý khoảng 22 tài liệu/giây và 120 truy vấn/giây
- Đạt 53.55 điểm trên benchmark BEIR (
all-MiniLM-L12-v2 đạt 42.69 điểm)
- Các embedding từ tĩnh như model2vec/minish có tốc độ suy luận nhanh hơn nhưng độ chính xác truy xuất thấp hơn
- Vì chỉ thực hiện tokenization + lookup table + trung bình nên nhanh hơn mô hình dựa trên transformer
- Cũng có cách tạo vector cho từng đoạn văn bản bằng Meta-Llama-3-8B rồi lưu vào cột SQLite BLOB, sau đó dùng FAISS để tìm kiếm
- Với 5 triệu chunk thì dùng khoảng 40GB bộ nhớ
faiss-gpu trên A6000 rất nhanh, còn faiss-cpu trên M1 Ultra chậm hơn nhưng vẫn đủ dùng nếu chỉ truy vấn vài lần mỗi ngày
Khuyến nghị cho tìm kiếm mã nguồn
- Với mã nguồn, nên tránh dùng cơ sở dữ liệu vector và ưu tiên kết hợp BM25 + trigram
- Embedding chậm và không phù hợp với code
- Nếu không có reranker thì nhiễu lớn, và việc re-index file cũng tốn công
- Tốc độ phản hồi tìm kiếm nhanh và chất lượng kết quả cũng tốt
- Có thể triển khai tìm kiếm BM25 trong PostgreSQL bằng plpgsql_bm25
- Hỗ trợ tìm kiếm lai kết hợp với
pgvector + Reciprocal Rank Fusion
- Áp embedding cho đường dẫn tệp + signature rồi fusion với BM25 cũng có thể cho kết quả tốt
- Cách agentic chạy gpt-oss 20B cùng với
ripgrep trong vòng lặp while cũng tỏ ra hiệu quả
Các giải pháp dựa trên cơ sở dữ liệu
- SQLite FTS5: phù hợp với tài liệu dạng file Markdown, có thể triển khai RAG ngay cả khi không có vector DB
- Mỗi file có thể có một trường mô tả ngắn để duyệt tài liệu bằng tìm kiếm từ khóa
- Cũng có thể thiết kế theo kiểu lưu vector fp16 vào SQLite dưới dạng BLOB, lọc ra tập con trước rồi tính độ tương đồng trong bộ nhớ
- Ngoài ra còn có các lựa chọn như
sqlite-vec, sqlite-vector, vec0, bm25 của SQLite
- “SQLite hoạt động tốt đến mức đáng ngạc nhiên”
- PostgreSQL + pgvector: tận dụng được kiến thức Postgres sẵn có, dễ bàn giao cho đội vận hành
- Cũng có thư viện llmemory hỗ trợ BM25 lai, mở rộng multi-query và reranking
- LanceDB: vector DB nhúng, khá tiện để sử dụng
- Được dùng cùng embedding nomic-embed-text của Ollama
- DuckDB: cung cấp extension tìm kiếm tương đồng vector, phù hợp với dự án nhỏ dưới 3GB
- Meilisearch, Typesense, Manticore: đơn giản hơn Elasticsearch/OpenSearch về mặt vận hành
Tìm kiếm lai và kiểu agentic
- nori (usenori.ai): kết hợp tìm kiếm ngữ nghĩa và tìm kiếm từ vựng bằng SQLite + vec0 + fts5
- Turbopuffer: hỗ trợ tìm kiếm lai vector + BM25
- Chỉ với tổ hợp tìm kiếm agentic và tìm kiếm văn bản cũng đã có thể đạt kết quả khá tốt
- Nếu thêm tìm kiếm vector và graph RAG thì có thể cải thiện thêm một chút về tốc độ và chất lượng
- Claude Code/Codex nội bộ sử dụng ripgrep
- Embedding áp vào đường dẫn tệp cũng có hiệu quả, và khi fusion với BM25 thì còn tốt hơn
Các trường hợp sử dụng BM25
- shebe: công cụ CLI/MCP để lập chỉ mục và tìm kiếm codebase dựa trên BM25
- Đặc biệt hữu ích trong workflow refactor (ví dụ: liệt kê các vị trí cần thay đổi khi nâng cấp Istio)
- Trong 85% trường hợp, chỉ cần khớp tag là đủ, không cần vector DB
- Người vận hành thêm tag vào cả đầu vào lẫn tài liệu để đạt mức khớp 100%
- Có ý kiến cho rằng phần lớn vector DB chỉ là “cái búa dành cho bài toán không tìm thấy thứ cần tìm”
Công cụ và thư viện chuyên biệt
- qmd: công cụ CLI để tìm kiếm file Markdown, cho kết quả truy vấn mờ tốt hơn
fzf
- ck: công cụ semantic grep viết bằng Rust
- Kiln: thêm file bằng kéo thả, có thể so sánh nhiều cấu hình khác nhau
- Hỗ trợ so sánh cách trích xuất, mô hình embedding, phương thức tìm kiếm (BM25, hybrid, vector)
- Có chức năng đánh giá độ chính xác tìm kiếm và tự động tạo bộ dữ liệu đánh giá
- libragen: CLI/MCP server để tạo thư viện nội dung RAG có quản lý phiên bản
- Có thể chuyển kho GitHub thành RAG DB
- piragi: thư viện Python RAG đơn giản, hỗ trợ nhiều nguồn như local/S3/API
- ragtune: công cụ CLI để debug và benchmark truy xuất của RAG cục bộ
Xử lý tài liệu và OCR
- discovery: dùng Qwen-3-VL-8B để OCR tài liệu, lưu vector bằng ChromaDB
- Triển khai RAG lai BM25 + embedding
- docling: công cụ trích xuất tài liệu, được dùng trong nhiều dự án RAG
- Khi chuyển đổi PDF, việc xử lý bảng, nhiều cột, hoặc bảng kéo dài qua nhiều trang vẫn khó
- Mô hình Mistral OCR cho kết quả tốt nhất (mô hình đóng)
Quản lý bộ nhớ và ngữ cảnh
- Thứ RAG truyền cho LLM chỉ là các chuỗi kết quả tìm kiếm ngắn
- Với mô hình nhỏ,
TOP_K=5 thường đã là giới hạn; nhiều hơn có thể gây quên ngữ cảnh
- Có thể cải thiện bằng cách tóm tắt trước file và thư mục
- Cũng có người dùng Sonnet + cửa sổ ngữ cảnh 1M để đưa toàn bộ nội dung vào context
- Có trường hợp xây dựng hệ thống memory cho Claude Code bằng tìm kiếm ngữ nghĩa trên file phiên làm việc
Ứng dụng doanh nghiệp và quy mô lớn
- Khi xử lý 300.000 tương tác khách hàng mỗi ngày, độ trễ và độ chính xác là rất quan trọng
- Sử dụng cách tiếp cận lai gồm embedding + full-text search + IVF-HNSW
- Một bài toán khó là quản lý việc lan truyền thông tin trong khoảng 600 hệ thống phân tán
- Đang thử nghiệm cách tiếp cận KAG (Knowledge Augmented Generation) để ánh xạ các quy tắc nghiệp vụ
- Đã triển khai thành công RAG hoàn toàn cục bộ cho hơn 500.000 bài báo bằng Postgres vector DB
Các công cụ và cách tiếp cận khác
- AnythingLLM: đi kèm vector DB đóng gói sẵn cho tài liệu
- LibreChat: cũng bao gồm vector DB đóng gói sẵn cho tài liệu
- ChromaDB: được dùng trong extension Obsidian để triển khai tìm kiếm ngữ nghĩa/hybrid
- SurrealDB: được dùng kết hợp với vector hóa cục bộ
- Giao diện truy vấn OData: khi cung cấp như một công cụ cho LLM thì khá hiệu quả, có thể phân tích file Excel 40.000 dòng
- Nextcloud MCP Server: dùng Qdrant làm vector DB, cung cấp tìm kiếm ngữ nghĩa cho tài liệu cá nhân
- LSP (Language Server Protocol): đã được thêm vào Claude Code nhưng hiện còn lỗi
- TreeSitter có thể hữu ích hơn (tra cứu theo tên symbol, tìm vị trí định nghĩa/sử dụng)
3 bình luận
Không rõ có xử lý tốt tiếng Hàn hay không.
Nhìn hiệu năng của một hệ thống RAG nội bộ còn khá thô sơ của công ty, đọc bài viết như thế này khiến góc nhìn của tôi cũng thay đổi đôi chút.
Ý kiến trên Hacker News
Nhóm chúng tôi đang vận hành một cơ sở dữ liệu hỏi đáp
Cả câu hỏi và câu trả lời đều được lập chỉ mục bằng chỉ mục trigram và embedding, rồi lưu vào Postgres
Khi tìm kiếm, chúng tôi dùng cả
pgvectorvà tìm kiếm trigram, rồi kết hợp kết quả bằng điểm mức độ liên quanỞ bước truy xuất, chúng tôi đã phát triển một mô hình embedding văn bản hiệu quả cao, thân thiện với CPU
Đó là mô hình MongoDB/mdbr-leaf-ir, hiện đứng số 1 trên leaderboard trong cùng phân khúc kích thước
Mô hình này tương thích với Snowflake/snowflake-arctic-embed-m-v1.5
Có thể so sánh tìm kiếm ngữ nghĩa vs BM25 vs hybrid qua bản demo search-sensei
Ví dụ, mô hình embedding nhận ra rằng “j lo” có nghĩa là “Jennifer Lopez”
Chúng tôi cũng đã công bố quy trình huấn luyện, và có thể huấn luyện dễ dàng ngay cả với phần cứng mức phổ thông
Từ tháng 4 năm 2024, tôi đã dùng Meta-Llama-3-8B để tạo vector
Tôi dùng Python và Transformers trên RTX-A6000; nhanh nhưng ồn và nóng khá nhiều
Sau đó tôi chuyển sang M1 Ultra và dùng thư viện MLX của Apple, tốc độ tương tự nhưng yên tĩnh hơn nhiều
Mô hình Llama có 4k chiều nên mỗi chunk chiếm 8KB theo fp16, và tôi lưu chúng bằng
numpy.save()vào cột BLOB của SQLiteKhi tìm kiếm, tôi tải toàn bộ vector từ SQLite, tạo
numpy.array, rồi tìm bằng FAISSfaiss-gputrên RTX6000 rất nhanh, cònfaiss-cputrên M1 Ultra cũng đủ nhanh cho nhu cầu của tôi (vài truy vấn mỗi ngày)Với 5 triệu chunk, mức dùng bộ nhớ vào khoảng 40GB, cả hai máy đều xử lý dư sức
Phần lớn tài liệu phức tạp của tôi là các file Markdown
Tôi khuyên dùng một công cụ CLI đơn giản là tobi/qmd
Trước đây tôi dùng quy trình dựa trên fzf, nhưng công cụ này cung cấp tìm kiếm mờ tốt hơn
Tôi không dùng nó để tìm kiếm mã nguồn
Tôi khuyên đừng dùng cơ sở dữ liệu vector cho tìm kiếm mã nguồn
Embedding chậm và không hợp với code
Kết hợp BM25 + trigram cho kết quả tốt hơn và phản hồi cũng nhanh hơn
Có thể tham khảo dự án plpgsql_bm25
Dự án có ví dụ kết hợp BM25 và pgvector bằng Reciprocal Rank Fusion cùng với notebook Jupyter
Nếu dùng mô hình không dành cho code thì tìm kiếm vector gây ra rất nhiều nhiễu
Giờ đây cách chạy vòng lặp
gpt-oss 20Bcùng ripgrep vừa nhanh hơn vừa chính xác hơn nhiềuNếu kết hợp (fusion) với BM25 thì còn tốt hơn nữa
Tôi đã tạo local-LLM-with-RAG để thử nghiệm RAG cục bộ
Nó tạo embedding bằng “nomic-embed-text” của Ollama và dùng LanceDB làm vector DB
Gần đây tôi đã cập nhật sang “agentic RAG”, nhưng với các dự án nhỏ thì có thể là hơi quá mức
Tôi lưu vector fp16 dưới dạng BLOB trong SQLite, lọc trước rồi tải vào bộ nhớ để tính độ tương đồng bằng phép nhân ma trận-vectơ (matvec)
Nếu numpy hoặc torch tận dụng đa luồng/BLAS/GPU thì sẽ rất nhanh
Nếu gặp nút thắt cổ chai, tôi dự định chuyển sang sqlite-vector
Cách này hiệu quả vì dữ liệu được thu hẹp đáng kể bằng các bộ lọc như ngày tháng hoặc vị trí
Phần backend được ẩn sau một giao diện có thể thay thế
95% tài liệu của tôi là các file Markdown nhỏ nên tôi dùng SQLite FTS5 làm chỉ mục tìm kiếm văn bản thuần
Tôi đã có sẵn chỉ mục nên kết nối thẳng vào mastra agent
Mỗi file có một trường mô tả ngắn; sau khi tìm theo từ khóa, nếu mô tả khớp thì tôi tải toàn bộ tài liệu
Mất khoảng một giờ để thiết lập, và hoạt động rất tốt
Tìm kiếm dựa trên embedding phổ biến hơn, nhưng về bản chất thì giống nhau
Chúng tôi quen dùng Postgres nên đã bắt đầu với PGVector
Sau đó chúng tôi phát hiện nội dung có khớp 100% với các trường bán cấu trúc trong prompt
Vì người vận hành bắt đầu gắn thẻ cả cho đầu vào lẫn tài liệu (khoảng 50 tài liệu)
Thế là chúng tôi tìm theo trường trước để đưa file tương ứng vào prompt, rồi mới thực hiện tìm kiếm embedding
Kết quả là trong 85% trường hợp không cần vectordb
Tôi đã tạo llmemory và đang dùng nó cả cục bộ lẫn trong ứng dụng công ty
Nó dựa trên PostgreSQL + pgvector, và bao gồm BM25 hybrid, mở rộng đa truy vấn, cùng reranking
Đây là lần đầu tôi công khai nó nên có thể vẫn còn vài lỗi nhỏ
Tôi khá hài lòng với hiệu năng của nó