Docs 菜单
Docs 主页
/
MongoDB Atlas
/ /

使用 Atlas Vector Search 构建本地 RAG 实现

在此页面上

  • 背景
  • 先决条件
  • 创建本地部署或Atlas集群
  • 设置环境
  • 使用本地模型生成嵌入
  • 创建 Atlas Vector Search 索引
  • 回答当地法学硕士问题

本教程演示了如何在本地实现检索增强生成 (RAG),而无需 API 密钥或信用。要了解有关 RAG 的更多信息,请参阅 使用 Atlas Vector Search 进行检索增强生成 (RAG)。

具体来说,您需要执行以下操作:

  1. 创建本地Atlas部署或在云上部署集群。

  2. 设置环境。

  3. 使用本地嵌入模型生成向量嵌入。

  4. 在您的数据上创建一个 Atlas Vector Search 索引。

  5. 使用本地 LLM 来回答有关数据的问题。


➤ 使用 Select your language(选择您的语言)下拉菜单设置此页面上示例的语言。


要完成本教程,您可以使用 Atlas CLI 创建本地 Atlas 部署,也可以在云上部署集群 。Atlas CLI 是 MongoDB Atlas 的命令行界面,您可以使用 Atlas CLI 从终端与 Atlas 进行交互,完成各种任务,包括创建本地 Atlas 部署。要了解更多信息,请参阅从 Atlas CLI 管理本地和云部署。

注意

本地 Atlas 部署仅用于测试。对于生产环境,部署一个集群。

在本教程中,您还将使用以下开源模型:

有几种方法可以在本地下载和部署 LLM。在本教程中,您将下载 Ollama 并提取上面列出的开源模型来执行 RAG 任务。

本教程还使用一种流行的开源 LLM 框架 LangChain 的 Go 语言端口来连接这些模型,并将其与 Atlas Vector Search 集成。如果您喜欢不同的模型或不同的框架,可以根据自己偏好的设置,将 Ollama 模型名称或 LangChain 库组件替换为相应的名称和组件来调整本教程。

可通过多种方法在本地下载和部署LLM。在本教程中,您下载Ollama 并拉取以下开源模型来执行 RAG 任务:

本教程还使用 LangChain4 j(一种流行的Java开源 LLM框架)连接到这些模型并将其与Atlas Vector Search集成。如果您更喜欢不同的模型或框架,您可以调整本教程,将 Ollama 模型名称或 LangChain4 j 库组件替换为您首选设置的等效名称。

在本教程中,您还将使用以下开源模型:

有几种方法可以在本地下载和部署 LLM。在本教程中,您将使用 GPT4All(一个用于本地 LLM 开发的开源生态系统)下载 Mistral 7B 模型。

在学习本教程时,您将使用交互式 Python 笔记本。此环境允许您创建和执行单独的代码块,而无需每次都运行整个文件。

在本教程中,您还将使用以下开源模型:

有几种方法可以在本地下载和部署 LLM。在本教程中,您将使用 GPT4All(一个用于本地 LLM 开发的开源生态系统)下载 Mistral 7B 模型。

如要完成本教程,您必须具备以下条件:

如要完成本教程,您必须具备以下条件:

如要完成本教程,您必须具备以下条件:

如要完成本教程,您必须具备以下条件:

注意

如果您使用诸如 Colab 之类的托管服务 ,请确保您有足够的 RAM 来运行本教程。否则,可能会遇到性能问题。

本教程需要本地或云Atlas部署,并加载示例AirBnB 列表数据集以用作向量数据库。

如果您现有的Atlas 集群运行MongoDB 6.0.11、7.0.2 或更高版本,且已加载 sample_airbnb.listingsAndReviews示例数据,则可跳过此步骤。

您可以使用Atlas CLI创建本地Atlas部署,也可以在云上部署集群。

您可以使用Atlas CLI创建本地部署。

1

在终端中,运行 atlas auth login,使用 Atlas 登录凭据进行身份验证。要了解更多令牌,请参阅从 Atlas CLI 连接

注意

如果您没有现成的 Atlas 帐户,请运行 atlas setup创建新帐户。

2

运行 atlas deployments setup 并按照提示创建本地部署。

有关详细说明,请参阅创建本地 Atlas 部署。

3
  1. 在终端中运行以下命令以下载示例数据:

    curl https://atlas-education.s3.amazonaws.com/sampledata.archive -o sampledata.archive
  2. 运行以下命令将数据加载到部署中,将 <port-number> 替换为托管部署的端口:

    mongorestore --archive=sampledata.archive --port=<port-number>

在本部分中,您将为本教程设立环境。创建项目,安装所需的包,并定义连接字符串:

1

在终端中运行以下命令,创建名为 local-rag-mongodb 的新目录并初始化项目:

mkdir local-rag-mongodb
cd local-rag-mongodb
go mod init local-rag-mongodb
2

运行以下命令:

go get github.com/joho/godotenv
go get go.mongodb.org/mongo-driver/mongo
go get github.com/tmc/langchaingo/llms
go get github.com/tmc/langchaingo/llms/ollama
go get github.com/tmc/langchaingo/prompts
3

在项目中,创建一个 .env 文件来存储连接字符串。

.env
ATLAS_CONNECTION_STRING = "<connection-string>"

<connection-string> 占位符值替换为 Atlas 连接字符串。

如果您使用本地 Atlas 部署,则连接字符串将遵循此格式,将 <port-number> 替换为本地部署的端口。

ATLAS_CONNECTION_STRING = "mongodb://localhost:<port-number>/?directConnection=true"

如果您使用的是 Atlas 集群,连接字符串应遵循此格式,将 "<connection-string>"; 替换为 Atlas 集群的SRV 连接字符串:

ATLAS_CONNECTION_STRING = "<connection-string>"

注意

连接字符串应使用以下格式:

mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

在本部分中,您将为本教程设立环境。创建项目,安装所需的包,并定义连接字符串:

1
  1. 在 IDE 中,使用 Maven 或 Gradle 创建名为 local-rag-mongodb 的Java项目。

  2. 根据您的包管理器,添加以下依赖项:

    如果使用 Maven,请将以下依赖项添加到项目的 pom.xml文件的 dependencies大量中:

    pom.xml
    <dependencies>
    <!-- MongoDB Java Sync Driver v5.2.0 or later -->
    <dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>[5.2.0,)</version>
    </dependency>
    <!-- Java library for working with Ollama -->
    <dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-ollama</artifactId>
    <version>0.35.0</version>
    </dependency>
    </dependencies>

    如果您使用 Gradle,请将以下内容添加到项目 build.gradle文件的 dependencies大量中:

    build.gradle
    dependencies {
    // MongoDB Java Sync Driver v5.2.0 or later
    implementation 'org.mongodb:mongodb-driver-sync:[5.2.0,)'
    // Java library for working with Ollama
    implementation 'dev.langchain4j:langchain4j-ollama:0.35.0'
    }
  3. 运行包管理器以安装项目。

2

注意

此示例在 IDE 中设置变量。生产应用程序可以通过部署配置、CI/CD管道或密钥管理器来管理环境变量,但您可以调整提供的代码以适合您的使用案例。

在 IDE 中,创建新的配置模板并将以下变量添加到项目中:

  • 如果您使用的是 IntelliJ IDEA,请创建新的 Application运行配置模板,然后在 Environment variables字段中将变量添加为分号分隔的值(示例FOO=123;BAR=456 )。应用更改并单击 OK

    要学习;了解更多信息,请参阅 IntelliJ IDEA 文档的从模板创建运行/调试配置部分。

  • 如果您使用的是 Eclipse,请创建新的 Java Application 启动配置,然后将每个变量作为新的键值对添加到 Environment标签页中。应用更改并单击 OK

    要学习;了解更多信息,请参阅 Eclipse IDE 文档的创建Java应用程序启动配置部分。

<port-number> 替换为本地部署的端口。

连接字符串应遵循以下格式:

ATLAS_CONNECTION_STRING = "mongodb://localhost:<port-number>/?directConnection=true"

用 Atlas 集群的 SRV 连接字符串替换 <connection-string> 占位符值。

连接字符串应使用以下格式:

mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

在本部分中,您将为本教程设立环境。创建项目,安装所需的包,并定义连接字符串:

1

在终端中运行以下命令,创建名为 local-rag-mongodb 的新目录并初始化项目:

mkdir local-rag-mongodb
cd local-rag-mongodb
npm init -y
2

运行以下命令:

npm install mongodb @xenova/transformers node-gyp gpt4all
3

在项目的package.json文件中,按以下示例所示指定type字段,然后保存文件。

package.json
{
"name": "local-rag-mongodb",
"type": "module",
...
}
4

在项目中,创建一个 .env 文件来存储连接字符串。

.env
ATLAS_CONNECTION_STRING = "<connection-string>"

<connection-string> 占位符值替换为 Atlas 连接字符串。

如果您使用本地 Atlas 部署,则连接字符串将遵循此格式,将 <port-number> 替换为本地部署的端口。

ATLAS_CONNECTION_STRING = "mongodb://localhost:<port-number>/?directConnection=true";

如果您使用的是 Atlas 集群,连接字符串应遵循此格式,将 "<connection-string>"; 替换为 Atlas 集群的SRV 连接字符串:

ATLAS_CONNECTION_STRING = "<connection-string>";

注意

连接字符串应使用以下格式:

mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

注意

最低 Node.js 版本要求

Node.js v 20 .x 引入了 --env-file 选项。如果您使用的是旧版本的 Node.js,请将 dotenv 包添加到项目中,或使用其他方法来管理环境变量。

在本节中,您将为本教程设置环境。

1

在终端中运行以下命令,创建一个名为 local-rag-mongodb 的新目录。

mkdir local-rag-mongodb
cd local-rag-mongodb
2

local-rag-mongodb 目录中,保存扩展名为 .ipynb 的文件。您将在笔记本中运行本教程的其余代码片段。您必须为每个代码片段创建一个新的代码区块。

3

在笔记本中运行以下命令:

pip install --quiet pymongo gpt4all sentence_transformers
4

如果您使用本地 Atlas 部署,请在笔记本中运行以下代码,将 <port-number> 替换为本地部署的端口。

ATLAS_CONNECTION_STRING = ("mongodb://localhost:<port-number>/?directConnection=true")

如果使用的是 Atlas 集群,请在笔记本中运行以下代码,将 <connection-string> 替换为 Atlas 集群的SRV 连接字符串:

ATLAS_CONNECTION_STRING = ("<connection-string>")

注意

连接字符串应使用以下格式:

mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

在本节中,您将加载本地嵌入模型,并使用 sample_airbnb 数据库中的数据生成向量嵌入,该数据库包含一个名为 listingsAndReviews 的集合。

1

