Hướng dẫn Qdrant đầy đủ: từ khởi động Docker đến tìm kiếm vector có lọc, dựng nền RAG đầu tiên của bạn

Chọn tới chọn lui cơ sở dữ liệu vector, Qdrant là một trong số ít lựa chọn tự dựng đơn giản mà chức năng lọc lại đủ mạnh. Bài này đi từ docker run một mạch đến lọc payload và tinh chỉnh chỉ mục.

Hướng dẫn Qdrant đầy đủ: từ khởi động Docker đến tìm kiếm vector có lọc, dựng nền RAG đầu tiên của bạn

Người làm RAG chắc đều trải qua cùng một giai đoạn: đầu tiên dùng một list trong bộ nhớ để lưu vector, dùng numpy tính cosine similarity, chạy rất vui. Rồi lượng dữ liệu lên tới trăm nghìn bản ghi, truy vấn bắt đầu mất vài giây, bạn mới nhận ra nên tìm một cơ sở dữ liệu vector thật sự.

Qdrant là một trong những lựa chọn hay được gợi ý nhất ở giai đoạn này. Nó là cơ sở dữ liệu vector mã nguồn mở viết bằng Rust, tự dựng đơn giản, hiệu quả bộ nhớ tốt, và chức năng lọc được thiết kế khá đầy đủ trong số các sản phẩm cùng loại.

Bài hướng dẫn này sẽ đưa từ số không đến chỗ dùng được: khởi động, tạo bảng, ghi vào, tìm kiếm, lọc, cùng vài cái bẫy bạn nên biết ngay từ đầu.

Đây là gì

Việc Qdrant làm, nói một câu là: lưu trữ vector nhiều chiều, và trong vài mili-giây tìm ra vài bản ghi có vector giống nhất với vector truy vấn của bạn.

Nhưng nó không chỉ làm tính toán độ tương đồng. Mỗi vector có thể gắn kèm một payload (bạn có thể hiểu là metadata), bên trong chứa bất kỳ dữ liệu JSON nào — tiêu đề tài liệu, tác giả, ngày tháng, giá, trạng thái tồn kho. Khi tìm kiếm có thể đồng thời làm "tương đồng ngữ nghĩa" và "lọc theo điều kiện payload", đây là năng lực có giá trị nhất của nó trong bối cảnh RAG.

Vì sao lọc lại quan trọng đến thế? Vì rất nhiều thông tin không thể nhét vào trong vector. Tài liệu chính thức nói rất rõ: khi một số đặc tính của đối tượng không thể biểu diễn bằng vector nhúng (ví dụ trạng thái tồn kho hoặc khoảng giá), việc đặt thêm điều kiện trở nên cần thiết. Bạn sẽ không muốn RAG gợi ý một tài liệu cũ đã bị gỡ khỏi kệ từ ba năm trước.

Làm được gì

Trong thực tế các mục đích thường gặp nhất có mấy loại sau:

  • Tầng truy xuất của RAG: cắt tài liệu thành khối, nhúng, lưu vào Qdrant, khi truy vấn lấy về các đoạn liên quan nhất để đưa cho LLM.
  • Tìm kiếm ngữ nghĩa: tìm kiếm trong trang không còn chỉ dựa vào so khớp từ khóa, mà hiểu được "laptop rẻ" và "máy tính xách tay giá bình dân" là cùng một thứ.
  • Hệ thống gợi ý: dùng vector nhúng của sản phẩm hoặc nội dung để tìm các mục tương tự.
  • Khử trùng lặp và phát hiện tương đồng: tìm ra các dữ liệu trùng lặp hoặc giống nhau ở mức độ cao.

Cách dùng (các bước)

Bước một: khởi động bằng Docker

Cách bắt đầu nhanh nhất là Docker:

docker pull qdrant/qdrant

docker run -p 6333:6333 -p 6334:6334 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
    qdrant/qdrant

Sau khi chạy lên có ba cổng cần nhớ:

  • localhost:6333 — REST API
  • localhost:6333/dashboard — giao diện quản lý web (rất khuyến khích mở ra xem, rất hữu ích cho việc gỡ lỗi)
  • localhost:6334 — gRPC

Cái -v gắn kết (mount) rất quan trọng. Không gắn thì container vừa dừng là dữ liệu mất, đây là cái bẫy đầu tiên người mới hay gặp nhất.

Bước hai: cài và kết nối client

Lấy Python làm ví dụ:

pip install qdrant-client
from qdrant_client import QdrantClient

client = QdrantClient(url="http://localhost:6333")

Bên chính thức đồng thời cung cấp client cho JavaScript, Rust, Java, C# và Go, khái niệm cú pháp nhất quán.

Bước ba: tạo collection

Collection tương đương với table trong cơ sở dữ liệu quan hệ. Khi tạo phải quyết định hai điều: số chiều vector và độ đo khoảng cách.

from qdrant_client.models import Distance, VectorParams

