Docs Menu
Docs Home
/
MongoDB Atlas
/ /

Atlas Vector Search を使用したローカル RAG 実装の構築

項目一覧

  • バックグラウンド
  • 前提条件
  • ローカル配置または Atlas クラスターの作成
  • 環境を設定する
  • ローカルモデルによる埋め込みの生成
  • Atlas Vector Search インデックスの作成
  • ローカル LM による質問への回答

このチュートリアルでは、 APIキーやクレジットを必要とせずに、検索拡張生成(RAG)をローカルに実装する方法を説明します。 RGRAG Atlas Vector Searchの詳細については、「 を使用した 検索拡張生成(RAG ) 」を してください。

具体的には、次のアクションを実行します。

  1. ローカル Atlas 配置を作成するか、クラウドにクラスターを配置します 。

  2. 環境を設定します。

  3. ローカル埋め込みモデルを使用してベクトル埋め込みを生成します。

  4. データに Atlas Vector Search インデックスを作成します。

  5. ローカル LLM を使用して、データに関する質問に答えます。


➤ [言語の選択] ドロップダウン メニューを使用して、このページの例の言語を設定します。


Tip

このチュートリアルの実行可能なバージョンを Pythonノートとして操作します。

このチュートリアルを完了するには、 Atlas CLIを使用してローカル Atlas 配置を作成するか、クラウドにクラスターを配置します。Atlas CLI は MongoDB Atlas のコマンドラインインターフェイスであり、Atlas CLI を使用してターミナルから Atlas とやり取りし、ローカル Atlas 配置の作成を含むさまざまなタスクを実行できます。詳細については、「Atlas CLI からのローカル配置とクラウド配置の管理」を参照してください。

注意

Atlas のローカル配置はテスト専用です。 本番環境には、 クラスターをデプロイします。

このチュートリアルでは、次のオープンソース モデルも使用します。

LLM をローカルにダウンロードして配置する方法はいくつかあります。このチュートリアルでは、Ollama をダウンロードし、上記のオープンソース モデルをプルして RAG タスクを実行します。

このチュートリアルでは、 Microsoft .拡張機能も使用します。これらのモデルに接続し、 Atlas ベクトル検索と統合するためのAI.Ollmaパッケージ。別のモデルまたは別のフレームワーク が必要な場合は、Ollma モデル名を希望する設定に対応するものに置き換えることで、このチュートリアルを調整できます。

このチュートリアルでは、次のオープンソース モデルも使用します。

LLM をローカルにダウンロードして配置する方法はいくつかあります。このチュートリアルでは、Ollama をダウンロードし、上記のオープンソース モデルをプルして RAG タスクを実行します。

このチュートリアルでは、人気のオープンソース LLM フレームワークである LangChain の Go 言語ポートも使用して、これらのモデルと接続し、Atlas Vector Search と統合します。別のモデルや別のフレームワークを使用する場合は、Ollama モデル名または LangChain ライブラリ コンポーネントを、使用する設定に合わせて置き換えることで、このチュートリアルを調整できます。

LM をローカルにダウンロードして配置するには、いくつかの方法があります。このチュートリアルでは、Ollma をダウンロードし、次のオープンソースモデルをプルして RG タスクを実行します。

このチュートリアルでは、 Java用の一般的なオープンソース LRMフレームワークである Llang Chartin 4j も使用して、これらのモデルに接続し、Atlas ベクトル検索と統合します。別のモデルまたは別のフレームワークを使用したい場合は、Ollma モデル名または Lgachein4 j ライブラリのコンポーネントを希望する設定の同等なものに置き換えることで、このチュートリアルを調整できます。

このチュートリアルでは、次のオープンソース モデルも使用します。

LLMをローカルにダウンロードして配置するには、いくつかの方法があります。 このチュートリアルでは、 GPT All4 を使用して Misttal7 B モデルをダウンロードします。 は、ローカルLLM 開発のためのオープンソースのエコシステムです。