这个示例使用了 Ollama 的 nomic-embed-text 模型。

运行以下命令以拉取嵌入模型:

ollama pull nomic-embed-text
2
  1. 创建 common 目录来存储将在多个步骤中重复使用的代码。

    mkdir common && cd common
  2. 创建一个名为 get-embeddings.go 的文件,并将以下代码粘贴到其中:

    get-embeddings.go
    package common
    import (
    "context"
    "log"
    "github.com/tmc/langchaingo/llms/ollama"
    )
    func GetEmbeddings(documents []string) [][]float32 {
    llm, err := ollama.New(ollama.WithModel("nomic-embed-text"))
    if err != nil {
    log.Fatalf("failed to connect to ollama: %v", err)
    }
    ctx := context.Background()
    embs, err := llm.CreateEmbedding(ctx, documents)
    if err != nil {
    log.Fatalf("failed to create ollama embedding: %v", err)
    }
    return embs
    }
  3. 为了简化此集合中文档与 BSON 的编组和解组,请创建一个名为 models.go 的文件并将以下代码粘贴到其中:

    models.go
    package common
    import (
    "time"
    "go.mongodb.org/mongo-driver/bson/primitive"
    )
    type Image struct {
    ThumbnailURL string `bson:"thumbnail_url"`
    MediumURL string `bson:"medium_url"`
    PictureURL string `bson:"picture_url"`
    XLPictureURL string `bson:"xl_picture_url"`
    }
    type Host struct {
    ID string `bson:"host_id"`
    URL string `bson:"host_url"`
    Name string `bson:"host_name"`
    Location string `bson:"host_location"`
    About string `bson:"host_about"`
    ThumbnailURL string `bson:"host_thumbnail_url"`
    PictureURL string `bson:"host_picture_url"`
    Neighborhood string `bson:"host_neighborhood"`
    IsSuperhost bool `bson:"host_is_superhost"`
    HasProfilePic bool `bson:"host_has_profile_pic"`
    IdentityVerified bool `bson:"host_identity_verified"`
    ListingsCount int32 `bson:"host_listings_count"`
    TotalListingsCount int32 `bson:"host_total_listings_count"`
    Verifications []string `bson:"host_verifications"`
    }
    type Location struct {
    Type string `bson:"type"`
    Coordinates []float64 `bson:"coordinates"`
    IsLocationExact bool `bson:"is_location_exact"`
    }
    type Address struct {
    Street string `bson:"street"`
    Suburb string `bson:"suburb"`
    GovernmentArea string `bson:"government_area"`
    Market string `bson:"market"`
    Country string `bson:"Country"`
    CountryCode string `bson:"country_code"`
    Location Location `bson:"location"`
    }
    type Availability struct {
    Thirty int32 `bson:"availability_30"`
    Sixty int32 `bson:"availability_60"`
    Ninety int32 `bson:"availability_90"`
    ThreeSixtyFive int32 `bson:"availability_365"`
    }
    type ReviewScores struct {
    Accuracy int32 `bson:"review_scores_accuracy"`
    Cleanliness int32 `bson:"review_scores_cleanliness"`
    CheckIn int32 `bson:"review_scores_checkin"`
    Communication int32 `bson:"review_scores_communication"`
    Location int32 `bson:"review_scores_location"`
    Value int32 `bson:"review_scores_value"`
    Rating int32 `bson:"review_scores_rating"`
    }
    type Review struct {
    ID string `bson:"_id"`
    Date time.Time `bson:"date,omitempty"`
    ListingId string `bson:"listing_id"`
    ReviewerId string `bson:"reviewer_id"`
    ReviewerName string `bson:"reviewer_name"`
    Comments string `bson:"comments"`
    }
    type Listing struct {
    ID string `bson:"_id"`
    ListingURL string `bson:"listing_url"`
    Name string `bson:"name"`
    Summary string `bson:"summary"`
    Space string `bson:"space"`
    Description string `bson:"description"`
    NeighborhoodOverview string `bson:"neighborhood_overview"`
    Notes string `bson:"notes"`
    Transit string `bson:"transit"`
    Access string `bson:"access"`
    Interaction string `bson:"interaction"`
    HouseRules string `bson:"house_rules"`
    PropertyType string `bson:"property_type"`
    RoomType string `bson:"room_type"`
    BedType string `bson:"bed_type"`
    MinimumNights string `bson:"minimum_nights"`
    MaximumNights string `bson:"maximum_nights"`
    CancellationPolicy string `bson:"cancellation_policy"`
    LastScraped time.Time `bson:"last_scraped,omitempty"`
    CalendarLastScraped time.Time `bson:"calendar_last_scraped,omitempty"`
    FirstReview time.Time `bson:"first_review,omitempty"`
    LastReview time.Time `bson:"last_review,omitempty"`
    Accommodates int32 `bson:"accommodates"`
    Bedrooms int32 `bson:"bedrooms"`
    Beds int32 `bson:"beds"`
    NumberOfReviews int32 `bson:"number_of_reviews"`
    Bathrooms primitive.Decimal128 `bson:"bathrooms"`
    Amenities []string `bson:"amenities"`
    Price primitive.Decimal128 `bson:"price"`
    WeeklyPrice primitive.Decimal128 `bson:"weekly_price"`
    MonthlyPrice primitive.Decimal128 `bson:"monthly_price"`
    CleaningFee primitive.Decimal128 `bson:"cleaning_fee"`
    ExtraPeople primitive.Decimal128 `bson:"extra_people"`
    GuestsIncluded primitive.Decimal128 `bson:"guests_included"`
    Image Image `bson:"images"`
    Host Host `bson:"host"`
    Address Address `bson:"address"`
    Availability Availability `bson:"availability"`
    ReviewScores ReviewScores `bson:"review_scores"`
    Reviews []Review `bson:"reviews"`
    Embeddings []float32 `bson:"embeddings,omitempty"`
    }
  4. 返回根目录。

    cd ../
  5. 创建另一个名为 generate-embeddings.go 的文件,并将以下代码粘贴到其中:

    generate-embeddings.go
    1package main
    2
    3import (
    4 "context"
    5 "local-rag-mongodb/common" // Module that contains the models and GetEmbeddings function
    6 "log"
    7 "os"
    8
    9 "github.com/joho/godotenv"
    10 "go.mongodb.org/mongo-driver/bson"
    11 "go.mongodb.org/mongo-driver/mongo"
    12 "go.mongodb.org/mongo-driver/mongo/options"
    13)
    14
    15func main() {
    16 ctx := context.Background()
    17
    18 if err := godotenv.Load(); err != nil {
    19 log.Println("no .env file found")
    20 }
    21
    22 // Connect to your Atlas cluster
    23 uri := os.Getenv("ATLAS_CONNECTION_STRING")
    24 if uri == "" {
    25 log.Fatal("set your 'ATLAS_CONNECTION_STRING' environment variable.")
    26 }
    27 clientOptions := options.Client().ApplyURI(uri)
    28 client, err := mongo.Connect(ctx, clientOptions)
    29 if err != nil {
    30 log.Fatalf("failed to connect to the server: %v", err)
    31 }
    32 defer func() { _ = client.Disconnect(ctx) }()
    33
    34 // Set the namespace
    35 coll := client.Database("sample_airbnb").Collection("listingsAndReviews")
    36
    37 filter := bson.D{
    38 {"$and",
    39 bson.A{
    40 bson.D{
    41 {"$and",
    42 bson.A{
    43 bson.D{{"summary", bson.D{{"$exists", true}}}},
    44 bson.D{{"summary", bson.D{{"$ne", ""}}}},
    45 },
    46 }},
    47 bson.D{{"embeddings", bson.D{{"$exists", false}}}},
    48 }},
    49 }
    50
    51 findOptions := options.Find().SetLimit(250)
    52
    53 cursor, err := coll.Find(ctx, filter, findOptions)
    54 if err != nil {
    55 log.Fatalf("failed to retrieve data from the server: %v", err)
    56 }
    57
    58 var listings []common.Listing
    59 if err = cursor.All(ctx, &listings); err != nil {
    60 log.Fatalf("failed to unmarshal retrieved docs to model objects: %v", err)
    61 }
    62
    63 var summaries []string
    64 for _, listing := range listings {
    65 summaries = append(summaries, listing.Summary)
    66 }
    67
    68 log.Println("Generating embeddings.")
    69 embeddings := common.GetEmbeddings(summaries)
    70
    71 updateDocuments := make([]mongo.WriteModel, len(listings))
    72 for i := range updateDocuments {
    73 updateDocuments[i] = mongo.NewUpdateOneModel().
    74 SetFilter(bson.D{{"_id", listings[i].ID}}).
    75 SetUpdate(bson.D{{"$set", bson.D{{"embeddings", embeddings[i]}}}})
    76 }
    77
    78 bulkWriteOptions := options.BulkWrite().SetOrdered(false)
    79
    80 result, err := coll.BulkWrite(ctx, updateDocuments, bulkWriteOptions)
    81 if err != nil {
    82 log.Fatalf("failed to update documents: %v", err)
    83 }
    84
    85 log.Printf("%d documents updated successfully.", result.MatchedCount)
    86}

    在此示例中,我们在生成嵌入时设立了 250 个文档的限制。为集合中超过 5000 个文档生成嵌入的过程很慢。如果要更改为其生成嵌入的文档数量:

    • 更改文档数量:在 Find() 选项中调整第 52 行的 .SetLimit(250) 数量。

    • 为所有文档生成嵌入:忽略第 54 行 Find() 调用中的选项。

  6. 运行以下命令来执行代码:

    go run generate-embeddings.go
    2024/10/10 15:49:23 Generating embeddings.
    2024/10/10 15:49:28 250 documents updated successfully.
1

运行以下命令,从 Ollama 中提取 nomic-embed-text 模型:

ollama pull nomic-embed-text
2

创建一个名为 OllamaModels.java 的文件并粘贴以下代码。

此代码定义了您将在项目中使用的本地 Ollama 嵌入和聊天模型。我们将在后续步骤中使用聊天模型。您可以根据首选设置的需要调整或创建其他模型。

此代码还定义了两种方法,以使用您之前下载的嵌入模型为给定输入生成嵌入:

  • getEmbeddings多个输入:List<String> 方法接受文本输入大量(),允许您在单个API调用中创建多个嵌入。该方法将API提供的浮点数数组转换为BSON双精度数组,以便存储在Atlas 集群中。

  • 单个输入:getEmbedding 方法接受单个String ,它表示要对向量数据进行的查询。该方法将API提供的浮点数大量转换为BSON双精度大量,以便在查询集合时使用。

