Chuyển đến nội dung chính

Cách triển khai Semantic Search trong MongoDB

Tìm hiểu cách triển khai semantic search trong MongoDB bằng Python. Tạo vector embedding, tạo chỉ mục Vector Search và chạy truy vấn $vectorSearch.
Đã cập nhật 31 thg 7, 2026  · 9 phút đọc

Khám phá với AI

Mở trong ChatGPTMở trong ClaudeMở trong Perplexity

Đây là những tình huống mà tìm kiếm văn bản thông thường tỏ ra hạn chế. Thực tế sẽ trông như sau:

  • Cổng hỗ trợ khách hàng không trả về kết quả khi người dùng gõ "won't turn on" thay vì chính xác cụm từ được dùng trong bài viết trợ giúp.
  • Nền tảng thương mại điện tử không tìm thấy gì khi người mua tìm "something warm for winter" vì không mô tả sản phẩm nào chứa đúng các từ đó.
  • Kho tri thức bỏ sót tài liệu phù hợp vì người dùng diễn đạt câu hỏi khác với cách tài liệu được viết.

Semantic search giải quyết vấn đề này. Nó hiểu ý nghĩa và mục đích đằng sau truy vấn thay vì khớp từ chính xác.

Hướng dẫn này chỉ cho bạn cách triển khai semantic search trong MongoDB bằng Python và mô hình embedding miễn phí nomic-embed-text-v1.

Lưu ý: nomic-embed-text-v1 khoảng 0,27 GB khi nạp vào bộ nhớ, vì vậy hãy đảm bảo máy của bạn có ít nhất 1 GB RAM trống trước khi chạy bất kỳ script nào trong hướng dẫn này. Lần chạy đầu tiên sẽ tải mô hình từ Hugging Face, nên cần thêm thời gian tùy theo kết nối internet của bạn. Các lần chạy sau sẽ nạp mô hình từ bộ nhớ đệm cục bộ. Suy luận trên CPU chậm hơn đáng kể so với GPU. Trên máy không có GPU chuyên dụng, mô hình chạy trên CPU và dùng RAM thay vì VRAM, nên việc tạo embedding có thể mất nhiều thời gian hơn.

Bạn sẽ học được các nội dung sau trong hướng dẫn này:

  • Vector embedding là gì và chúng biểu diễn ý nghĩa như thế nào
  • Cách tạo và lưu trữ embedding trong MongoDB
  • Cách tạo chỉ mục MongoDB Vector Search
  • Cách chuyển đổi truy vấn của người dùng thành vector
  • Cách chạy pipeline tổng hợp $vectorSearch và diễn giải kết quả

Bạn có thể tìm toàn bộ mẫu mã cho hướng dẫn này trong kho GitHub.

Tìm kiếm văn bản so với Semantic Search

Tìm kiếm văn bản khớp các tài liệu chứa đúng những từ trong truy vấn của bạn. Semantic search khớp các tài liệu mang cùng ý nghĩa với truy vấn của bạn, ngay cả khi chúng dùng từ ngữ hoàn toàn khác.

Bảng sau cho thấy mỗi phương pháp trả về gì cho truy vấn "heart problems":

Văn bản tài liệu Tìm kiếm văn bản Semantic search Lý do tìm kiếm văn bản trả về
"...heart problems in adults..." Chứa chính xác cụm từ "heart problems"
"...cardiac conditions and symptoms..." Không Không chứa chính xác cụm từ "heart problems"
"...risk factors for heart disease..." Không Không chứa chính xác cụm từ "heart problems"
"...chest pain and shortness of breath..." Không Không chứa chính xác cụm từ "heart problems"

Các khái niệm về Semantic Search

Ba khái niệm là trung tâm của semantic search trong MongoDB. Chúng sẽ giúp bạn hiểu vì sao mỗi bước trong hướng dẫn này hoạt động như vậy.

Vector embedding

Một mô hình embedding chuyển văn bản thành một danh sách số có độ dài cố định gọi là vector. Mỗi số trong vector biểu diễn một chiều ý nghĩa.

Hai đoạn văn bản có ý nghĩa tương tự sẽ tạo ra các vector gần nhau về mặt số học. "cardiac conditions" và "heart problems" nằm gần nhau trong không gian vector, dù chúng không chia sẻ từ nào. Sự gần nhau đó là điều làm cho semantic search khả thi.