このチュートリアルでは、対話型の Python ノートブックを使用します。この環境では、毎回ファイル全体を実行中することなく、個々のコード ブロックを作成して実行できます。

このチュートリアルでは、次のオープンソース モデルも使用します。

LLMをローカルにダウンロードして配置するには、いくつかの方法があります。 このチュートリアルでは、 GPT All4 を使用して Misttal7 B モデルをダウンロードします。 は、ローカルLLM 開発のためのオープンソースのエコシステムです。

Atlas の サンプル データ セット からの映画データを含むコレクションを使用します。

Atlas の サンプル データ セット からの映画データを含むコレクションを使用します。

Atlas の サンプル データ セット からの映画データを含むコレクションを使用します。

  • Ollama がインストールされました。

Atlas の サンプル データ セット からの映画データを含むコレクションを使用します。

Atlas の サンプル データ セット からの映画データを含むコレクションを使用します。

注意

Colab などのホスト型サービスを使用する場合 、このチュートリアルを実行するのに十分な RAM があることを確認してください。付けない場合、パフォーマンスの問題が発生する可能性があります。

このチュートリアルでは、ベクトルデータベースとして使用するために、サンプルBnB リスト データセットをロードされたローカルまたはクラウドの 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>

Atlas CLI または Atlas UIを使用して、新しいクラスターを作成して配置できます。新しいクラスターにサンプルデータをプリロードしていることを確認します。

Atlas が提供するサンプルデータをクラスターにロードする方法について学ぶには、「サンプルデータのロード 」を参照してください。

詳細な手順については、「 クラスターの作成 」を参照してください。

このセクションでは、このチュートリアルの環境を設定します。 プロジェクトを作成し、必要なパッケージをインストールし、接続文字列を定義します。

1

ターミナルで次のコマンドを実行して、 MyCompany.RAG.Localという名前の新しいディレクトリを作成し、プロジェクトを初期化します。

dotnet new console -o MyCompany.RAG.Local
cd MyCompany.RAG.Local
2

次のコマンドを実行します。

dotnet add package MongoDB.Driver --version 3.1.0
dotnet add package Microsoft.Extensions.AI.Ollama --prerelease

次のコマンドを実行します。

dotnet add package MongoDB.Driver --version 3.1.0
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.AI --prerelease
dotnet add package Microsoft.Extensions.AI.Ollama --prerelease
3

set接続文字列をPowerShell でエクスポートするか、IDE の環境変数マネージャーを使用して接続文字列をプロジェクトで使用できるようにします。

export ATLAS_CONNECTION_STRING="<connection-string>"

<connection-string> プレースホルダー値を Atlas 接続文字列に置き換えます。

Atlas をローカル配置で使用している場合は、接続文字列がこの形式に従い、<port-number> がローカル配置のポートに置き換えられます。

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

Atlas クラスターを使用している場合は、接続文字列が次の形式に従い、"<connection-string>"; をAtlasクラスターのSRV接続文字列に置き換えます。

export ATLAS_CONNECTION_STRING="<connection-string>"

注意

接続stringには、次の形式を使用する必要があります。

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

このセクションでは、このチュートリアルの環境を設定します。 プロジェクトを作成し、必要なパッケージをインストールし、接続文字列を定義します。

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>"

注意

接続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.grouple
    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 で新しい構成テンプレートを作成し、次の変数をプロジェクトに追加します 。

<port-number> をローカル配置のポートに置き換えます。

接続文字列は、次の形式に従う必要があります。

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

<connection-string> プレースホルダー値を、Atlas クラスター SRV 接続文字列に置き換えます。

接続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>";

注意

接続stringには、次の形式を使用する必要があります。

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

注意

Node.js の最低バージョン要件

Node.js v20.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 --upgrade 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>")

注意

接続stringには、次の形式を使用する必要があります。

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