OllamaModels.java
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
import dev.langchain4j.model.output.Response;
import org.bson.BsonArray;
import org.bson.BsonDouble;
import java.util.List;
import static java.time.Duration.ofSeconds;
public class OllamaModels {
private static final String host = "http://localhost:11434";
private static OllamaEmbeddingModel embeddingModel;
private static OllamaChatModel chatModel;
/**
* Returns the Ollama embedding model used by the getEmbeddings() and getEmbedding() methods
* to generate vector embeddings.
*/
public static OllamaEmbeddingModel getEmbeddingModel() {
if (embeddingModel == null) {
embeddingModel = OllamaEmbeddingModel.builder()
.timeout(ofSeconds(10))
.modelName("nomic-embed-text")
.baseUrl(host)
.build();
}
return embeddingModel;
}
/**
* Returns the Ollama chat model interface used by the createPrompt() method
* to process queries and generate responses.
*/
public static OllamaChatModel getChatModel() {
if (chatModel == null) {
chatModel = OllamaChatModel.builder()
.timeout(ofSeconds(25))
.modelName("mistral")
.baseUrl(host)
.build();
}
return chatModel;
}
/**
* Takes an array of strings and returns a collection of BSON array embeddings
* to store in the database.
*/
public static List<BsonArray> getEmbeddings(List<String> texts) {
List<TextSegment> textSegments = texts.stream()
.map(TextSegment::from)
.toList();
Response<List<Embedding>> response = getEmbeddingModel().embedAll(textSegments);
return response.content().stream()
.map(e -> new BsonArray(
e.vectorAsList().stream()
.map(BsonDouble::new)
.toList()))
.toList();
}
/**
* Takes a single string and returns a BSON array embedding to
* use in a vector query.
*/
public static BsonArray getEmbedding(String text) {
Response<Embedding> response = getEmbeddingModel().embed(text);
return new BsonArray(
response.content().vectorAsList().stream()
.map(BsonDouble::new)
.toList());
}
}
3

创建一个名为EmbeddingGenerator.java的文件并粘贴以下代码。

此代码使用getEmbeddings 方法和MongoDB Java同步驱动程序来执行以下操作:

  1. 连接到本地Atlas部署或Atlas 集群。

  2. sample_airbnb.listingsAndReviews集合中获取具有非空 summary字段的文档子集。

    注意

    出于演示目的,我们设立了 limit 个 250 文档以减少处理时间。您可以根据需要调整或删除此限制,以更好地适应您的使用案例。

  3. 使用您之前定义的 getEmbeddings 方法,从每个文档的 summary字段生成嵌入。

  4. 使用包含相应嵌入值的新 embedding字段更新每个文档。

EmbeddingGenerator.java
import com.mongodb.MongoException;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.Updates;
import com.mongodb.client.model.WriteModel;
import org.bson.BsonArray;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.ArrayList;
import java.util.List;
public class EmbeddingGenerator {
public static void main(String[] args) {
String uri = System.getenv("ATLAS_CONNECTION_STRING");
if (uri == null || uri.isEmpty()) {
throw new RuntimeException("ATLAS_CONNECTION_STRING env variable is not set or is empty.");
}
// establish connection and set namespace
try (MongoClient mongoClient = MongoClients.create(uri)) {
MongoDatabase database = mongoClient.getDatabase("sample_airbnb");
MongoCollection<Document> collection = database.getCollection("listingsAndReviews");
// define parameters for the find() operation
// NOTE: this example uses a limit to reduce processing time
Bson projectionFields = Projections.fields(
Projections.include("_id", "summary"));
Bson filterSummary = Filters.ne("summary", "");
int limit = 250;
try (MongoCursor<Document> cursor = collection
.find(filterSummary)
.projection(projectionFields)
.limit(limit)
.iterator()) {
List<String> summaries = new ArrayList<>();
List<String> documentIds = new ArrayList<>();
while (cursor.hasNext()) {
Document document = cursor.next();
String summary = document.getString("summary");
String id = document.get("_id").toString();
summaries.add(summary);
documentIds.add(id);
}
// generate embeddings for the summary in each document
// and add to the document to the 'embeddings' array field
System.out.println("Generating embeddings for " + summaries.size() + " documents.");
System.out.println("This operation may take up to several minutes.");
List<BsonArray> embeddings = OllamaModels.getEmbeddings(summaries);
List<WriteModel<Document>> updateDocuments = new ArrayList<>();
for (int j = 0; j < summaries.size(); j++) {
UpdateOneModel<Document> updateDoc = new UpdateOneModel<>(
Filters.eq("_id", documentIds.get(j)),
Updates.set("embeddings", embeddings.get(j)));
updateDocuments.add(updateDoc);
}
// bulk write the updated documents to the 'listingsAndReviews' collection
int result = performBulkWrite(updateDocuments, collection);
System.out.println("Added embeddings successfully to " + result + " documents.");
}
} catch (MongoException me) {
throw new RuntimeException("Failed to connect to MongoDB", me);
} catch (Exception e) {
throw new RuntimeException("Operation failed: ", e);
}
}
/**
* Performs a bulk write operation on the specified collection.
*/
private static int performBulkWrite(List<WriteModel<Document>> updateDocuments, MongoCollection<Document> collection) {
if (updateDocuments.isEmpty()) {
return 0;
}
BulkWriteResult result;
try {
BulkWriteOptions options = new BulkWriteOptions().ordered(false);
result = collection.bulkWrite(updateDocuments, options);
return result.getModifiedCount();
} catch (MongoException me) {
throw new RuntimeException("Failed to insert documents", me);
}
}
}
4

保存并运行该文件。 输出类似如下所示:

Generating embeddings for 250 documents.
This operation may take up to several minutes.
Added embeddings successfully to 250 documents.
1

此示例使用了 Hugging Face 模型集线器中的 mixedbread-ai/mxbai-embed-large-v1 模型。下载模型文件的最简单方法是使用 Git 和 Git Large File Storage 克隆存储库。Hugging Face 需要用户访问令牌 Git over SSH 来验证克隆存储库的请求。

git clone https://<your-hugging-face-username>:<your-hugging-face-user-access-token>@huggingface.co/mixedbread-ai/mxbai-embed-large-v1
git clone git@hf.co:mixedbread-ai/mxbai-embed-large-v1

提示

Git 大文件存储

Hugging Face 模型文件很大,需要 Git 大文件存储 ( git-lfs) 来克隆存储库。如果您看到与大文件存储相关的错误,请确保已安装 git-lfs。

2

获取计算机上本地模型文件的路径。这是包含您刚刚克隆的 git 存储库的父目录。如果在为本教程创建的项目目录中克隆了模型存储库,则父目录路径应与以下内容类似:

/Users/<username>/local-rag-mongodb

检查模型目录,确保其中包含一个具有 model_quantized.onnx 文件的 onnx 目录:

cd mxbai-embed-large-v1/onnx
ls
model.onnx model_fp16.onnx model_quantized.onnx
3
  1. 导航回 local-rag-mongodb 父目录。

  2. 创建一个名为 get-embeddings.js 的文件,并将以下代码粘贴到其中:

    get-embeddings.js
    import { env, pipeline } from '@xenova/transformers';
    // Function to generate embeddings for given data
    export async function getEmbeddings(data) {
    // Replace this path with the parent directory that contains the model files
    env.localModelPath = '/Users/<username>/local-rag-mongodb/';
    env.allowRemoteModels = false;
    const task = 'feature-extraction';
    const model = 'mxbai-embed-large-v1';
    const embedder = await pipeline(
    task, model);
    const results = await embedder(data, { pooling: 'mean', normalize: true });
    return Array.from(results.data);
    }

    '/Users/<username>/local-rag-mongodb/' 替换为上一步中的本地路径。

  3. 创建另一个名为 generate-embeddings.js 的文件,并将以下代码粘贴到其中:

    generate-embeddings.js
    1import { MongoClient } from 'mongodb';
    2import { getEmbeddings } from './get-embeddings.js';
    3
    4async function run() {
    5 const client = new MongoClient(process.env.ATLAS_CONNECTION_STRING);
    6
    7 try {
    8 // Connect to your local MongoDB deployment
    9 await client.connect();
    10 const db = client.db("sample_airbnb");
    11 const collection = db.collection("listingsAndReviews");
    12
    13 const filter = { '$and': [
    14 { 'summary': { '$exists': true, '$ne': null } },
    15 { 'embeddings': { '$exists': false } }
    16 ]};
    17
    18 // This is a long-running operation for all docs in the collection,
    19 // so we limit the docs for this example
    20 const cursor = collection.find(filter).limit(50);
    21
    22 // To verify that you have the local embedding model configured properly,
    23 // try generating an embedding for one document
    24 const firstDoc = await cursor.next();
    25 if (!firstDoc) {
    26 console.log('No document found.');
    27 return;
    28 }
    29
    30 const firstDocEmbeddings = await getEmbeddings(firstDoc.summary);
    31 console.log(firstDocEmbeddings);
    32
    33 // After confirming you are successfully generating embeddings,
    34 // uncomment the following code to generate embeddings for all docs.
    35 /* cursor.rewind(); // Reset the cursor to process documents again
    36 * console.log("Generating embeddings for documents. Standby.");
    37 * let updatedDocCount = 0;
    38 *
    39 * for await (const doc of cursor) {
    40 * const text = doc.summary;
    41 * const embeddings = await getEmbeddings(text);
    42 * await collection.updateOne({ "_id": doc._id },
    43 * {
    44 * "$set": {
    45 * "embeddings": embeddings
    46 * }
    47 * }
    48 * );
    49 * updatedDocCount += 1;
    50 * }
    51 * console.log("Count of documents updated: " + updatedDocCount);
    52 */
    53 } catch (err) {
    54 console.log(err.stack);
    55 }
    56 finally {
    57 await client.close();
    58 }
    59}
    60run().catch(console.dir);

    此代码包含几行代码,用于测试您是否已正确下载模型并使用正确的路径。执行以下命令以执行代码:

    node --env-file=.env generate-embeddings.js
    Tensor {
    dims: [ 1, 1024 ],
    type: 'float32',
    data: Float32Array(1024) [
    -0.01897735893726349, -0.001120976754464209, -0.021224822849035263,
    -0.023649735376238823, -0.03350808471441269, -0.0014186901971697807,
    -0.009617107920348644, 0.03344292938709259, 0.05424851179122925,
    -0.025904450565576553, 0.029770011082291603, -0.0006215018220245838,
    0.011056603863835335, -0.018984895199537277, 0.03985185548663139,
    -0.015273082070052624, -0.03193040192127228, 0.018376577645540237,
    -0.02236943319439888, 0.01433168537914753, 0.02085157483816147,
    -0.005689046811312437, -0.05541415512561798, -0.055907104164361954,
    -0.019112611189484596, 0.02196515165269375, 0.027313007041811943,
    -0.008618313819169998, 0.045496534556150436, 0.06271681934595108,
    -0.0028660669922828674, -0.02433634363114834, 0.02016191929578781,
    -0.013882477767765522, -0.025465600192546844, 0.0000950733374338597,
    0.018200192600488663, -0.010413561016321182, -0.002004098379984498,
    -0.058351870626211166, 0.01749623566865921, -0.013926318846642971,
    -0.00278360559605062, -0.010333008132874966, 0.004406726453453302,
    0.04118744656443596, 0.02210155501961708, -0.016340743750333786,
    0.004163357429206371, -0.018561601638793945, 0.0021984230261296034,
    -0.012378614395856857, 0.026662321761250496, -0.006476820446550846,
    0.001278138137422502, -0.010084952227771282, -0.055993322283029556,
    -0.015850437805056572, 0.015145729295909405, 0.07512971013784409,
    -0.004111358895897865, -0.028162647038698196, 0.023396577686071396,
    -0.01159974467009306, 0.021751703694462776, 0.006198467221111059,
    0.014084039255976677, -0.0003913900291081518, 0.006310020107775927,
    -0.04500332102179527, 0.017774192616343498, -0.018170733004808426,
    0.026185045018792152, -0.04488714039325714, -0.048510149121284485,
    0.015152698382735252, 0.012136898003518581, 0.0405895821750164,
    -0.024783289059996605, -0.05514788627624512, 0.03484730422496796,
    -0.013530988246202469, 0.0319477915763855, 0.04537525027990341,
    -0.04497901350259781, 0.009621822275221348, -0.013845544308423996,
    0.0046155862510204315, 0.03047163411974907, 0.0058857654221355915,
    0.005858785007148981, 0.01180865429341793, 0.02734190598130226,
    0.012322399765253067, 0.03992653638124466, 0.015777742490172386,
    0.017797520384192467, 0.02265017107129097, -0.018233606591820717,
    0.02064627595245838,
    ... 924 more items
    ],
    size: 1024
    }
  4. 在确认使用本地模型成功生成嵌入后,您可以选择取消注释第 35-52 行中的代码,为集合中的所有文档生成嵌入。保存文件。

    然后,请运行命令以执行代码:

    node --env-file=.env generate-embeddings.js
    [
    Tensor {
    dims: [ 1024 ],
    type: 'float32',
    data: Float32Array(1024) [
    -0.043243519961833954, 0.01316747535020113, -0.011639945209026337,
    -0.025046885013580322, 0.005129443947225809, -0.02003324404358864,
    0.005245734006166458, 0.10105721652507782, 0.05425914749503136,
    -0.010824322700500488, 0.021903572604060173, 0.048009492456912994,
    0.01291663944721222, -0.015903260558843613, -0.008034848608076572,
    -0.003592714900150895, -0.029337648302316666, 0.02282896265387535,
    -0.029112281277775764, 0.011099508963525295, -0.012238143011927605,
    -0.008351574651896954, -0.048714976757764816, 0.001015961286611855,
    0.02252192236483097, 0.04426417499780655, 0.03514830768108368,
    -0.02088250033557415, 0.06391220539808273, 0.06896235048770905,
    -0.015386332757771015, -0.019206153228878975, 0.015263230539858341,
    -0.00019019744649995118, -0.032121095806360245, 0.015855342149734497,
    0.05055809020996094, 0.004083932377398014, 0.026945054531097412,
    -0.0505746565759182, -0.009507855400443077, -0.012497996911406517,
    0.06249537691473961, -0.04026378318667412, 0.010749109089374542,
    0.016748877242207527, -0.0235306303948164, -0.03941794112324715,
    0.027474915608763695, -0.02181144617497921, 0.0026422827504575253,
    0.005104491952806711, 0.027314607053995132, 0.019283341243863106,
    0.005245842970907688, -0.018712762743234634, -0.08618085831403732,
    0.003314188914373517, 0.008071620017290115, 0.05356570705771446,
    -0.008000597357749939, 0.006983411032706499, -0.0070550404489040375,
    -0.043323490768671036, 0.03490140289068222, 0.03810165822505951,
    0.0406375490128994, -0.0032191979698836803, 0.01489361934363842,
    -0.01609957590699196, -0.006372962612658739, 0.03360277786850929,
    -0.014810526743531227, -0.00925799086689949, -0.01885424554347992,
    0.0182492695748806, 0.009002899751067162, -0.004713123198598623,
    -0.00846288911998272, -0.012471121735870838, -0.0080558517947793,
    0.0135461101308465, 0.03335557505488396, -0.0027410900220274925,
    -0.02145615592598915, 0.01378028653562069, 0.03708091005682945,
    0.03519297018647194, 0.014239554293453693, 0.02219904027879238,
    0.0015641176141798496, 0.02624501660466194, 0.022713981568813324,
    -0.004414170514792204, 0.026919621974229813, -0.002607459668070078,
    -0.04017219692468643, -0.003570320550352335, -0.022905709221959114,
    0.030657364055514336,
    ... 924 more items
    ],
    size: 1024
    }
    ]
    Generating embeddings for documents. Standby.
    Count of documents updated: 50
1

此代码执行以下操作:

  • 连接到本地 Atlas 部署或 Atlas 集群并选择 sample_airbnb.listingsAndReviews 集合。

  • 从 Hugging Face 模型中心加载 mixedbread-ai/mxbai-embed-large-v1 模型并保存到本地。要了解更多信息,请参阅下载模型

  • 定义一个使用模型生成向量嵌入的函数。

  • 对于集合中的文档子集:

    • 从文档的 summary 字段生成嵌入内容。

    • 通过创建一个名为 embeddings 的新字段来更新文档,该字段包含嵌入内容。

    from pymongo import MongoClient
    from sentence_transformers import SentenceTransformer
    # Connect to your local Atlas deployment or Atlas Cluster
    client = MongoClient(ATLAS_CONNECTION_STRING)
    # Select the sample_airbnb.listingsAndReviews collection
    collection = client["sample_airbnb"]["listingsAndReviews"]
    # Load the embedding model (https://huggingface.co/sentence-transformers/mixedbread-ai/mxbai-embed-large-v1)
    model_path = "<model-path>"
    model = SentenceTransformer('mixedbread-ai/mxbai-embed-large-v1')
    model.save(model_path)
    model = SentenceTransformer(model_path)
    # Define function to generate embeddings
    def get_embedding(text):
    return model.encode(text).tolist()
    # Filters for only documents with a summary field and without an embeddings field
    filter = { '$and': [ { 'summary': { '$exists': True, '$ne': None } }, { 'embeddings': { '$exists': False } } ] }
    # Creates embeddings for subset of the collection
    updated_doc_count = 0
    for document in collection.find(filter).limit(50):
    text = document['summary']
    embedding = get_embedding(text)
    collection.update_one({ '_id': document['_id'] }, { "$set": { 'embeddings': embedding } }, upsert=True)
    updated_doc_count += 1
    print("Documents updated: {}".format(updated_doc_count))
    Documents updated: 50
2

这条路径应类似于: /Users/<username>/local-rag-mongodb

3

此代码可能需要几分钟才能运行。完成后,您可以使用部署的连接字符串从mongosh 连接到本地部署或应用程序,以查看向量嵌入。然后,您可以对 sample_airbnb.listingsAndReviews集合运行读取操作。

您可以通过导航到集群中的 sample_airbnb.listingsAndReviews 集合并展开文档中的字段,在 Atlas UI 中查看向量嵌入。

提示

您可以将样本数据中的嵌入转换为 BSON 向量,以便在 Atlas 中高效存储和摄取向量。要了解更多信息,请参阅如何将原生嵌入转换为 BSON 向量

要在 sample_airbnb.listingsAndReviews 集合上启用向量搜索,请创建一个 Atlas Vector Search 索引。

本教程将引导您了解如何使用支持的MongoDB驱动程序或使用Atlas CLI以编程方式创建Atlas Vector Search索引。有关创建Atlas Vector Search索引的其他方法的信息,请参阅如何为向量搜索的字段创建索引。

注意

要创建 Atlas Vector Search 索引,您必须对 Atlas 项目具有Project Data Access Admin或更高访问权限。

要使用 MongoDB Go 驱动程序 v1.16.0 或更高版本为集合创建 Atlas Vector Search 索引,请执行以下步骤:

1

创建一个名为 vector-index.go 的文件并将以下代码粘贴到该文件中:

vector-index.go
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/joho/godotenv"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
ctx := context.Background()
if err := godotenv.Load(); err != nil {
log.Println("no .env file found")
}
// Connect to your Atlas cluster
uri := os.Getenv("ATLAS_CONNECTION_STRING")
if uri == "" {
log.Fatal("set your 'ATLAS_CONNECTION_STRING' environment variable.")
}
clientOptions := options.Client().ApplyURI(uri)
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatalf("failed to connect to the server: %v", err)
}
defer func() { _ = client.Disconnect(ctx) }()
// Set the namespace
coll := client.Database("sample_airbnb").Collection("listingsAndReviews")
indexName := "vector_index"
opts := options.SearchIndexes().SetName(indexName).SetType("vectorSearch")
type vectorDefinitionField struct {
Type string `bson:"type"`
Path string `bson:"path"`
NumDimensions int `bson:"numDimensions"`
Similarity string `bson:"similarity"`
}
type vectorDefinition struct {
Fields []vectorDefinitionField `bson:"fields"`
}
indexModel := mongo.SearchIndexModel{
Definition: vectorDefinition{
Fields: []vectorDefinitionField{{
Type: "vector",
Path: "embeddings",
NumDimensions: 768,
Similarity: "cosine"}},
},
Options: opts,
}
log.Println("Creating the index.")
searchIndexName, err := coll.SearchIndexes().CreateOne(ctx, indexModel)
if err != nil {
log.Fatalf("failed to create the search index: %v", err)
}
// Await the creation of the index.
log.Println("Polling to confirm successful index creation.")
log.Println("NOTE: This may take up to a minute.")
searchIndexes := coll.SearchIndexes()
var doc bson.Raw
for doc == nil {
cursor, err := searchIndexes.List(ctx, options.SearchIndexes().SetName(searchIndexName))
if err != nil {
fmt.Errorf("failed to list search indexes: %w", err)
}
if !cursor.Next(ctx) {
break
}
name := cursor.Current.Lookup("name").StringValue()
queryable := cursor.Current.Lookup("queryable").Boolean()
if name == searchIndexName && queryable {
doc = cursor.Current
} else {
time.Sleep(5 * time.Second)
}
}
log.Println("Name of Index Created: " + searchIndexName)
}