Hướng dẫn này sử dụng mô hình embedding nomic-embed-text-v1, miễn phí, mã nguồn mở và chạy hoàn toàn trên máy cục bộ của bạn. Mô hình sẽ tự động tải từ Hugging Face lần đầu bạn chạy script và lưu cục bộ cho các lần chạy sau. Mô hình tạo ra 768 số cho mỗi đầu vào.

Chỉ mục tìm kiếm vector

Một chỉ mục tìm kiếm vector cho MongoDB biết trường nào chứa embedding, số chiều mong đợi là bao nhiêu và hàm đo tương đồng nào sẽ dùng để so sánh. Bạn phải tạo chỉ mục này trước khi có thể chạy bất kỳ truy vấn $vectorSearch nào.

Giai đoạn tổng hợp $vectorSearch

$vectorSearch là giai đoạn tổng hợp thực thi việc tìm kiếm. Nó nhận một vector truy vấn, tìm trong trường đã lập chỉ mục và trả về các tài liệu được xếp hạng theo mức độ tương đồng ngữ nghĩa. Bạn có thể chuỗi nó với $project và các giai đoạn khác như mọi pipeline tổng hợp khác.

Giờ hãy bắt đầu.

Điều kiện tiên quyết

Trước khi bắt đầu, hãy đảm bảo bạn đã có:

  • Cài đặt Python 3.8 trở lên
  • Một tài khoản MongoDB Atlas với cụm M0 (Gói miễn phí) đã được thiết lập
  • pymongo 4.7 trở lên, các gói Python sentence-transformerseinops đã cài đặt
  • Hiểu biết cơ bản về Python và các collection trong MongoDB
  • Chuỗi kết nối Atlas của bạn, có trong giao diện Atlas tại **Database > Connect > Drivers**
  • Địa chỉ IP của bạn đã được cho phép trong Atlas trước khi chạy bất kỳ script nào. Vào **Security > Network Access** trong giao diện Atlas và thêm địa chỉ IP hiện tại của bạn.

Thiết lập dự án của bạn

Tạo một thư mục cho dự án và điều hướng vào đó trong terminal của bạn:

mkdir mongodb-semantic-search
cd mongodb-semantic-search

Tạo và kích hoạt môi trường ảo để cô lập các phụ thuộc của dự án:

python -m venv venv
source venv/bin/activate

Trên Windows, kích hoạt môi trường ảo bằng:

venv\Scripts\activate

Bây giờ cài đặt các gói cần thiết:

pip install pymongo sentence-transformers einops

Bạn sẽ tạo một tệp Python cho mỗi bước trong hướng dẫn này. Tất cả tệp nằm trong thư mục mongodb-semantic-search.

Tạo tệp tiện ích Embedding

Tạo tệp tên embedding_utils.py. Tệp này chứa hai hàm embedding được dùng trong hướng dẫn:

from sentence_transformers import SentenceTransformer

# Load the free, open-source embedding model.
# The model downloads from Hugging Face on first run and saves locally.
# trust_remote_code=True is required by this model:
model = SentenceTransformer("nomic-ai/nomic-embed-text-v1", trust_remote_code=True)


def get_embedding(text, precision="float32"):
    # Use this function when embedding text you plan to store in MongoDB:
    return model.encode(text, precision=precision).tolist()


def get_query_embedding(text, precision="float32"):
    # Use this function when embedding a user's search query:
    return model.encode(text, precision=precision).tolist()

Cảnh báo: trust_remote_code=True cho phép mô hình thực thi mã Python đặc thù của mô hình được tải từ Hugging Face xuống máy của bạn. Nếu mã nguồn của mô hình trên Hugging Face bị xâm nhập hoặc cập nhật với thay đổi độc hại, mã đó sẽ tự động chạy trong môi trường của bạn. Hãy thận trọng trước khi dùng tham số này trong triển khai sản xuất.

Tạo và lưu trữ Vector Embedding

Tạo tệp tên generate_embeddings.py. Tệp này nhập hàm embedding từ embedding_utils.py, tạo một vector cho trường văn bản của mỗi tài liệu và lưu toàn bộ tài liệu, bao gồm embedding, vào MongoDB:

from embedding_utils import get_embedding
from pymongo import MongoClient

# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
    "mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
    appname="devrel-tutorial-python-semantic-search"
)

collection = mongodb_client["sample_db"]["documents"]

# Sample data:
sample_documents = [
    {
        "title": "MongoDB Atlas",
        "text": "MongoDB Atlas is a fully managed cloud database."
    },
    {
        "title": "Vector Search",
        "text": "Vector search finds results based on semantic meaning."
    },
    {
        "title": "Nomic AI",
        "text": "nomic-embed-text-v1 is a free, open-source embedding model."
    },
]