このセクションでは、埋め込みモデルをローカルに読み込み、sample_airbnbデータベースのデータを使用してベクトル埋め込みを生成します。このデータベースには、listingsAndReviews という 1 つのコレクションが含まれています。

1

この例では Ollama から nomic-embed-text モデルを使用しています。

次のコマンドを実行して、埋め込みモデルをプルします。

ollama pull nomic-embed-text
2

実装の各部分のロジックをカプセル化するには、サービスを調整および管理するためのいくつかのクラスを作成します。

  1. OllamaAIService.csというファイルを作成し、次のコードをそのファイルに貼り付けます。

    OllamaAIService.cs
    namespace MyCompany.RAG.Local;
    using Microsoft.Extensions.AI;
    public class OllamaAIService
    {
    private static readonly Uri OllamaUri = new("http://localhost:11434/");
    private static readonly string EmbeddingModelName = "nomic-embed-text";
    private static readonly OllamaEmbeddingGenerator EmbeddingGenerator = new OllamaEmbeddingGenerator(OllamaUri, EmbeddingModelName);
    public async Task<float[]> GetEmbedding(string text)
    {
    var embedding = await EmbeddingGenerator.GenerateEmbeddingVectorAsync(text);
    return embedding.ToArray();
    }
    }
  2. MongoDBDataService.csという別のファイルを作成し、次のコードをそのファイルに貼り付けます。

    MongoDBDataService.cs
    1namespace MyCompany.RAG.Local;
    2
    3using MongoDB.Driver;
    4using MongoDB.Bson;
    5
    6public class MongoDBDataService
    7{
    8 private static readonly string? ConnectionString = Environment.GetEnvironmentVariable("ATLAS_CONNECTION_STRING");
    9 private static readonly MongoClient Client = new MongoClient(ConnectionString);
    10 private static readonly IMongoDatabase Database = Client.GetDatabase("sample_airbnb");
    11 private static readonly IMongoCollection<BsonDocument> Collection = Database.GetCollection<BsonDocument>("listingsAndReviews");
    12
    13 public List<BsonDocument>? GetDocuments()
    14 {
    15 var filter = Builders<BsonDocument>.Filter.And(
    16 Builders<BsonDocument>.Filter.And(
    17 Builders<BsonDocument>.Filter.Exists("summary", true),
    18 Builders<BsonDocument>.Filter.Ne("summary", "")
    19 ),
    20 Builders<BsonDocument>.Filter.Exists("embeddings", false)
    21 );
    22 return Collection.Find(filter).Limit(250).ToList();
    23 }
    24
    25 public async Task<string> UpdateDocuments(Dictionary<string, float[]> embeddings)
    26 {
    27 var listWrites = new List<WriteModel<BsonDocument>>();
    28 foreach(var kvp in embeddings)
    29 {
    30 var filterForUpdate = Builders<BsonDocument>.Filter.Eq("_id", kvp.Key);
    31 var updateDefinition = Builders<BsonDocument>.Update.Set("embeddings", kvp.Value);
    32 listWrites.Add(new UpdateOneModel<BsonDocument>(filterForUpdate, updateDefinition));
    33 }
    34
    35 try
    36 {
    37 var result = await Collection.BulkWriteAsync(listWrites);
    38 listWrites.Clear();
    39 return $"{result.ModifiedCount} documents updated successfully.";
    40 } catch (Exception e)
    41 {
    42 return $"Exception: {e.Message}";
    43 }
    44 }
    45}

    埋め込みの生成には時間と計算リソースが必要です。この例では 、コレクションからのみ 250 ドキュメントの埋め込みを生成します。これにかかる時間は数分未満です。埋め込みを生成するドキュメントの数を変更する場合は、以下の手順に従います。

    • ドキュメント数を変更します。21 行の Find() 呼び出しで .Limit(250) の数を調整します。

    • すべてのドキュメントの埋め込みを生成します。21 行の Find() 呼び出しで .Limit(250) を完全に省略します。

  3. EmbeddingGenerator.csという別のファイルを作成し、次のコードをそのファイルに貼り付けます。

    EmbeddingGenerator.cs
    1namespace MyCompany.RAG.Local;
    2
    3public class EmbeddingGenerator
    4{
    5 private readonly MongoDBDataService _dataService = new();
    6 private readonly OllamaAIService _ollamaAiService = new();
    7
    8 public async Task<string> GenerateEmbeddings()
    9 {
    10 // Retrieve documents from MongoDB
    11 var documents = _dataService.GetDocuments();
    12 if (documents != null)
    13 {
    14 Console.WriteLine("Generating embeddings.");
    15 Dictionary<string, float[]> embeddings = new Dictionary<string, float[]>();
    16 foreach (var document in documents)
    17 {
    18 try
    19 {
    20 var id = document.GetValue("_id").ToString();
    21 var summary = document.GetValue("summary").ToString();
    22 if (id != null && summary != null)
    23 {
    24 // Use Ollama to generate vector embeddings for each
    25 // document's "summary" field
    26 var embedding = await _ollamaAiService.GetEmbedding(summary);
    27 embeddings.Add(id, embedding);
    28 }
    29 }
    30 catch (Exception e)
    31 {
    32 return $"Error creating embeddings for summaries: {e.Message}";
    33 }
    34 }
    35 // Add a new field to the MongoDB documents with the vector embedding
    36 var result = await _dataService.UpdateDocuments(embeddings);
    37 return result;
    38 }
    39 else
    40 {
    41 return "No documents found";
    42 }
    43 }
    44}

    このコードには次のロジックが含まれています。

    • データベースからドキュメントを取得します。

    • 埋め込みモデルを使用して、各ドキュメントの summaryフィールドのベクトル埋め込みを生成します。

    • 新しい埋め込みでドキュメントを更新します。

  4. 次のコードを Program.cs に貼り付けます。

    Program.cs
    1using MyCompany.RAG.Local;
    2
    3var embeddingGenerator = new EmbeddingGenerator();
    4var result = await embeddingGenerator.GenerateEmbeddings();
    5Console.WriteLine(result);
  5. プロジェクトをコンパイルして実行し、埋め込みを生成します。

    dotnet run MyCompany.RAG.Local.csproj
    Generating embeddings.
    250 documents updated successfully.
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 を超えるドキュメントの埋め込みを生成するプロセスは低速です。埋め込みを生成するドキュメントの数を変更する場合は、以下の手順に従います。

    • ドキュメント数を変更する: 52 行の Find() オプションで .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