此索引定义指定对 sample_airbnb.listingsAndReviews 集合的 vectorSearch 类型索引中的 embeddings 字段进行索引。该字段包含使用嵌入模型创建的嵌入。索引定义指定了 768 个向量维度,并使用 cosine 来衡量相似性。

2

保存文件,然后在终端中运行以下命令来执行代码:

go run vector-index.go

要使用 MongoDB Java 驱动程序 v5.2.0 或更高版本为集合创建 Atlas Vector Search 索引,请执行以下步骤:

1

创建一个名为VectorIndex.java的文件并粘贴以下代码。

此代码调用 createSearchIndexes() 辅助方法,该方法获取 MongoCollection对象并使用以下索引定义在您的集合上创建Atlas Vector Search索引:

  • embeddingsample_airbnb.listingsAndReviews 集合的 vectorSearch索引类型中为 字段编制索引。该字段包含使用嵌入模型创建的嵌入。

  • 强制执行 768 向量维度,并使用 cosine 衡量向量之间的相似性。

VectorIndex.java
import com.mongodb.MongoException;
import com.mongodb.client.ListSearchIndexesIterable;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.SearchIndexModel;
import com.mongodb.client.model.SearchIndexType;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.Collections;
import java.util.List;
public class VectorIndex {
public static void main(String[] args) {
String uri = System.getenv("ATLAS_CONNECTION_STRING");
if (uri == null || uri.isEmpty()) {
throw new IllegalStateException("ATLAS_CONNECTION_STRING env variable is not set or is empty.");
}
// establish connection and set namespace
try (MongoClient mongoClient = MongoClients.create(uri)) {
MongoDatabase database = mongoClient.getDatabase("sample_airbnb");
MongoCollection<Document> collection = database.getCollection("listingsAndReviews");
// define the index details for the index model
String indexName = "vector_index";
Bson definition = new Document(
"fields",
Collections.singletonList(
new Document("type", "vector")
.append("path", "embeddings")
.append("numDimensions", 768)
.append("similarity", "cosine")));
SearchIndexModel indexModel = new SearchIndexModel(
indexName,
definition,
SearchIndexType.vectorSearch());
// create the index using the defined model
try {
List<String> result = collection.createSearchIndexes(Collections.singletonList(indexModel));
System.out.println("Successfully created a vector index named: " + result);
} catch (Exception e) {
throw new RuntimeException(e);
}
// wait for Atlas to build the index and make it queryable
System.out.println("Polling to confirm the index has completed building.");
System.out.println("It may take up to a minute for the index to build before you can query using it.");
waitForIndexReady(collection, indexName);
} catch (MongoException me) {
throw new RuntimeException("Failed to connect to MongoDB ", me);
} catch (Exception e) {
throw new RuntimeException("Operation failed: ", e);
}
}
/**
* Polls the collection to check whether the specified index is ready to query.
*/
public static void waitForIndexReady(MongoCollection<Document> collection, String indexName) throws InterruptedException {
ListSearchIndexesIterable<Document> searchIndexes = collection.listSearchIndexes();
while (true) {
try (MongoCursor<Document> cursor = searchIndexes.iterator()) {
if (!cursor.hasNext()) {
break;
}
Document current = cursor.next();
String name = current.getString("name");
boolean queryable = current.getBoolean("queryable");
if (name.equals(indexName) && queryable) {
System.out.println(indexName + " index is ready to query");
return;
} else {
Thread.sleep(500);
}
}
}
}
}
2

保存并运行该文件。 输出类似如下所示:

Successfully created a vector index named: [vector_index]
Polling to confirm the index has completed building.
It may take up to a minute for the index to build before you can query using it.
vector_index index is ready to query

要使用MongoDB 节点驱动程序v 6.6.0或更高版本为集合创建 Atlas Vector Search 索引,请执行以下步骤:

1

创建一个名为 vector-index.js 的文件并将以下代码粘贴到该文件中:

vector-index.js
import { MongoClient } from 'mongodb';
// Connect to your Atlas deployment
const client = new MongoClient(process.env.ATLAS_CONNECTION_STRING);
async function run() {
try {
const database = client.db("sample_airbnb");
const collection = database.collection("listingsAndReviews");
// Define your Atlas Vector Search index
const index = {
name: "vector_index",
type: "vectorSearch",
definition: {
"fields": [
{
"type": "vector",
"numDimensions": 1024,
"path": "embeddings",
"similarity": "cosine"
}
]
}
}
// Call the method to create the index
const result = await collection.createSearchIndex(index);
console.log(result);
} finally {
await client.close();
}
}
run().catch(console.dir);

此索引定义指定对 sample_airbnb.listingsAndReviews 集合的 vectorSearch 类型索引中的 embeddings 字段进行索引。该字段包含使用嵌入模型创建的嵌入。索引定义指定了 1024 个向量维度,并使用 cosine 来衡量相似性。

2
  1. 保存文件,然后在终端中运行以下命令来执行代码:

    node --env-file=.env vector-index.js

要使用PyMongo驱动程序 v 4.7或更高版本为集合创建 Atlas Vector Search 索引,请执行以下步骤:

您可以使用 PyMongo 驱动程序直接从应用程序创建索引。将以下代码粘贴到您的笔记本中并运行:

from pymongo.operations import SearchIndexModel
# Create your index model, then create the search index
search_index_model = SearchIndexModel(
definition = {
"fields": [
{
"type": "vector",
"numDimensions": 1024,
"path": "embeddings",
"similarity": "cosine"
}
]
},
name = "vector_index",
type = "vectorSearch"
)
collection.create_search_index(model=search_index_model)

此索引定义指定对 sample_airbnb.listingsAndReviews 集合的 vectorSearch 类型索引中的 embeddings 字段进行索引。该字段包含使用嵌入模型创建的嵌入。索引定义指定了 1024 个向量维度,并使用 cosine 来衡量相似性。

要使用Atlas CLI创建Atlas Vector Search索引,请执行以下步骤:

1

创建一个名为 vector-index.json 的文件,并将以下索引定义粘贴到该文件中:

vector-index.json
{
"database": "sample_airbnb",
"collectionName": "listingsAndReviews",
"type": "vectorSearch",
"name": "vector_index",
"fields": [
{
"type": "vector",
"path": "embeddings",
"numDimensions": 768,
"similarity": "cosine"
}
]
}

该索引定义指定以下内容:

2

保存文件到您的项目目录,然后在终端中运行以下命令,将 <path-to-file> 替换为您创建的 vector-index.json 文件的路径。

atlas deployments search indexes create --file <path-to-file>

示例,您的路径可能类似于:/Users/<username>/local-rag-mongodb/vector-index.json

1

创建一个名为 vector-index.json 的文件,并将以下索引定义粘贴到该文件中:

vector-index.json
{
"database": "sample_airbnb",
"collectionName": "listingsAndReviews",
"type": "vectorSearch",
"name": "vector_index",
"fields": [
{
"type": "vector",
"path": "embeddings",
"numDimensions": 768,
"similarity": "cosine"
}
]
}

该索引定义指定以下内容:

2

保存文件到您的项目目录,然后在终端中运行以下命令,将 <path-to-file> 替换为您创建的 vector-index.json 文件的路径。

atlas deployments search indexes create --file <path-to-file>

示例,您的路径可能类似于:/Users/<username>/local-rag-mongodb/vector-index.json

1

创建一个名为 vector-index.json 的文件,并将以下索引定义粘贴到该文件中。

此索引定义指定对 sample_airbnb.listingsAndReviews 集合的 vectorSearch 类型索引中的 embeddings 字段进行索引。该字段包含使用嵌入模型创建的嵌入。索引定义指定了 1024 个向量维度,并使用 cosine 来衡量相似性。

{
"database": "sample_airbnb",
"collectionName": "listingsAndReviews",
"type": "vectorSearch",
"name": "vector_index",
"fields": [
{
"type": "vector",
"path": "embeddings",
"numDimensions": 1024,
"similarity": "cosine"
}
]
}
2

保存文件到您的项目目录,然后在终端中运行以下命令,将 <path-to-file> 替换为您创建的 vector-index.json 文件的路径。

atlas deployments search indexes create --file <path-to-file>

该路径应类似于:/Users/<username>/local-rag-mongodb/vector-index.json

1

创建一个名为 vector-index.json 的文件,并将以下索引定义粘贴到该文件中。

此索引定义指定对 sample_airbnb.listingsAndReviews 集合的 vectorSearch 类型索引中的 embeddings 字段进行索引。该字段包含使用嵌入模型创建的嵌入。索引定义指定了 1024 个向量维度,并使用 cosine 来衡量相似性。

{
"database": "sample_airbnb",
"collectionName": "listingsAndReviews",
"type": "vectorSearch",
"name": "vector_index",
"fields": [
{
"type": "vector",
"path": "embeddings",
"numDimensions": 1024,
"similarity": "cosine"
}
]
}
2

保存文件到您的项目目录,然后在终端中运行以下命令,将 <path-to-file> 替换为您创建的 vector-index.json 文件的路径。

atlas deployments search indexes create --file <path-to-file>

该路径应类似于:/Users/<username>/local-rag-mongodb/vector-index.json

本部分演示了一个示例RAG实施,您可以使用Atlas Vector Search和 Ollama 在本地运行该实施。