docs_to_insert = []

for doc in sample_documents:
    embedding = get_embedding(doc["text"])
    docs_to_insert.append({
        "title": doc["title"],
        "text": doc["text"],
        # The vector lives alongside your original data in the same document:
        "embedding": embedding
    })

# Drop the collection before each run to avoid inserting duplicate documents:
collection.drop()

result = collection.insert_many(docs_to_insert)
print(f"Inserted {len(result.inserted_ids)} documents with embeddings.")

mongodb_client.close()

Trong đoạn mã trên, collection.drop() xóa tất cả tài liệu và chỉ mục trên collection trước mỗi lần chạy. Chạy lại generate_embeddings.py mà không có dòng này sẽ chèn tài liệu trùng lặp, khiến $vectorSearch trả về cùng một tài liệu nhiều lần. Việc drop collection cũng xóa chỉ mục vector search, vì vậy bạn phải chạy lại create_vector_index.py mỗi khi chạy lại generate_embeddings.py.

Chạy script:

python generate_embeddings.py

Bạn sẽ nhận được đầu ra sau trong terminal:

<All keys matched successfully>
Inserted 3 documents with embeddings.

Mỗi tài liệu trong MongoDB hiện chứa cả văn bản gốc và vector 768 chiều của nó trong trường embedding. Vector nằm cạnh dữ liệu của bạn trong cùng một tài liệu, nên không cần lưu trữ hay tra cứu riêng khi truy vấn.

Tạo chỉ mục MongoDB Vector Search

Tạo tệp tên create_vector_index.py. Tệp này định nghĩa và tạo một chỉ mục vector search trên trường embedding để MongoDB có thể chạy truy vấn $vectorSearch trên collection của bạn.

Định nghĩa chỉ mục yêu cầu ba trường:

  • path: trường chứa embedding (embedding trong hướng dẫn này)
  • numDimensions: phải khớp với kích thước đầu ra của mô hình (768 với nomic-embed-text-v1)
  • similarity: hàm so sánh (cosine là đúng với mô hình này)

Giá trị numDimensions phải khớp chính xác với mô hình embedding của bạn. Không khớp sẽ khiến việc tạo chỉ mục thất bại:

from pymongo.mongo_client import MongoClient
from pymongo.operations import SearchIndexModel
import time

# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
    "mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
    appname="devrel-tutorial-python-semantic-search"
)

# Point to the same database and collection you used in the previous step:
database = mongodb_client["sample_db"]
collection = database["documents"]

# Define the vector search index.
# The three required fields tell MongoDB what to index and how to compare vectors:
search_index_model = SearchIndexModel(
    definition={
        "fields": [
            {
                "type": "vector",
                "path": "embedding",      # The field name from generate_embeddings.py
                "numDimensions": 768,     # nomic-embed-text-v1 always outputs 768 dimensions
                "similarity": "cosine"    # Recommended similarity function for this model
            }
        ]
    },
    name="vector_index",
    type="vectorSearch"
)

result = collection.create_search_index(model=search_index_model)
print("New search index named " + result + " is building.")

# Poll every five seconds until the index is ready to accept queries:
print("Polling to check if the index is ready. This may take up to a minute.")

predicate = lambda index: index.get("queryable") is True

while True:
    indices = list(collection.list_search_indexes(result))
    if len(indices) and predicate(indices[0]):
        break
    time.sleep(5)

print(result + " is ready for querying.")

mongodb_client.close()

Chạy script:

python create_vector_index.py

Bạn sẽ nhận được đầu ra sau trong terminal:

New search index named vector_index is building.
Polling to check if the index is ready. This may take up to a minute.
vector_index is ready for querying.

Chỉ mục mất tối đa khoảng một phút để xây dựng. Vòng lặp polling kiểm tra mỗi 5 giây và chỉ thoát khi MongoDB xác nhận chỉ mục có thể truy vấn. Đừng chuyển sang bước tiếp theo cho đến khi bạn thấy "vector_index is ready for querying."

Chuyển đổi truy vấn tìm kiếm của bạn thành vector

Tạo tệp tên generate_query_vector.py. Tệp này nhập hàm get_query_embedding() từ tệp embedding_utils.py. Chạy tệp generate_query_vector.py để xác minh mô hình embedding được nạp đúng và tạo ra một vector 768 chiều:

  • Nó định nghĩa hàm get_query_embedding() được bước tiếp theo nhập vào.
  • Bạn có thể chạy riêng nó để xác minh mô hình hoạt động đúng.