client.create_collection(
    collection_name="test_collection",
    vectors_config=VectorParams(size=4, distance=Distance.DOT),
)

size phải khớp hoàn toàn với số chiều đầu ra của mô hình nhúng của bạn, điền sai là báo lỗi lúc ghi vào. Độ đo khoảng cách hỗ trợ tích trong (DOT), cosine (COSINE) và khoảng cách Euclid (EUCLID).

Gợi ý thực tế: nếu bạn dùng OpenAI hoặc đa số mô hình nhúng mã nguồn mở, chọn COSINE thường ổn định nhất. Trừ khi bạn chắc chắn đầu ra của mô hình đã được chuẩn hóa, còn không thì đừng mặc định dùng DOT.

Bước bốn: ghi vector vào (upsert)

from qdrant_client.models import PointStruct

client.upsert(
    collection_name="test_collection",
    wait=True,
    points=[
        PointStruct(
            id=1,
            vector=[0.05, 0.61, 0.76, 0.74],
            payload={"city": "Taipei", "category": "tech", "price": 1200},
        ),
        PointStruct(
            id=2,
            vector=[0.19, 0.81, 0.75, 0.11],
            payload={"city": "Taichung", "category": "food", "price": 300},
        ),
    ],
)

Lưu ý wait=True. Nó sẽ đợi ghi xong mới trả về — giai đoạn kiểm thử nhất định phải bật, không thì dòng sau bạn sẽ không tìm thấy dữ liệu rồi hoài nghi cuộc đời. Môi trường chính thức khi ghi theo lô có thể tắt để tăng thông lượng.

Bước năm: tìm kiếm và lọc

Đây là phần đáng học nhất của Qdrant. Điều kiện lọc có ba mệnh đề, có thể kết hợp và lồng nhau tùy ý:

  • must — mọi điều kiện đều phải thỏa, tương đương AND
  • should — thỏa ít nhất một, tương đương OR
  • must_not — mọi điều kiện đều không được thỏa, tương đương NOT A AND NOT B

Một ví dụ thực tế:

{
  "filter": {
    "must": [
      { "key": "city", "match": { "value": "Taipei" } }
    ],
    "must_not": [
      { "key": "category", "match": { "value": "food" } }
    ]
  }
}

Đoạn này có nghĩa là: thành phố là Taipei, và phân loại không phải food. Ngoài so khớp chính xác, nó còn hỗ trợ truy vấn theo khoảng (khoảng giá), tìm kiếm không gian địa lý, so khớp toàn văn và lọc đối tượng lồng nhau.

Kỹ thuật nâng cao

Nhất định phải tạo chỉ mục payload. Bên chính thức khuyến nghị rõ: với các trường thường dùng để lọc hãy tạo chỉ mục payload, không thì việc lọc sẽ thoái hóa thành quét toàn bảng. Đây là cái bẫy dễ đạp nhất mà cũng dễ sửa nhất về hiệu năng.

Nghĩ rõ schema của payload trước. Vector có thể tính lại, nhưng cấu trúc payload sửa lại thì rất đau. Trước khi bắt đầu hãy nghĩ sẵn tương lai bạn sẽ lọc bằng điều kiện gì — khoảng thời gian? Cấp quyền? Nguồn dữ liệu? Trong hệ thống RAG, việc lọc quyền "tài liệu này người dùng này có được xem không" gần như chắc chắn sẽ dùng đến, đừng đợi làm xong mới thêm.

Dùng dashboard để gỡ lỗi. localhost:6333/dashboard xem được trực tiếp trạng thái collection, duyệt points, chạy thử truy vấn. Nhanh hơn nhiều so với print một mạch.

Tắt wait khi ghi theo lô. Khi ghi lượng lớn, wait=True sẽ làm chậm tốc độ nghiêm trọng, đổi thành gửi theo lô rồi xác nhận lại một lần là được.

Dùng giá trị có ý nghĩa cho id. Qdrant hỗ trợ số nguyên và UUID làm id. Dùng khóa chính của dữ liệu gốc hoặc giá trị băm của nó, khi chạy lại chỉ mục mới ghi đè đúng chứ không tạo trùng lặp.

Lưu ý

Phải tính trước việc quy hoạch bộ nhớ. Cơ sở dữ liệu vector ngốn bộ nhớ, cách ước lượng thô là "số vector × số chiều × 4 byte" cộng thêm chi phí của chỉ mục và payload. Một triệu vector 1536 chiều, chỉ riêng vector thô đã khoảng 6GB. Khi lượng dữ liệu lớn phải nghiên cứu tùy chọn lượng tử hóa (quantization) và lưu trên đĩa (on-disk) của Qdrant.

Điền sai số chiều thì không cứu lại được. Số chiều vector của collection một khi đã tạo là không đổi được, chỉ có thể dựng lại. Khi đổi mô hình nhúng đặc biệt dễ đạp phải.