次のコマンドを実行して、コストの埋め込みテキストモデルを Ollma からプルします。

ollama pull nomic-embed-text
2

OllamaModels.java というファイルを作成し、次のコードを貼り付けます。

このコードは、プロジェクトで使用するローカル Ollma 埋め込みとチャット モデルを定義します。後のステップでは、チャット モデルを操作します。お好みの設定に応じて、追加のモデルを調整したり、作成したりできます。

このコードでは、以前にダウンロードした埋め込みモデルを使用して、特定の入力の埋め込みを生成するための 2 つの方法も定義しています。

  • 複数の入力:getEmbeddings メソッドはテキスト入力の配列(List<String> )を受け入れるため、1 回のAPI呼び出しに複数の埋め込みを作成できます。このメソッドは、 APIが提供する浮動小数点数の配列を、Atlas クラスターに保存するために double のBSON配列に変換します。

  • 単一入力:getEmbedding Stringメソッドは単一の を受け入れます。これはベクトルデータに対して実行するクエリを表します。メソッドは、 APIが提供する浮動小数点数の配列を、コレクションをクエリするときに使用する double の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 Sync ドライバーを使用して次の操作を実行します。

  1. ローカルの Atlas 配置または Atlas クラスターに接続します。

  2. 空でない summaryフィールドを持つドキュメントのサブセットを sample_airbnb.listingsAndReviewsコレクションから取得します。

    注意

    デモ用に、処理時間を短縮するために 250 ドキュメントの limit を設定します。この制限は、必要に応じて調整または削除して、ユースケースにより適したものにすることができます。

  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