Bạn phải dùng cùng một mô hình ở đây như ở bước đầu tiên. Mô hình khác sẽ tạo vector nằm trong không gian số khác, khiến việc so sánh trở nên vô nghĩa:

from embedding_utils import get_query_embedding

user_query = "I need an automated, scalable system for serious information storage"
query_vector = get_query_embedding(user_query)
print(f"Query: '{user_query}'")
print(f"Vector dimensions: {len(query_vector)}")
print(f"First 5 values: {query_vector[:5]}")

Chạy script:

python generate_query_vector.py

Bạn sẽ nhận được đầu ra tương tự như sau:

<All keys matched successfully>
Query: 'I need an automated, scalable system for serious information storage'
Vector dimensions: 768
First 5 values: [0.0025914530269801617, 0.09862980246543884, -0.023092379793524742, -0.0171672236174345, -0.05548065900802612]

Trong đầu ra trên, Vector dimensions: 768 xác nhận rằng đầu ra của mô hình khớp với embedding đã lưu của bạn.

Chạy truy vấn $vectorSearch

Tạo tệp tên run_vector_search.py. Tệp này nhập hàm get_query_embedding() từ tệp embedding_utils.py và chuyển đổi cụm tìm kiếm của người dùng thành vector. run_vector_search.py chạy một pipeline tổng hợp $vectorSearch và in ra các kết quả được xếp hạng theo mức độ tương đồng ngữ nghĩa:

from embedding_utils import get_query_embedding
from pymongo import MongoClient

# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
    "mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
    appname="devrel-tutorial-python-semantic-search"
)

collection = mongodb_client["sample_db"]["documents"]

# Generate a query vector from the user's search input:
user_query = "I need an automated, scalable system for serious information storage"
query_vector = get_query_embedding(user_query)

# Define the $vectorSearch aggregation pipeline:
pipeline = [
    {
        "$vectorSearch": {
            "index": "vector_index",      # The index created in create_vector_index.py
            "path": "embedding",          # The field that holds your stored vectors
            "queryVector": query_vector,  # The vector generated from the user's query
            "numCandidates": 150,         # How many neighbors MongoDB considers
            "limit": 3                    # How many results to return
        }
    },
    {
        "$project": {
            "_id": 0,
            "title": 1,
            "text": 1,
            "score": {
                "$meta": "vectorSearchScore"  # Relevance score for each result
            }
        }
    }
]

results = collection.aggregate(pipeline)

print(f"\nTop results for query: '{user_query}'\n")

for doc in results:
    print(f"Title: {doc['title']}")
    print(f"Text:  {doc['text']}")
    print(f"Score: {doc['score']:.4f}")
    print()

mongodb_client.close()

Trong đoạn mã trên, hai tham số điều khiển hành vi tìm kiếm:

  • numCandidates đặt kích thước tập ứng viên ban đầu mà MongoDB xem xét trước khi thu hẹp xuống kết quả cuối cùng. Giá trị lớn hơn cải thiện khả năng bao phủ nhưng mất nhiều thời gian hơn một chút.
  • limit đặt số lượng kết quả bạn nhận được. Trong hướng dẫn này, limit đặt là 3 và numCandidates đặt là 150. Điểm khởi đầu phổ biến là đặt numCandidates gấp 10–15 lần giới hạn của bạn.

Bây giờ chạy script:

python run_vector_search.py

Bạn sẽ nhận được đầu ra tương tự như sau:

<All keys matched successfully>

Top results for query: 'I need an automated, scalable system for serious information storage.'

Title: MongoDB Atlas
Text:  MongoDB Atlas is a fully managed cloud database.
Score: 0.7211

Title: Nomic AI
Text:  nomic-embed-text-v1 is a free, open-source embedding model.
Score: 0.6818

Title: Vector Search
Text:  Vector search finds results based on semantic meaning.
Score: 0.6642

"MongoDB Atlas" đạt điểm cao nhất dù truy vấn "I need an automated, scalable system for serious information storage" không chia sẻ từ nào với câu "MongoDB Atlas is a fully managed cloud database." Kết quả được trả về vì các vector của chúng gần nhau về ý nghĩa: "...một hệ thống tự động, có khả năng mở rộng để lưu trữ thông tin" ánh xạ ngữ nghĩa tới "...một cơ sở dữ liệu đám mây được quản lý hoàn toàn". Một tìm kiếm văn bản với cùng truy vấn sẽ trả về 0 kết quả, vì không có từ nào trùng khớp chính xác trong bất kỳ tài liệu nào. Đó là semantic search hoạt động như mong đợi.