1
  1. 导航至common目录。

    cd common
  2. 创建一个名为 retrieve-documents.go 的文件,并将以下代码粘贴到其中:

    retrieve-documents.go
    package common
    import (
    "context"
    "log"
    "os"
    "github.com/joho/godotenv"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    )
    type Document struct {
    Summary string `bson:"summary"`
    ListingURL string `bson:"listing_url"`
    Score float64 `bson:"score"`
    }
    func RetrieveDocuments(query string) []Document {
    ctx := context.Background()
    if err := godotenv.Load(); err != nil {
    log.Println("no .env file found")
    }
    // Connect to your Atlas cluster
    uri := os.Getenv("ATLAS_CONNECTION_STRING")
    if uri == "" {
    log.Fatal("set your 'ATLAS_CONNECTION_STRING' environment variable.")
    }
    clientOptions := options.Client().ApplyURI(uri)
    client, err := mongo.Connect(ctx, clientOptions)
    if err != nil {
    log.Fatalf("failed to connect to the server: %v", err)
    }
    defer func() { _ = client.Disconnect(ctx) }()
    // Set the namespace
    coll := client.Database("sample_airbnb").Collection("listingsAndReviews")
    var array []string
    array = append(array, query)
    queryEmbedding := GetEmbeddings(array)
    vectorSearchStage := bson.D{
    {"$vectorSearch", bson.D{
    {"index", "vector_index"},
    {"path", "embeddings"},
    {"queryVector", queryEmbedding[0]},
    {"exact", true},
    {"limit", 5},
    }}}
    projectStage := bson.D{
    {"$project", bson.D{
    {"_id", 0},
    {"summary", 1},
    {"listing_url", 1},
    {"score", bson.D{{"$meta", "vectorSearchScore"}}},
    }}}
    cursor, err := coll.Aggregate(ctx, mongo.Pipeline{vectorSearchStage, projectStage})
    if err != nil {
    log.Fatalf("failed to retrieve data from the server: %v", err)
    }
    var results []Document
    if err = cursor.All(ctx, &results); err != nil {
    log.Fatalf("failed to unmarshal retrieved docs to model objects: %v", err)
    }
    return results
    }

    此代码对您的本地 Atlas 部署或 Atlas 集群执行向量查询。

  3. 运行测试查询,确认您获得了预期结果。返回到项目根目录。

    cd ../
  4. 创建一个名为 test-query.go 的新文件,并粘贴以下代码:

    test-query.go
    package main
    import (
    "fmt"
    "local-rag-mongodb/common" // Module that contains the RetrieveDocuments function
    "log"
    "strings"
    )
    func main() {
    query := "beach house"
    matchingDocuments := common.RetrieveDocuments(query)
    if matchingDocuments == nil {
    log.Fatal("No documents matched the query.\n")
    }
    var textDocuments strings.Builder
    for _, doc := range matchingDocuments {
    // Print the contents of the matching documents for verification
    fmt.Printf("Summary: %v\n", doc.Summary)
    fmt.Printf("Listing URL: %v\n", doc.ListingURL)
    fmt.Printf("Score: %v\n", doc.Score)
    // Build a single text string to use as the context for the QA
    textDocuments.WriteString("Summary: ")
    textDocuments.WriteString(doc.Summary)
    textDocuments.WriteString("\n")
    textDocuments.WriteString("Listing URL: ")
    textDocuments.WriteString(doc.ListingURL)
    textDocuments.WriteString("\n")
    }
    fmt.Printf("\nThe constructed context for the QA follows:\n\n")
    fmt.Printf(textDocuments.String())
    }
  5. 运行以下代码以执行查询:

    go run test-query.go
    Summary: "Lani Beach House" Aloha - Please do not reserve until reading about the State Tax in "Other Things to Note" section. Please do not reserve unless you agree to pay taxes to Hawaii Beach Homes directly. If you have questions, please inquire before booking. The home has been completely redecorated in a luxurious island style: vaulted ceilings, skylights, granite counter tops, stainless steel appliances and a gourmet kitchen are just some of the the features. All bedrooms have ocean views
    Listing URL: https://www.airbnb.com/rooms/11553333
    Score: 0.85715651512146
    Summary: This peaceful house in North Bondi is 300m to the beach and a minute's walk to cafes and bars. With 3 bedrooms, (can sleep up to 8) it is perfect for families, friends and pets. The kitchen was recently renovated and a new lounge and chairs installed. The house has a peaceful, airy, laidback vibe - a perfect beach retreat. Longer-term bookings encouraged. Parking for one car. A parking permit for a second car can also be obtained on request.
    Listing URL: https://www.airbnb.com/rooms/10423504
    Score: 0.8425835371017456
    Summary: There are 2 bedrooms and a living room in the house. 1 Bathroom. 1 Kitchen. Friendly neighbourhood. Close to sea side and Historical places.
    Listing URL: https://www.airbnb.com/rooms/10488837
    Score: 0.8403302431106567
    Summary: Ocean Living! Secluded Secret Beach! Less than 20 steps to the Ocean! This spacious 4 Bedroom and 4 Bath house has all you need for your family or group. Perfect for Family Vacations and executive retreats. We are in a gated beachfront estate, with lots of space for your activities.
    Listing URL: https://www.airbnb.com/rooms/10317142
    Score: 0.8367050886154175
    Summary: This is a gorgeous home just off the main rd, with lots of sun and new amenities. room has own entrance with small deck, close proximity to the beach , bus to the junction , around the corner form all the cafes, bars and restaurants (2 mins).
    Listing URL: https://www.airbnb.com/rooms/11719579
    Score: 0.8262639045715332
    The constructed context for the QA follows:
    Summary: "Lani Beach House" Aloha - Please do not reserve until reading about the State Tax in "Other Things to Note" section. Please do not reserve unless you agree to pay taxes to Hawaii Beach Homes directly. If you have questions, please inquire before booking. The home has been completely redecorated in a luxurious island style: vaulted ceilings, skylights, granite counter tops, stainless steel appliances and a gourmet kitchen are just some of the the features. All bedrooms have ocean views
    Listing URL: https://www.airbnb.com/rooms/11553333
    Summary: This peaceful house in North Bondi is 300m to the beach and a minute's walk to cafes and bars. With 3 bedrooms, (can sleep up to 8) it is perfect for families, friends and pets. The kitchen was recently renovated and a new lounge and chairs installed. The house has a peaceful, airy, laidback vibe - a perfect beach retreat. Longer-term bookings encouraged. Parking for one car. A parking permit for a second car can also be obtained on request.
    Listing URL: https://www.airbnb.com/rooms/10423504
    Summary: There are 2 bedrooms and a living room in the house. 1 Bathroom. 1 Kitchen. Friendly neighbourhood. Close to sea side and Historical places.
    Listing URL: https://www.airbnb.com/rooms/10488837
    Summary: Ocean Living! Secluded Secret Beach! Less than 20 steps to the Ocean! This spacious 4 Bedroom and 4 Bath house has all you need for your family or group. Perfect for Family Vacations and executive retreats. We are in a gated beachfront estate, with lots of space for your activities.
    Listing URL: https://www.airbnb.com/rooms/10317142
    Summary: This is a gorgeous home just off the main rd, with lots of sun and new amenities. room has own entrance with small deck, close proximity to the beach , bus to the junction , around the corner form all the cafes, bars and restaurants (2 mins).
    Listing URL: https://www.airbnb.com/rooms/11719579
2

运行以下命令以拉取生成模型:

ollama pull mistral
3

创建一个名为 local-llm.go 的文件,并粘贴以下代码:

local-llm.go
package main
import (
"context"
"local-rag-mongodb/common" // Module that contains the RetrieveDocuments function
"log"
"strings"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/ollama"
"github.com/tmc/langchaingo/prompts"
)
func main() {
// Retrieve documents from the collection that match the query
const query = "beach house"
matchingDocuments := common.RetrieveDocuments(query)
if matchingDocuments == nil {
log.Fatalf("no documents matched the query %q", query)
}
// Generate the text string from the matching documents to pass to the
// LLM as context to answer the question
var textDocuments strings.Builder
for _, doc := range matchingDocuments {
textDocuments.WriteString("Summary: ")
textDocuments.WriteString(doc.Summary)
textDocuments.WriteString("\n")
textDocuments.WriteString("Listing URL: ")
textDocuments.WriteString(doc.ListingURL)
textDocuments.WriteString("\n")
}
// Have the LLM answer the question using the provided context
llm, err := ollama.New(ollama.WithModel("mistral"))
if err != nil {
log.Fatalf("failed to initialize the Ollama Mistral model client: %v", err)
}
const question = `Can you recommend me a few AirBnBs that are beach houses?
Include a link to the listings.`
template := prompts.NewPromptTemplate(
`Use the following pieces of context to answer the question at the end.
Context: {{.context}}
Question: {{.question}}`,
[]string{"context", "question"},
)
prompt, err := template.Format(map[string]any{
"context": textDocuments.String(),
"question": question,
})
ctx := context.Background()
completion, err := llms.GenerateFromSinglePrompt(ctx, llm, prompt)
if err != nil {
log.Fatalf("failed to generate a response from the given prompt: %q", prompt)
}
log.Println("Response: ", completion)
}

此代码执行以下操作:

  • 为查询字符串创建嵌入。

  • 查询相关文档。

  • 提示 LLM 并返回响应。生成的响应可能会有所不同。

运行以下代码,完成 RAG 实现:

go run local-llm.go
2024/10/09 10:34:02 Response: Based on the context provided, here are some Airbnb listings for beach houses that you might find interesting:
1. Lani Beach House (Hawaii) - [Link](https://www.airbnb.com/rooms/11553333)
2. Peaceful North Bondi House (Australia) - [Link](https://www.airbnb.com/rooms/10423504)
3. Ocean Living! Secluded Secret Beach! (Florida, USA) - [Link](https://www.airbnb.com/rooms/10317142)
4. Gorgeous Home just off the main road (California, USA) - [Link](https://www.airbnb.com/rooms/11719579)

本部分演示了一个示例RAG实施,您可以使用Atlas Vector Search和 Ollama 在本地运行该实施。

1

创建一个名为 LocalLLM.java 的新文件并粘贴以下代码。

此代码使用 getEmbeddingretrieveDocuments 方法以及 Ollama chatmodel 来执行以下操作:

  1. 连接到本地Atlas部署或Atlas 集群

  2. 使用您之前定义的 getEmbedding 方法为查询字符串生成嵌入。

  3. 使用 retrieveDocuments 方法查询集合中的相关文档。

    我们的查询包括一个带有投影阶段的聚合管道,仅返回 listing_urlsummary 和向量 score 字段。您可以修改或删除此管道,以更好地适应您的数据和使用案例。

  4. 使用 createPrompt 方法将问题与检索到的文档连接起来,从而创建上下文。

  5. 将创建的提示提供给您之前定义的 LLM 以生成响应。chatmodel

  6. 将问题和生成的响应打印到控制台。

    注意

    出于演示目的,我们还打印了带有上下文信息的填写提示。您应该在生产环境中删除此行。

LocalLLM.java
import com.mongodb.MongoException;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.search.FieldSearchPath;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.ollama.OllamaChatModel;
import org.bson.BsonArray;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.mongodb.client.model.Aggregates.project;
import static com.mongodb.client.model.Aggregates.vectorSearch;
import static com.mongodb.client.model.Projections.exclude;
import static com.mongodb.client.model.Projections.fields;
import static com.mongodb.client.model.Projections.include;
import static com.mongodb.client.model.Projections.metaVectorSearchScore;
import static com.mongodb.client.model.search.SearchPath.fieldPath;
import static com.mongodb.client.model.search.VectorSearchOptions.exactVectorSearchOptions;
import static java.util.Arrays.asList;
public class LocalLLM {
// User input: the question to answer
static String question = "Can you recommend me a few AirBnBs that are beach houses? Include a link to the listings.";
public static void main(String[] args) {
String uri = System.getenv("ATLAS_CONNECTION_STRING");
if (uri == null || uri.isEmpty()) {
throw new IllegalStateException("ATLAS_CONNECTION_STRING env variable is not set or is empty.");
}
// establish connection and set namespace
try (MongoClient mongoClient = MongoClients.create(uri)) {
MongoDatabase database = mongoClient.getDatabase("sample_airbnb");
MongoCollection<Document> collection = database.getCollection("listingsAndReviews");
// generate a response to the user question
System.out.println("Question: " + question);
try {
createPrompt(question, collection);
} catch (Exception e) {
throw new RuntimeException("An error occurred while generating the response: ", e);
}
} catch (MongoException me) {
throw new RuntimeException("Failed to connect to MongoDB ", me);
} catch (Exception e) {
throw new RuntimeException("Operation failed: ", e);
}
}
/**
* Returns a list of documents from the specified MongoDB collection that
* match the user's question.
* NOTE: Update or omit the projection stage to change the desired fields in the response
*/
public static List<Document> retrieveDocuments(String question, MongoCollection<Document> collection) {
try {
// generate the query embedding to use in the vector search
BsonArray queryEmbeddingBsonArray = OllamaModels.getEmbedding(question);
List<Double> queryEmbedding = new ArrayList<>();
for (BsonValue value : queryEmbeddingBsonArray.stream().toList()) {
queryEmbedding.add(value.asDouble().getValue());
}
// define the pipeline stages for the vector search index
String indexName = "vector_index";
FieldSearchPath fieldSearchPath = fieldPath("embeddings");
int limit = 5;
List<Bson> pipeline = asList(
vectorSearch(
fieldSearchPath,
queryEmbedding,
indexName,
limit,
exactVectorSearchOptions()),
project(
fields(
exclude("_id"),
include("listing_url"),
include("summary"),
metaVectorSearchScore("score"))));
// run the query and return the matching documents
List<Document> matchingDocuments = new ArrayList<>();
collection.aggregate(pipeline).forEach(matchingDocuments::add);
return matchingDocuments;
} catch (Exception e) {
System.err.println("Error occurred while retrieving documents: " + e.getMessage());
return new ArrayList<>();
}
}
/**
* Creates a templated prompt using the question and retrieved documents, then generates
* a response using the local Ollama chat model.
*/
public static void createPrompt(String question, MongoCollection<Document> collection) {
// Retrieve documents matching the user's question
List<Document> retrievedDocuments = retrieveDocuments(question, collection);
if (retrievedDocuments.isEmpty()) {
System.out.println("No relevant documents found. Unable to generate a response.");
return;
} else
System.out.println("Generating a response from the retrieved documents. This may take a few moments.");
// Create a prompt template
OllamaChatModel ollamaChatModel = OllamaModels.getChatModel();
PromptTemplate promptBuilder = PromptTemplate.from("""
Use the following pieces of context to answer the question at the end:
{{information}}
---------------
{{question}}
""");
// build the information string from the retrieved documents
StringBuilder informationBuilder = new StringBuilder();
for (int i = 0; i < retrievedDocuments.size(); i++) {
Document doc = retrievedDocuments.get(i);
String listingUrl = doc.getString("listing_url");
String summary = doc.getString("summary");
informationBuilder.append("Listing URL: ").append(listingUrl)
.append("\nSummary: ").append(summary)
.append("\n\n");
}
String information = informationBuilder.toString();
Map<String, Object> variables = new HashMap<>();
variables.put("question", question);
variables.put("information", information);
// generate and output the response from the chat model
Prompt prompt = promptBuilder.apply(variables);
AiMessage response = ollamaChatModel.generate(prompt.toUserMessage()).content();
System.out.println("Answer: " + response.text());
// display the filled-in prompt and context information
// NOTE: included for demonstration purposes only
System.out.println("______________________");
System.out.println("Final Prompt Sent to LLM:");
System.out.println(prompt.text());
System.out.println("______________________");
System.out.println("Number of documents in context: " + retrievedDocuments.size());
}
}
2

运行以下命令以拉取生成模型:

ollama pull mistral
3

保存并运行文件以完成 RAG实施。输出如下所示,但生成的响应可能会有所不同:

响应输出
Question: Can you recommend me a few AirBnBs that are beach houses? Include a link to the listings.
Generating a response from the retrieved documents. This may take a few moments.
Answer: Based on the context provided, here are some beach house Airbnb listings that might suit your needs:
1. Lani Beach House - Aloha: This luxurious beach house offers ocean views from all bedrooms and features vaulted ceilings, skylights, granite countertops, stainless steel appliances, and a gourmet kitchen. You can find it at this link: https://www.airbnb.com/rooms/11553333
2. Ocean Living! Secluded Secret Beach!: This spacious 4-bedroom, 4-bath beach house is perfect for families or groups and is less than 20 steps from the ocean. It's located in a gated beachfront estate with lots of space for activities. You can find it at this link: https://www.airbnb.com/rooms/10317142
3. A beautiful and comfortable 1-Bedroom Condo in Makaha Valley: This condo offers stunning ocean and mountain views, a full kitchen, large bathroom, and is suited for longer stays. The famous Makaha Surfing Beach is not even a mile away. You can find it at this link: https://www.airbnb.com/rooms/10266175
4. There are 2 bedrooms and a living room in the house: This listing does not provide much information about the beach, but it mentions that the house is close to the sea side and historical places. You can find it at this link: https://www.airbnb.com/rooms/10488837
5. The Apartment on Copacabana beach block: This apartment is well-located, a 5-minute walk from Ipanema beach, and offers all the amenities of home, including a kitchen, washing machine, and several utensils for use. You can find it at this link: https://www.airbnb.com/rooms/10038496
______________________
Final Prompt Sent to LLM:
Use the following pieces of context to answer the question at the end:
Listing URL: https://www.airbnb.com/rooms/11553333
Summary: "Lani Beach House" Aloha - Please do not reserve until reading about the State Tax in "Other Things to Note" section. Please do not reserve unless you agree to pay taxes to Hawaii Beach Homes directly. If you have questions, please inquire before booking. The home has been completely redecorated in a luxurious island style: vaulted ceilings, skylights, granite counter tops, stainless steel appliances and a gourmet kitchen are just some of the the features. All bedrooms have ocean views
Listing URL: https://www.airbnb.com/rooms/10317142
Summary: Ocean Living! Secluded Secret Beach! Less than 20 steps to the Ocean! This spacious 4 Bedroom and 4 Bath house has all you need for your family or group. Perfect for Family Vacations and executive retreats. We are in a gated beachfront estate, with lots of space for your activities.
Listing URL: https://www.airbnb.com/rooms/10266175
Summary: A beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views All the amenities of home, suited for longer stays. Full kitchen & large bathroom. Several gas BBQ's for all guests to use & a large heated pool surrounded by reclining chairs to sunbathe. The Ocean you see in the pictures is not even a mile away, known as the famous Makaha Surfing Beach. Golfing, hiking,snorkeling paddle boarding, surfing are all just minutes from the front door.
Listing URL: https://www.airbnb.com/rooms/10488837
Summary: There are 2 bedrooms and a living room in the house. 1 Bathroom. 1 Kitchen. Friendly neighbourhood. Close to sea side and Historical places.
Listing URL: https://www.airbnb.com/rooms/10038496
Summary: The Apartment has a living room, toilet, bedroom (suite) and American kitchen. Well located, on the Copacabana beach block a 05 Min. walk from Ipanema beach (Arpoador). Internet wifi, cable tv, air conditioning in the bedroom, ceiling fans in the bedroom and living room, kitchen with microwave, cooker, Blender, dishes, cutlery and service area with fridge, washing machine, clothesline for drying clothes and closet with several utensils for use. The property boasts 45 m2.
---------------
Can you recommend me a few AirBnBs that are beach houses? Include a link to the listings.
______________________
Number of documents in context: 5

本部分演示了一个示例RAG实施,您可以使用Atlas Vector Search和 GPT4 All 在本地运行该实施。

1

创建一个名为 retrieve-documents.js 的文件,并将以下代码粘贴到其中:

retrieve-documents.js
import { MongoClient } from 'mongodb';
import { getEmbeddings } from './get-embeddings.js';
// Function to get the results of a vector query
export async function getQueryResults(query) {
// Connect to your Atlas cluster
const client = new MongoClient(process.env.ATLAS_CONNECTION_STRING);
try {
// Get embeddings for a query
const queryEmbeddings = await getEmbeddings(query);
await client.connect();
const db = client.db("sample_airbnb");
const collection = db.collection("listingsAndReviews");
const pipeline = [
{
$vectorSearch: {
index: "vector_index",
queryVector: queryEmbeddings,
path: "embeddings",
exact: true,
limit: 5
}
}, {
$project: {
_id: 0,
summary: 1,
listing_url: 1,
score: {
$meta: "vectorSearchScore"
}
}
}
];
// Retrieve documents from Atlas using this Vector Search query
const result = collection.aggregate(pipeline);
const arrayOfQueryDocs = [];
for await (const doc of result) {
arrayOfQueryDocs.push(doc);
}
return arrayOfQueryDocs;
} catch (err) {
console.log(err.stack);
}
finally {
await client.close();
}
}

此代码对您的本地 Atlas 部署或 Atlas 集群执行向量查询。

运行测试查询,确认您获得了预期结果。创建一个名为 test-query.js 的新文件,并在其中粘贴以下代码:

运行以下代码以执行查询:

node --env-file=.env test-query.js
{
listing_url: 'https://www.airbnb.com/rooms/10317142',
summary: 'Ocean Living! Secluded Secret Beach! Less than 20 steps to the Ocean! This spacious 4 Bedroom and 4 Bath house has all you need for your family or group. Perfect for Family Vacations and executive retreats. We are in a gated beachfront estate, with lots of space for your activities.',
score: 0.8703486323356628
}
{
listing_url: 'https://www.airbnb.com/rooms/10488837',
summary: 'There are 2 bedrooms and a living room in the house. 1 Bathroom. 1 Kitchen. Friendly neighbourhood. Close to sea side and Historical places.',
score: 0.861828088760376
}
{
listing_url: 'https://www.airbnb.com/rooms/11719579',
summary: 'This is a gorgeous home just off the main rd, with lots of sun and new amenities. room has own entrance with small deck, close proximity to the beach , bus to the junction , around the corner form all the cafes, bars and restaurants (2 mins).',
score: 0.8616757392883301
}
{
listing_url: 'https://www.airbnb.com/rooms/12657285',
summary: 'This favourite home offers a huge balcony, lots of space, easy life, all the comfort you need and a fantastic location! The beach is only 3 minutes away. Metro is 2 blocks away (starting august 2016).',
score: 0.8583258986473083
}
{
listing_url: 'https://www.airbnb.com/rooms/10985735',
summary: '5 minutes to seaside where you can swim, and 5 minutes to the woods, this two floors single house contains a cultivated garden with fruit trees, two large bedrooms and a big living room with a large sea view.',
score: 0.8573609590530396
}
2
  1. 点击以下按钮从 GPT4All 下载 Mistral 7B 模型。要探索其他模型,请参阅 GPT4All 网站。

    立即下载
  2. 将此模型移动到 local-rag-mongodb 项目目录中。

  3. 在您的项目目录中,下载包含模型信息的文件。

    curl -L https://gpt4all.io/models/models3.json -o ./models3.json
3

创建一个名为 local-llm.js 的文件,并粘贴以下代码:

local-llm.js
import { loadModel, createCompletionStream } from "gpt4all";
import { getQueryResults } from './retrieve-documents.js';
async function run() {
try {
const query = "beach house";
const documents = await getQueryResults(query);
let textDocuments = "";
documents.forEach(doc => {
const summary = doc.summary;
const link = doc.listing_url;
const string = `Summary: ${summary} Link: ${link}. \n`
textDocuments += string;
});
const model = await loadModel(
"mistral-7b-openorca.gguf2.Q4_0.gguf", {
verbose: true,
allowDownload: false,
modelConfigFile: "./models3.json"
}
);
const question = "Can you recommend me a few AirBnBs that are beach houses? Include a link to the listings.";
const prompt = `Use the following pieces of context to answer the question at the end.
{${textDocuments}}
Question: {${question}}`;
process.stdout.write("Output: ");
const stream = createCompletionStream(model, prompt);
stream.tokens.on("data", (data) => {
process.stdout.write(data);
});
//wait till stream finishes.
await stream.result;
process.stdout.write("\n");
model.dispose();
console.log("\n Source documents: \n");
console.log(textDocuments);
} catch (err) {
console.log(err.stack);
}
}
run().catch(console.dir);

此代码执行以下操作:

  • 为查询字符串创建嵌入。

  • 查询相关文档。

  • 提示 LLM 并返回响应。生成的响应可能会有所不同。

运行以下代码,完成 RAG 实现:

node --env-file=.env local-llm.js
Found mistral-7b-openorca.gguf2.Q4_0.gguf at /Users/dachary.carey/.cache/gpt4all/mistral-7b-openorca.gguf2.Q4_0.gguf
Creating LLModel: {
llmOptions: {
model_name: 'mistral-7b-openorca.gguf2.Q4_0.gguf',
model_path: '/Users/dachary.carey/.cache/gpt4all',
library_path: '/Users/dachary.carey/temp/local-rag-mongodb/node_modules/gpt4all/runtimes/darwin/native;/Users/dachary.carey/temp/local-rag-mongodb',
device: 'cpu',
nCtx: 2048,
ngl: 100
},
modelConfig: {
systemPrompt: '<|im_start|>system\n' +
'You are MistralOrca, a large language model trained by Alignment Lab AI.\n' +
'<|im_end|>',
promptTemplate: '<|im_start|>user\n%1<|im_end|>\n<|im_start|>assistant\n%2<|im_end|>\n',
order: 'e',
md5sum: 'f692417a22405d80573ac10cb0cd6c6a',
name: 'Mistral OpenOrca',
filename: 'mistral-7b-openorca.gguf2.Q4_0.gguf',
filesize: '4108928128',
requires: '2.7.1',
ramrequired: '8',
parameters: '7 billion',
quant: 'q4_0',
type: 'Mistral',
description: '<strong>Strong overall fast chat model</strong><br><ul><li>Fast responses</li><li>Chat based model</li><li>Trained by Mistral AI<li>Finetuned on OpenOrca dataset curated via <a href="https://atlas.nomic.ai/">Nomic Atlas</a><li>Licensed for commercial use</ul>',
url: 'https://gpt4all.io/models/gguf/mistral-7b-openorca.gguf2.Q4_0.gguf',
path: '/Users/dachary.carey/.cache/gpt4all/mistral-7b-openorca.gguf2.Q4_0.gguf'
}
}
Output: Yes, here are a few AirBnB beach houses with links to the listings:
1. Ocean Living! Secluded Secret Beach! Less than 20 steps to the Ocean! - https://www.airbnb.com/rooms/10317142
2. 2 Bedrooms and a living room in the house. 1 Bathroom. 1 Kitchen. Friendly neighbourhood. Close to sea side and Historical places - https://www.airbnb.com/rooms/10488837
3. Gorgeous home just off the main rd, with lots of sun and new amenities. Room has own entrance with small deck, close proximity to the beach - https://www.airbnb.com/rooms/11719579
4. This favourite home offers a huge balcony, lots of space, easy life, all the comfort you need and a fantastic location! The beach is only 3 minutes away. Metro is 2 blocks away (starting august 2016) - https://www.airbnb.com/rooms/12657285
5. 5 minutes to seaside where you can swim, and 5 minutes to the woods, this two floors single house contains a cultivated garden with fruit trees, two large bedrooms and a big living room with a large sea view - https://www.airbnb.com/rooms/10985735
Source documents:
Summary: Ocean Living! Secluded Secret Beach! Less than 20 steps to the Ocean! This spacious 4 Bedroom and 4 Bath house has all you need for your family or group. Perfect for Family Vacations and executive retreats. We are in a gated beachfront estate, with lots of space for your activities. Link: https://www.airbnb.com/rooms/10317142.
Summary: There are 2 bedrooms and a living room in the house. 1 Bathroom. 1 Kitchen. Friendly neighbourhood. Close to sea side and Historical places. Link: https://www.airbnb.com/rooms/10488837.
Summary: This is a gorgeous home just off the main rd, with lots of sun and new amenities. room has own entrance with small deck, close proximity to the beach , bus to the junction , around the corner form all the cafes, bars and restaurants (2 mins). Link: https://www.airbnb.com/rooms/11719579.
Summary: This favourite home offers a huge balcony, lots of space, easy life, all the comfort you need and a fantastic location! The beach is only 3 minutes away. Metro is 2 blocks away (starting august 2016). Link: https://www.airbnb.com/rooms/12657285.
Summary: 5 minutes to seaside where you can swim, and 5 minutes to the woods, this two floors single house contains a cultivated garden with fruit trees, two large bedrooms and a big living room with a large sea view. Link: https://www.airbnb.com/rooms/10985735.

本部分演示了一个示例RAG实施,您可以使用Atlas Vector Search和 GPT4 All 在本地运行该实施。

在笔记本中,运行以下代码片段:

1

在此步骤中,您将创建一个名为 get_query_results 的检索函数,用于运行示例向量搜索查询。它使用 get_embedding 函数从搜索查询创建嵌入。然后,它运行查询以返回语义相似的文档。

要了解更多信息,请参阅运行向量搜索查询。

# Function to get the results of a vector search query
def get_query_results(query):
query_embedding = get_embedding(query)
pipeline = [
{
"$vectorSearch": {
"index": "vector_index",
"queryVector": query_embedding,
"path": "embeddings",
"exact": True,
"limit": 5
}
}, {
"$project": {
"_id": 0,
"summary": 1,
"listing_url": 1,
"score": {
"$meta": "vectorSearchScore"
}
}
}
]
results = collection.aggregate(pipeline)
array_of_results = []
for doc in results:
array_of_results.append(doc)
return array_of_results

要检查该函数是否返回相关文档,请运行以下代码来查询搜索词 beach house

注意

您的输出可能会有所不同,因为环境差异可能会引起嵌入的细微变化。

import pprint
pprint.pprint(get_query_results("beach house"))
[{'listing_url': 'https://www.airbnb.com/rooms/10317142',
'score': 0.84868323802948,
'summary': 'Ocean Living! Secluded Secret Beach! Less than 20 steps to the '
'Ocean! This spacious 4 Bedroom and 4 Bath house has all you need '
'for your family or group. Perfect for Family Vacations and '
'executive retreats. We are in a gated beachfront estate, with '
'lots of space for your activities.'},
{'listing_url': 'https://www.airbnb.com/rooms/10488837',
'score': 0.8457906246185303,
'summary': 'There are 2 bedrooms and a living room in the house. 1 Bathroom. '
'1 Kitchen. Friendly neighbourhood. Close to sea side and '
'Historical places.'},
{'listing_url': 'https://www.airbnb.com/rooms/10423504',
'score': 0.830578088760376,
'summary': 'This peaceful house in North Bondi is 300m to the beach and a '
"minute's walk to cafes and bars. With 3 bedrooms, (can sleep up "
'to 8) it is perfect for families, friends and pets. The kitchen '
'was recently renovated and a new lounge and chairs installed. '
'The house has a peaceful, airy, laidback vibe - a perfect beach '
'retreat. Longer-term bookings encouraged. Parking for one car. A '
'parking permit for a second car can also be obtained on '
'request.'},
{'listing_url': 'https://www.airbnb.com/rooms/10548991',
'score': 0.8174338340759277,
'summary': 'Newly furnished two story home. The upstairs features a full '
...
{'listing_url': 'https://www.airbnb.com/rooms/10186755',
'score': 0.8083034157752991,
'summary': 'Near to underground metro station. Walking distance to seaside. '
'2 floors 1 entry. Husband, wife, girl and boy is living.'}]
2
  1. 点击以下按钮从 GPT4All 下载 Mistral 7B 模型。要探索其他模型,请参阅 GPT4All 网站。

    立即下载
  2. 将此模型移动到 local-rag-mongodb 项目目录中。

  3. 在您的笔记本中,运行以下代码以加载本地 LLM

    from gpt4all import GPT4All
    local_llm_path = "./mistral-7b-openorca.gguf2.Q4_0.gguf"
    local_llm = GPT4All(local_llm_path)
3

运行以下代码以完成 RAG 的实现。此代码执行以下操作:

  • 使用刚刚定义的函数在集合中查询相关文档。

  • 使用检索到的文档作为上下文提示 LLM。产生的响应可能会有所不同。

question = "Can you recommend a few AirBnBs that are beach houses? Include a link to the listing."
documents = get_query_results(question)
text_documents = ""
for doc in documents:
summary = doc.get("summary", "")
link = doc.get("listing_url", "")
string = f"Summary: {summary} Link: {link}. \n"
text_documents += string
prompt = f"""Use the following pieces of context to answer the question at the end.
{text_documents}
Question: {question}
"""
response = local_llm.generate(prompt)
cleaned_response = response.replace('\\n', '\n')
print(cleaned_response)
Answer: Yes, I can recommend a few AirBnB listings that are beach houses. Here they are with their respective links:
1. Ocean Living! Secluded Secret Beach! Less than 20 steps to the Ocean! (https://www.airbnb.com/rooms/10317142)
2. Beautiful and comfortable 1 Bedroom Air Conditioned Condo in Makaha Valley - stunning Ocean & Mountain views (https://www.airbnb.com/rooms/10266175)
3. Peaceful house in North Bondi, close to the beach and cafes (https://www.airbnb.com/rooms/10423504)

后退

混合搜索