Đừng coi Qdrant là cơ sở dữ liệu chính. Nó là tầng truy xuất, tài liệu gốc và dữ liệu nghiệp vụ nên lưu ở cơ sở dữ liệu vốn có của bạn, payload của Qdrant chỉ chứa các trường cần cho truy xuất và lọc. Trộn chung sẽ rất khó bảo trì về sau.

Đừng xem nhẹ chi phí vận hành khi tự dựng. Docker chạy lên rất đơn giản, nhưng sao lưu, giám sát, mở rộng, nâng cấp phiên bản đều cần người trông. Đội ngũ không có nhân lực vận hành thì Qdrant Cloud hoặc các giải pháp lưu trữ được quản lý khác có thể đáng tiền hơn.

Bình luận của TheAI Academy

Kinh nghiệm của bản thân tôi là, việc chọn cơ sở dữ liệu vector thực ra không then chốt như tưởng tượng — Qdrant, Weaviate, Milvus mấy hãng này khác biệt hạn chế ở tìm kiếm tương đồng cơ bản, thứ thật sự quyết định chất lượng RAG là chiến lược cắt khối, mô hình nhúng và sắp xếp lại (rerank).

Nhưng nếu phải chọn một lý do để dùng Qdrant, tôi sẽ nói đó là chức năng lọc. Thiết kế must / should / must_not của nó gọn gàng, lồng nhau được, và hiệu năng được thiết kế nghiêm túc (với điều kiện bạn có tạo chỉ mục). Trong bối cảnh RAG doanh nghiệp thật sự, lọc quyền và lọc theo thời hiệu là nhu cầu tất yếu, khi đó năng lực lọc từ "điểm cộng" chuyển thành "bắt buộc".

Một lợi ích thực tế khác là ngưỡng tự dựng thấp. Một dòng docker run là chạy lên được, không cần Kubernetes, không cần một đống dịch vụ phụ thuộc. Với những đội muốn kiểm chứng ý tưởng trên máy trước, hệ số ma sát này rất quan trọng.

Bình luận: Qdrant không phải nhanh nhất, cũng không phải nhiều tính năng nhất, nhưng nó là một trong những cơ sở dữ liệu vector có đường đi từ "ý tưởng đến chạy lên" ngắn nhất. Với đa số đội ngũ Việt Nam, đặc tính này có giá trị hơn là điểm benchmark đẹp.

Gợi ý cho lập trình viên Việt Nam: nếu làm RAG lần đầu, hãy dùng Docker chạy thông cả quy trình trên máy trước, khống chế lượng dữ liệu ở mức vài nghìn bản ghi, đặt sự chú ý vào chất lượng cắt khối và truy xuất, đừng vội tinh chỉnh hiệu năng. Đợi chất lượng đứng vững rồi mới bàn đến quy mô. Nếu bạn cần GPU để chạy mô hình nhúng hoặc LLM cục bộ, có thể tham khảo hướng dẫn RunPod của chúng tôi. Muốn xem thêm công cụ liên quan đến phát triển, mục Framework phát triển và hạ tầng AI trên trang có tổng hợp.

Nguồn tham khảo

(Bài viết được tổng hợp theo tài liệu chính thức, cú pháp API lấy theo tài liệu mới nhất của bên chính thức.)

Câu hỏi thường gặp

Qdrant với Pinecone, Weaviate nên chọn thế nào?

Năng lực tìm kiếm tương đồng cơ bản khác biệt hạn chế. Ưu thế của Qdrant là ngưỡng tự dựng thấp (một dòng docker run) và chức năng lọc thiết kế đầy đủ; Pinecone là quản lý trọn gói, miễn vận hành nhưng chi phí cao hơn; Weaviate tích hợp sẵn nhiều tính năng mô-đun hơn. Nếu đội ngũ muốn kiểm chứng nhanh trên máy trước thì Qdrant ít ma sát nhất.

Vì sao truy vấn lọc của tôi rất chậm?

Nguyên nhân thường gặp nhất là chưa tạo chỉ mục payload cho trường lọc. Bên chính thức khuyến nghị rõ hãy tạo chỉ mục cho các trường thường dùng để lọc, không thì việc lọc sẽ thoái hóa thành quét toàn bảng. Đây là một trong những vấn đề hiệu năng dễ sửa nhất.

Số chiều vector của collection có sửa được không?

Không. Số chiều được cố định khi tạo collection, muốn sửa chỉ có thể dựng lại collection và ghi lại toàn bộ dữ liệu. Vì vậy trước khi đổi mô hình nhúng nhất định phải xác nhận số chiều đầu ra của mô hình mới, đây là cái bẫy hay đạp phải nhất khi đổi mô hình.

Qdrant chạy bằng Docker khởi động lại có mất dữ liệu không?

Nếu không gắn volume thì sẽ mất. Nhất định phải thêm tham số -v khi docker run, gắn thư mục qdrant_storage vào /qdrant/storage bên trong container, đây là lỗi đầu tiên người mới hay gặp nhất.

繁體中文版 →