Điểm chính cần ghi nhớ

  • MongoDB Vector Search chạy trên cụm M0 Gói miễn phí. Không cần gói trả phí.
  • Bạn phải dùng cùng một mô hình embedding cho cả việc tạo embedding lưu trữ và tạo vector truy vấn. Trộn lẫn mô hình sẽ cho kết quả vô nghĩa.
  • Giá trị numDimensions trong định nghĩa chỉ mục phải khớp chính xác với kích thước đầu ra của mô hình embedding. nomic-embed-text-v1 luôn tạo ra 768 chiều.
  • input_type="document" tối ưu embedding cho lưu trữ. input_type="query" tối ưu cho truy xuất. Dùng đúng loại ở mỗi giai đoạn.
  • numCandidates kiểm soát độ rộng phạm vi tìm kiếm mà MongoDB quét trước khi thu hẹp xuống kết quả cuối cùng theo limit. Giá trị lớn hơn cải thiện khả năng bao phủ với chi phí thời gian truy vấn.
  • vectorSearchScore xếp hạng kết quả theo mức độ tương đồng ngữ nghĩa. Kết quả không cần chứa bất kỳ từ nào trùng với truy vấn. Phạm vi điểm số thay đổi theo mô hình và dữ liệu, nhưng các ngưỡng sau là điểm khởi đầu hữu ích cho nomic-embed-text-v1 với cosine similarity:
    • 0,9 trở lên: Ý nghĩa gần như giống hệt. Tài liệu và truy vấn gần như tương đương về ngữ nghĩa.
    • 0,7 đến 0,9: Mức liên quan mạnh. Tài liệu rõ ràng liên quan đến mục đích truy vấn.
    • 0,5 đến 0,7: Mức liên quan vừa. Tài liệu liên quan theo chủ đề nhưng dùng khung/ ngữ cảnh khác.
    • Thấp hơn 0,5: Mức liên quan yếu. Mối liên hệ lỏng lẻo và kết quả có thể không hữu ích.

Bạn có thể tìm toàn bộ mẫu mã cho hướng dẫn này trong kho GitHub.

Tài liệu nên đọc thêm

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

Tôi có cần gói Atlas trả phí để triển khai semantic search không?

Không. Cả bốn bước trong hướng dẫn này đều chạy trên cụm M0 thuộc Gói miễn phí, không mất phí.

Điều gì xảy ra nếu tôi dùng mô hình embedding khác cho truy vấn so với tài liệu đã lưu?

Kết quả của bạn sẽ vô nghĩa. Các vector sẽ không thể so sánh vì các mô hình khác nhau ánh xạ văn bản vào những không gian số khác nhau. Luôn dùng cùng một mô hình cho cả lập chỉ mục và truy vấn.

Sự khác nhau giữa `numCandidates` và `limit` là gì?

numCandidates là số lượng vector MongoDB xem xét trong quá trình tìm kiếm. limit là số lượng kết quả hàng đầu được trả về cho bạn. Giá trị numCandidates cao hơn cải thiện chất lượng kết quả với chi phí truy vấn chậm hơn đôi chút. Điểm khởi đầu phổ biến là đặt numCandidates gấp 10 đến 15 lần limit của bạn.

Tôi có thể dùng semantic search và text search cùng lúc không?

Có. MongoDB hỗ trợ hybrid search, kết hợp $vectorSearch$search trong một pipeline.

Semantic search có hoạt động với các ngôn ngữ khác ngoài tiếng Anh không?

Phụ thuộc vào mô hình embedding của bạn. nomic-embed-text-v1 được huấn luyện chủ yếu trên văn bản tiếng Anh. Đối với các trường hợp đa ngôn ngữ, hãy chọn mô hình embedding đa ngôn ngữ được huấn luyện trên những ngôn ngữ mà dữ liệu của bạn chứa.


Damilola Oladele's photo
Author
Damilola Oladele
Chủ đề
Có liên quan

blogs

Claude Opus 4.6: Tính năng, điểm chuẩn, các bài kiểm tra thực hành và hơn thế nữa

Mô hình mới nhất của Anthropic dẫn đầu bảng xếp hạng về mã hóa theo hướng tác nhân và suy luận phức tạp. Thêm nữa, nó có cửa sổ ngữ cảnh 1M.
Matt Crabtree's photo

Matt Crabtree

10 phút

Xem ThêmXem Thêm