この例では、 混合 bread-AI/mxます:1 埋め込み-大多数の v からのモデル化モデルファイルをダウンロードする最も簡単な方法は、Git と GitLargeファイルストレージを使用してリポジトリをクローンすることです。 名前を付けるには ユーザーのアクセス トークン が必要です または SSH 経由の Git を使用して、リポジトリを複製するリクエストを認証します。

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

Tip

GitLargeファイルストレージ

は、大きなファイル ストレージを必要とします( 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コレクションに対して読み取り操作を実行できます。

Atlas UI でベクトル埋め込みを表示するには、クラスター内の sample_airbnb.listingsAndReviews コレクションに移動し、ドキュメントのフィールドを展開します。

Tip

Atlas でベクトルを効率的に保存、取り込みするために、サンプルデータの埋め込みを BSON ベクトルに変換することもできます。詳細については、「ネイティブ埋め込みを BSON ベクトルに変換する方法」を参照してください。

sample_airbnb.listingsAndReviewsコレクションでベクトル検索を有効にするには、Atlas Vector Search インデックスを作成します。

このチュートリアルでは、サポートされているMongoDBドライバー、または Atlas CLI を使用して、Atlas ベクトル検索インデックスをプログラム的に作成する方法について説明します。 Atlas ベクトル検索インデックスを作成するその他の方法については、「ベクトル検索のフィールドにインデックスを作成する方法 」を参照してください。

注意

Atlas Vector Search インデックスを作成するには、Atlas プロジェクトに対するProject Data Access Admin以上のアクセス権が必要です。

MongoDB C#ドライバー v 以降を使用してコレクション用に Atlas3.1.0 ベクトル検索インデックスを作成するには、次の手順を実行します。

1

検索インデックスを定義するために、MongoDBDataService.cs という名前のファイルに新しい CreateVectorIndex() メソッドを追加します。

MongoDBDataService.cs
namespace MyCompany.RAG.Local;
using MongoDB.Driver;
using MongoDB.Bson;
public class DataService
{
private static readonly string? ConnectionString = Environment.GetEnvironmentVariable("ATLAS_CONNECTION_STRING");
private static readonly MongoClient Client = new MongoClient(ConnectionString);
private static readonly IMongoDatabase Database = Client.GetDatabase("sample_airbnb");
private static readonly IMongoCollection<BsonDocument> Collection = Database.GetCollection<BsonDocument>("listingsAndReviews");
public List<BsonDocument>? GetDocuments()
{
// Method details...
}
public async Task<string> UpdateDocuments(Dictionary<string, float[]> embeddings)
{
// Method details...
}
public string CreateVectorIndex()
{
try
{
var searchIndexView = Collection.SearchIndexes;
var name = "vector_index2";
var type = SearchIndexType.VectorSearch;
var definition = new BsonDocument
{
{ "fields", new BsonArray
{
new BsonDocument
{
{ "type", "vector" },
{ "path", "embeddings" },
{ "numDimensions", 768 },
{ "similarity", "cosine" }
}
}
}
};
var model = new CreateSearchIndexModel(name, type, definition);
searchIndexView.CreateOne(model);
Console.WriteLine($"New search index named {name} is building.");
// Polling for index status
Console.WriteLine("Polling to check if the index is ready. This may take up to a minute.");
bool queryable = false;
while (!queryable)
{
var indexes = searchIndexView.List();
foreach (var index in indexes.ToEnumerable())
{
if (index["name"] == name)
{
queryable = index["queryable"].AsBoolean;
}
}
if (!queryable)
{
Thread.Sleep(5000);
}
}
return $"{name} is ready for querying.";
}
catch (Exception e)
{
return $"Exception: {e.Message}";
}
}
}

このインデックス定義は、 sample_airbnb.listingsAndReviewsコレクションのvectorSearchタイプのインデックスでembeddingsフィールドをインデックス化することを指定します。 このフィールドには、埋め込みモデルを使用して作成された埋め込みが含まれます。 インデックス定義では、 768ベクトル次元を指定し、 cosineを使用して類似性を測定します。

2

Program.cs のコードを次のコードで置き換えて、DataService を初期化し、インデックス作成メソッドを呼び出します。

using MyCompany.RAG.Local;
var dataService = new MongoDBDataService();
var result = dataService.CreateVectorIndex();
Console.WriteLine(result);
3

ファイルを保存し、プロジェクトをコンパイルして実行し、インデックスを作成します。

dotnet run MyCompany.RAG.Local.csproj

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 ベクトル検索インデックスを作成します。

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 Node ドライバー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 ベクトル検索インデックスを作成するには、次の手順を実行します。

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 という名前のファイルを作成し、ファイルに次のインデックス定義を貼り付けます。

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 のようになります。

このセクションでは、Atlas ベクトル検索と Ollma を使用してローカルで実行できるサンプルRG実装を示します。

1
  1. MongoDBDataService.cs という名前のファイルに新しい PerformVectorQuery() メソッドを追加します。

    MongoDBDataService.cs
    namespace MyCompany.RAG.Local;
    using MongoDB.Driver;
    using MongoDB.Bson;
    public class MongoDBDataService
    {
    private static readonly string? ConnectionString = Environment.GetEnvironmentVariable("ATLAS_CONNECTION_STRING");
    private static readonly MongoClient Client = new MongoClient(ConnectionString);
    private static readonly IMongoDatabase Database = Client.GetDatabase("sample_airbnb");
    private static readonly IMongoCollection<BsonDocument> Collection = Database.GetCollection<BsonDocument>("listingsAndReviews");
    public List<BsonDocument>? GetDocuments()
    {
    // Method details...
    }
    public async Task<string> UpdateDocuments(Dictionary<string, float[]> embeddings)
    {
    // Method details...
    }
    public string CreateVectorIndex()
    {
    // Method details...
    }
    public List<BsonDocument>? PerformVectorQuery(float[] vector)
    {
    var vectorSearchStage = new BsonDocument
    {
    {
    "$vectorSearch",
    new BsonDocument
    {
    { "index", "vector_index" },
    { "path", "embeddings" },
    { "queryVector", new BsonArray(vector) },
    { "exact", true },
    { "limit", 5 }
    }
    }
    };
    var projectStage = new BsonDocument
    {
    {
    "$project",
    new BsonDocument
    {
    { "_id", 0 },
    { "summary", 1 },
    { "listing_url", 1 },
    { "score",
    new BsonDocument
    {
    { "$meta", "vectorSearchScore"}
    }
    }
    }
    }
    };
    var pipeline = new[] { vectorSearchStage, projectStage };
    return Collection.Aggregate<BsonDocument>(pipeline).ToList();
    }
    }

    このコードは、ローカル Atlas 配置または Atlas クラスターに対してベクトル クエリを実行します。

  2. PerformTestQuery.csという別のファイルを作成し、次のコードをそのファイルに貼り付けます。

    PerformanceTestQuery.cs
    1namespace MyCompany.RAG.Local;
    2
    3public class PerformTestQuery
    4{
    5 private readonly MongoDBDataService _dataService = new();
    6 private readonly OllamaAIService _ollamaAiService = new();
    7
    8 public async Task<string> GetQueryResults(string question)
    9 {
    10 // Get the vector embedding for the query
    11 var query = question;
    12 var queryEmbedding = await _ollamaAiService.GetEmbedding(query);
    13 // Query the vector database for applicable query results
    14 var matchingDocuments = _dataService.PerformVectorQuery(queryEmbedding);
    15 // Construct a string from the query results for performing QA with the LLM
    16 var sb = new System.Text.StringBuilder();
    17 if (matchingDocuments != null)
    18 {
    19 foreach (var doc in matchingDocuments)
    20 {
    21 sb.AppendLine($"Summary: {doc.GetValue("summary").ToString()}");
    22 sb.AppendLine($"Listing URL: {doc.GetValue("listing_url").ToString()}");
    23 }
    24 }
    25 else
    26 {
    27 return "No matching documents found.";
    28 }
    29 return sb.ToString();
    30 }
    31}

    このコードには次のロジックが含まれています。

    • クエリの埋め込みを定義します。

    • 一致するドキュメントを MongoDBDataService から取得します。

    • 各ドキュメントから「サマリー」と「リスティングURL 」を含む string を作成し、サマリーとして LM に渡します。

  3. テスト クエリを実行して、期待どおりの結果が得られることを確認します。

    Program.cs のコードを次のコードで置き換えます。

    Program.cs
    1using MyCompany.RAG.Local;
    2
    3var query = "beach house";
    4var queryCoordinator = new PerformTestQuery();
    5var result = await queryCoordinator.GetQueryResults(query);
    6Console.WriteLine(result);
  4. ファイルを保存し、プロジェクトをコンパイルして実行し、期待どおりのクエリ結果が得られるかどうかをテストします。

    dotnet run MyCompany.RAG.Local.csproj
    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: 4 Bedroom Country Beach House w/ option to add a separate studio unit- total of 5 bedrooms/2.5 baths at an additional cost. 27 girl steps to white sand beach & infamous Alligator Pond. Private road, NO highway to cross! Safe beach for children & seniors. Convenient! For pricing to add on additional Studio unit, click on our profile pic and input your dates for quote and details!
    Listing URL: https://www.airbnb.com/rooms/12906000
    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
2

次のコマンドを実行して、生成モデルをプルします。

ollama pull mistral
3
  1. 新しい静的メンバーを OllamaAIService.csクラスに追加し、新しい SummarizeAnswer非同期タスクで使用します。

    OllamaAIService.cs
    namespace MyCompany.RAG.Local;
    using Microsoft.Extensions.AI;
    public class OllamaAIService
    {
    private static readonly System.Uri OllamaUri = new Uri("http://localhost:11434/");
    private static readonly Uri OllamaUri = new("http://localhost:11434/");
    private static readonly string EmbeddingModelName = "nomic-embed-text";
    private static readonly OllamaEmbeddingGenerator EmbeddingGenerator = new OllamaEmbeddingGenerator(OllamaUri, EmbeddingModelName);
    private static readonly string ChatModelName = "mistral";
    private static readonly OllamaChatClient ChatClient = new OllamaChatClient(OllamaUri, ChatModelName);
    public async Task<float[]> GetEmbedding(string text)
    {
    // Method details...
    }
    public async Task<string> SummarizeAnswer(string context)
    {
    string question = "Can you recommend me a few AirBnBs that are beach houses? Include a link to the listings.";
    string prompt = $"""
    Use the following pieces of context to answer the question at the end.
    Context: {context}
    Question: {question}
    """;
    ChatCompletion response = await ChatClient.CompleteAsync(prompt, new ChatOptions { MaxOutputTokens = 400 });
    return response.ToString();
    }
    }

    これにより LVM がプロンプトされ、応答が返されます。生成される応答は異なる場合があります。

  2. 新しい PerformQuestionAnswerクラスを定義し、

    • クエリの埋め込みを定義します。

    • 一致するドキュメントを MongoDBDataService から取得します。

    • 応答を要約するには、 LM を使用します。

    AIService.cs
    namespace MyCompany.RAG.Local;
    public class PerformQuestionAnswer
    {
    private readonly MongoDBDataService _dataService = new();
    private readonly OllamaAIService _ollamaAiService = new();
    public async Task<string> SummarizeResults(string question)
    {
    // Get the vector embedding for the query
    var query = question;
    var queryEmbedding = await _ollamaAiService.GetEmbedding(query);
    // Query the vector database for applicable query results
    var matchingDocuments = _dataService.PerformVectorQuery(queryEmbedding);
    // Construct a string from the query results for performing QA with the LLM
    var sb = new System.Text.StringBuilder();
    if (matchingDocuments != null)
    {
    foreach (var doc in matchingDocuments)
    {
    sb.AppendLine($"Summary: {doc.GetValue("summary").ToString()}");
    sb.AppendLine($"Listing URL: {doc.GetValue("listing_url").ToString()}");
    }
    }
    else
    {
    return "No matching documents found.";
    }
    return await _ollamaAiService.SummarizeAnswer(sb.ToString());
    }
    }
  3. Program.cs の内容を新しいブロックに置き換えてタスクを実行します。

    Program.cs
    using MyCompany.RAG.Local;
    var qaTaskCoordinator = new PerformQuestionAnswer();
    const string query = "beach house";
    var results = await qaTaskCoordinator.SummarizeResults(query);
    Console.WriteLine(results);
  4. ファイルを保存し、プロジェクトをコンパイルして実行し、 RAG実装を完了します。

    dotnet run MyCompany.RAG.Local.csproj
    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)

