Parte #3: pesquise seus dados semanticamente com o Atlas Vector Search do MongoDB
Dominic Frei6 min read • Published Feb 09, 2024 • Updated Sep 18, 2024
Avalie esse Tutorial
Esta parte final da série mostrará como usar o endpoint Amazon SageMaker criado no parte anterior e executar uma pesquisa semântica em seus dados usando o MongoDB Atlas Vector Search.
Este artigo é parte de uma série de três partes:
As duas partes mostradas neste tutorial serão:
- Criação e atualização de embeddings/vetores para seus dados.
- Criar vetores para uma consulta de pesquisa e enviá-los via Atlas Vector Search.
Se ainda não tiver feito isso, crie um novo cluster em sua conta do MongoDB Atlas. Certifique-se de marcar
[Add sample dataset](https://www.mongodb.com/developer/products/atlas/atlas-sample-datasets/)
para obter os dados de amostra com os quais trabalharemos imediatamente em seu cluster.Se você estiver usando um cluster que já tenha sido configurado, clique nos três pontos do cluster e, em seguida, em
Load Sample Dataset
.Há mais uma etapa que precisamos dar no Atlas, que é criar um índice de pesquisa, especificamente para o Vector Search.
Na visão geral do seu banco de dados, clique em
Create Index
.A página Search será exibida. Clique em
Create Search Index
aqui.Em seguida, escolha
Atlas Vector Search
- > JSON Editor
.Abra o banco de dados
sample_mflix
e escolha a coleçãoembedded_movies
(não movies
). Dê um nome ao seu índice (aqui, VectorSearchIndex
).A configuração do índice precisa indicar o número de dimensões. Isso depende do modelo e, no nosso caso, é 384. O caminho informa ao índice qual campo será usado para armazenar os vetores. Vamos chamá-lo de
embedding
aqui. A similaridade do texto geralmente é feita com a funçãocosine
.1 { 2 "fields": [ 3 { 4 "numDimensions": 384, 5 "path": "embedding", 6 "similarity": "cosine", 7 "type": "vector" 8 } 9 ] 10 }
Pressione
Create
e você está pronto para Go.Você está pronto para a parte final?
Vamos dar uma olhada no código (aqui, em Python)!
Na seção a seguir, examinaremos os três arquivos relevantes que mostram como você pode implementar um aplicativo de servidor que usa o endpoint do Amazon SageMaker.
O módulo
sagemaker.py
é o invólucro em torno do endpoint Lambda/Gatevia que criamos no exemplo anterior.Certifique-se de criar um arquivo
.env
com o URL salvo emEMBDDING_SERVICE
.Deve ficar assim:
1 MONGODB_CONNECTION_STRING="mongodb+srv://<username>:<password>@<cluster_id>.mongodb.net/?retryWrites=true&w=majority" 2 EMBEDDING_SERVICE="https://<your_endpoint>.amazonaws.com/TEST/sageMakerResource"
A função a seguir anexará a consulta para a qual queremos fazer o Atlas Search à URL e a executará.
1 import os 2 from typing import Optional 3 from urllib.parse import quote 4 5 import requests 6 from dotenv import load_dotenv 7 8 load_dotenv() 9 10 EMBEDDING_SERVICE = os.environ.get("EMBEDDING_SERVICE")
Como resultado, esperamos encontrar o vetor em um campo JSON chamado
embedding
.1 def create_embedding(plot: str) -> Optional[float]: 2 encoded_plot = quote(plot) 3 embedding_url = f"{EMBEDDING_SERVICE}?query={encoded_plot}" 4 5 embedding_response = requests.get(embedding_url) 6 embedding_vector = embedding_response.json()["embedding"] 7 8 return embedding_vector
O módulo
atlas.py
é o invólucro em torno de tudo o que é MongoDB Atlas.Semelhante a
sagemaker.py
, primeiro capturamos o MONGODB_CONNECTION_STRING
que você pode recuperar no Atlas clicando em Connect
em seu cluster. É a URL autenticada para o seu cluster. Também precisamos salvar MONGODB_CONNECTION_STRING em nosso arquivo .env.Em seguida, Go em frente e definimos um conjunto de variáveis que definimos nas partes anteriores, como
VectorSearchIndex
e embedding
, juntamente com os sample_mflix
dados de demonstração criados automaticamente .Usando o driver Atlas para Python (chamado PyMongo), criamos então um
MongoClient
que mantém a conexão com o cluster Atlas.1 import os 2 3 from dotenv import load_dotenv 4 from pymongo import MongoClient, UpdateOne 5 6 from sagemaker import create_embedding 7 8 load_dotenv() 9 10 MONGODB_CONNECTION_STRING = os.environ.get("MONGODB_CONNECTION_STRING") 11 DATABASE_NAME = "sample_mflix" 12 COLLECTION_NAME = "embedded_movies" 13 VECTOR_SEARCH_INDEX_NAME = "VectorSearchIndex" 14 EMBEDDING_PATH = "embedding" 15 mongo_client = MongoClient(MONGODB_CONNECTION_STRING) 16 database = mongo_client[DATABASE_NAME] 17 movies_collection = database[COLLECTION_NAME]
A primeira etapa será preparar de fato os dados já existentes com embeddings.
Esse é o único objetivo da função
add_missing_embeddings
.Criaremos um filtro para os documentos com incorporações ausentes e os recuperaremos do banco de dados, mostrando apenas seu gráfico, que é o único campo em que estamos interessados por enquanto.
Supondo que encontraremos apenas alguns por vez, podemos examiná-los e chamar o endpoint
create_embedding
para cada um, criando uma incorporação para o enredo do filme.Em seguida, adicionaremos essas novas incorporações à array
movies_to_update
para que, a longo prazo, precisemos apenas de um bulk_write
para o banco de dados, o que torna a chamada mais eficiente.Observe que, para criar grandes conjuntos de dados com muitas incorporações, convém ajustar a função lambda para receber uma matriz de consultas em vez de apenas uma única consulta. Para este exemplo simples, servirá.
1 def add_missing_embeddings(): 2 movies_with_a_plot_without_embedding_filter = { 3 "$and": [ 4 {"plot": {"$exists": True, "$ne": ""}}, 5 {"embedding": {"$exists": False}}, 6 ] 7 } 8 only_show_plot_projection = {"plot": 1} 9 10 movies = movies_collection.find( 11 movies_with_a_plot_without_embedding_filter, 12 only_show_plot_projection, 13 ) 14 15 movies_to_update = [] 16 17 for movie in movies: 18 embedding = create_embedding(movie["plot"]) 19 update_operation = UpdateOne( 20 {"_id": movie["_id"]}, 21 {"$set": {"embedding": embedding}}, 22 ) 23 movies_to_update.append(update_operation) 24 25 if movies_to_update: 26 result = movies_collection.bulk_write(movies_to_update) 27 print(f"Updated {result.modified_count} movies") 28 29 else: 30 print("No movies to update")
Agora que os dados estão preparados, adicionamos mais duas funções de que precisamos para oferecer um bom serviço REST para nosso aplicativo cliente.
Primeiro, queremos poder atualizar o gráfico, o que significa que precisamos atualizar os embeddings novamente.
O
update_plot
é semelhante à função inicial do add_missing_embeddings
, mas um pouco mais simples, pois só precisamos atualizar um documento.1 def update_plot(title: str, plot: str) -> dict: 2 embedding = create_embedding(plot) 3 4 result = movies_collection.find_one_and_update( 5 {"title": title}, 6 {"$set": {"plot": plot, "embedding": embedding}}, 7 return_document=True, 8 ) 9 10 return result
A outra função que precisamos oferecer é o vetor real do Atlas Search. Isso pode ser feito usando a aggregation pipeline doMongoDB Atlas , que pode ser acessada por meio do driver doAtlas .
O estágio
$vectorSearch
precisa incluir o nome do índice que queremos usar, o caminho para a incorporação e as informações sobre quantos resultados queremos obter. Desta vez, queremos apenas recuperar o título, por isso adicionamos um estágio$project
ao pipeline. Certifique-se de usar list
para transformar o cursor que a pesquisa retorna em uma lista de python.1 def execute_vector_search(vector: [float]) -> list[dict]: 2 vector_search_query = { 3 "$vectorSearch": { 4 "index": VECTOR_SEARCH_INDEX_NAME, 5 "path": EMBEDDING_PATH, 6 "queryVector": vector, 7 "numCandidates": 10, 8 "limit": 5, 9 } 10 } 11 projection = {"$project": {"_id": 0, "title": 1}} 12 results = movies_collection.aggregate([vector_search_query, projection]) 13 results_list = list(results) 14 15 return results_list
Agora, podemos colocar tudo junto. Vamos usar o Flusk para expor um serviço REST para nosso aplicativo cliente.
1 from flask import Flask, request, jsonify 2 3 from atlas import execute_vector_search, update_plot 4 from sagemaker import create_embedding 5 6 app = Flask(__name__)
Uma rota que queremos expor é
/movies/<title>
que pode ser executada com uma operaçãoPUT
para atualizar o enredo de um filme dado o título. O título será um parâmetro de consulta enquanto o gráfico é passado por meio do corpo. Esta função está usando o update_plot
que criamos antes em atlas.py
e retorna o filme com seu novo enredo em caso de sucesso.1 @app.route("/movies/<title>", methods=["PUT"]) 2 def update_movie(title: str): 3 try: 4 request_json = request.get_json() 5 plot = request_json["plot"] 6 updated_movie = update_plot(title, plot) 7 8 if updated_movie: 9 return jsonify( 10 { 11 "message": "Movie updated successfully", 12 "updated_movie": updated_movie, 13 } 14 ) 15 else: 16 return jsonify({"error": f"Movie with title {title} not found"}), 404 17 18 except Exception as e: 19 return jsonify({"error": str(e)}), 500
O outro endpoint, finalmente, é a pesquisa vetorial:
/movies/search
.Um
query
é POST
'ed para esse ponto de extremidade que, em seguida, usará create_embedding
primeiro para criar um vetor a partir dessa consulta. Observe que também precisamos criar vetores para a consulta, pois é disso que a pesquisa de vetores precisa para compará-la com os dados reais (ou melhor, com seus embeddings).Em seguida, chamamos
execute_vector_search
com esse embedding
para recuperar os resultados, que serão retornados em caso de sucesso.1 @app.route("/movies/search", methods=["POST"]) 2 def search_movies(): 3 try: 4 request_json = request.get_json() 5 query = request_json["query"] 6 embedding = create_embedding(query) 7 8 results = execute_vector_search(embedding) 9 10 jsonified_results = jsonify( 11 { 12 "message": "Movies searched successfully", 13 "results": results, 14 } 15 ) 16 17 return jsonified_results 18 19 except Exception as e: 20 return jsonify({"error": str(e)}), 500 21 22 23 if __name__ == "__main__": 24 app.run(debug=True)
E isso é tudo que você precisa fazer. Foi fácil, não foi?
Go em frente e execute o aplicativo Pipeline (main.py) e, quando estiver pronto, envie um cURL para ver o Atlas Vector Search em ação. Aqui está um exemplo ao executá-lo localmente:
1 curl -X POST -H "Content-Type: application/json" -d '{"query": "A movie about the Earth, Mars and an invasion."}' http://127.0.0.1:5000/movies/search
Isso deve levar ao seguinte resultado:
1 { 2 "message": "Movies searched successfully", 3 "results": [ 4 { 5 "title": "The War of the Worlds" 6 }, 7 { 8 "title": "The 6th Day" 9 }, 10 { 11 "title": "Pixels" 12 }, 13 { 14 "title": "Journey to Saturn" 15 }, 16 { 17 "title": "Moonraker" 18 } 19 ] 20 }
Guerra dos Mundos - um filme sobre a Terra, Marte e uma invasão. E que ótimo, não é?
É claro que esta é apenas uma visão geral rápida de como usar o Amazon SageMaker para criar vetores e, em seguida, pesquisar por meio do MongoDB Atlas Vector Search.
Temos um curso completo para você aprender sobre todas essas partes em detalhes. Visite a página do Search Laboratório no GitHub para saber mais.
✅ Já tem uma conta AWS? O Atlas aceita pagamentos por uso por meio do AWS Marketplace (AWS MP) sem nenhum compromisso inicial — basta se inscrever no MongoDB Atlas por meio do AWS Marketplace.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.