このセクションでは、Atlas ベクトル検索と Ollma を使用してローカルで実行できるサンプルRG実装を示します。

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)
}

このコードでは、次の処理が行われます。

  • クエリstringの埋め込みを作成します。

  • 関連するドキュメントをクエリします。

  • 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)

このセクションでは、Atlas ベクトル検索と Ollma を使用してローカルで実行できるサンプルRG実装を示します。

1

LocalLLM.java という新しいファイルを作成し、次のコードを貼り付けます。

このコードでは、getEmbedding メソッドと retrieveDocuments メソッドと Ollma chatmodel を使用して次の処理を実行します。

  1. Atlas のローカル配置または Atlas クラスターに接続する

  2. 以前に定義した getEmbedding メソッドを使用して、クエリ文字列の埋め込みを生成します。

  3. retrieveDocuments メソッドを使用して、コレクション内で関連するドキュメントをクエリします。

    このクエリには、listing_urlsummary 、およびベクトルscore フィールドのみを返すプロジェクションステージを持つ集計パイプラインが含まれています。このパイプラインを変更または削除して、データと のユースケースに適しています。

  4. createPrompt メソッドを使用して、取得されたドキュメントに質問を連結してコンテキストを作成します。

  5. 作成したプロンプトを、以前に定義した LMchatmodel にフィードして応答を生成します。

  6. 質問と生成された応答をコンソールに出力します。

    注意

    デモ用に、コンテキスト情報とともに入力されるプロンプトも出力します。 この行は本番環境で削除する必要があります。

LocalMM.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

このセクションでは、Atlas ベクトル検索と GPT すべてを使用してローカルで実行できるサンプルRG実装を示します。4

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. 次のボタンをクリックして、GPT 4すべてから Misttal 7 B モデルをダウンロードします。 他のモデルを確認するには、「 GPT4 すべてのウェブサイト 」を参照してください。

    ダウンロード
  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);

このコードでは、次の処理が行われます。

  • クエリstringの埋め込みを作成します。

  • 関連するドキュメントをクエリします。

  • 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.

このセクションでは、Atlas ベクトル検索と GPT すべてを使用してローカルで実行できるサンプルRG実装を示します。4

ノートブックで、次のコード スニペットを実行します。

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. 次のボタンをクリックして、GPT 4すべてから Misttal 7 B モデルをダウンロードします。 他のモデルを確認するには、「 GPT4 すべてのウェブサイト 」を参照してください。

    ダウンロード
  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)

戻る

ハイブリッド検索