このバージョンはまだ開発中であり、まだ安定しているとは考えられていません。最新のスナップショットバージョンについては、Spring AI 1.0.3 を使用してください。 |
ベクトルデータベース
ベクトルデータベースは、AI アプリケーションで重要なロールを果たす特殊な型のデータベースです。
ベクトルデータベースでは、クエリが従来のリレーショナルデータベースとは異なります。完全一致の代わりに、類似性検索が実行されます。クエリとしてベクトルが与えられると、ベクトルデータベースはクエリベクトルに「類似した」ベクトルを返します。この類似性が高レベルでどのように計算されるかについての詳細は、ベクトルの類似性で説明されています。
ベクトルデータベースは、データを AI モデルと統合するために使用されます。使用の最初のステップは、データをベクトルデータベースにロードすることです。次に、ユーザーのクエリが AI モデルに送信されると、最初に類似したドキュメントのセットが取得されます。これらのドキュメントはユーザーの質問のコンテキストとして機能し、ユーザーのクエリとともに AI モデルに送信されます。この手法は検索拡張生成 (RAG) として知られています。
次のセクションでは、複数のベクトルデータベース実装を使用するための Spring AI インターフェースと、いくつかの高レベルのサンプルの使用箇所について説明します。
最後のセクションは、ベクトルデータベースでの類似性検索の基礎となるアプローチをわかりやすく説明することを目的としています。
API の概要
このセクションは、Spring AI フレームワーク内の VectorStore
インターフェースとその関連クラスのガイドとして機能します。
Spring AI は、VectorStore
インターフェースとその読み取り専用版である VectorStoreRetriever
インターフェースを介してベクトルデータベースと対話するための抽象化された API を提供します。
VectorStoreRetriever インターフェース
Spring AI は、ドキュメント取得機能のみを公開する VectorStoreRetriever
と呼ばれる読み取り専用インターフェースを提供します。
@FunctionalInterface
public interface VectorStoreRetriever {
List<Document> similaritySearch(SearchRequest request);
default List<Document> similaritySearch(String query) {
return this.similaritySearch(SearchRequest.builder().query(query).build());
}
}
この関数インターフェースは、ベクトルストアからドキュメントを取得するだけで、変更操作は行わないユースケース向けに設計されています。ドキュメント取得に必要な機能のみを公開することで、最小権限の原則に従っています。
VectorStore インターフェース
VectorStore
インターフェースは VectorStoreRetriever
を継承し、変異機能を追加します。
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {
default String getName() {
return this.getClass().getSimpleName();
}
void add(List<Document> documents);
void delete(List<String> idList);
void delete(Filter.Expression filterExpression);
default void delete(String filterExpression) { ... };
default <T> Optional<T> getNativeClient() {
return Optional.empty();
}
}
VectorStore
インターフェースは読み取り操作と書き込み操作の両方を組み合わせ、ベクトルデータベース内のドキュメントの追加、削除、検索を可能にします。
SearchRequest ビルダー
public class SearchRequest {
public static final double SIMILARITY_THRESHOLD_ACCEPT_ALL = 0.0;
public static final int DEFAULT_TOP_K = 4;
private String query = "";
private int topK = DEFAULT_TOP_K;
private double similarityThreshold = SIMILARITY_THRESHOLD_ACCEPT_ALL;
@Nullable
private Filter.Expression filterExpression;
public static Builder from(SearchRequest originalSearchRequest) {
return builder().query(originalSearchRequest.getQuery())
.topK(originalSearchRequest.getTopK())
.similarityThreshold(originalSearchRequest.getSimilarityThreshold())
.filterExpression(originalSearchRequest.getFilterExpression());
}
public static class Builder {
private final SearchRequest searchRequest = new SearchRequest();
public Builder query(String query) {
Assert.notNull(query, "Query can not be null.");
this.searchRequest.query = query;
return this;
}
public Builder topK(int topK) {
Assert.isTrue(topK >= 0, "TopK should be positive.");
this.searchRequest.topK = topK;
return this;
}
public Builder similarityThreshold(double threshold) {
Assert.isTrue(threshold >= 0 && threshold <= 1, "Similarity threshold must be in [0,1] range.");
this.searchRequest.similarityThreshold = threshold;
return this;
}
public Builder similarityThresholdAll() {
this.searchRequest.similarityThreshold = 0.0;
return this;
}
public Builder filterExpression(@Nullable Filter.Expression expression) {
this.searchRequest.filterExpression = expression;
return this;
}
public Builder filterExpression(@Nullable String textExpression) {
this.searchRequest.filterExpression = (textExpression != null)
? new FilterExpressionTextParser().parse(textExpression) : null;
return this;
}
public SearchRequest build() {
return this.searchRequest;
}
}
public String getQuery() {...}
public int getTopK() {...}
public double getSimilarityThreshold() {...}
public Filter.Expression getFilterExpression() {...}
}
データをベクトルデータベースに挿入するには、データを Document
オブジェクト内にカプセル化します。Document
クラスは、PDF や Word ドキュメントなどのデータソースからのコンテンツをカプセル化し、文字列として表現されるテキストを含めます。ファイル名などの詳細を含む、キーと値のペアの形式のメタデータも含まれます。
ベクトルデータベースに挿入されると、テキストコンテンツは、埋め込みモデルを使用して数値配列、またはベクトル埋め込みと呼ばれる float[]
に変換されます。Word2Vec [Wikipedia] (英語) 、GLoVE [Wikipedia] (英語) 、BERT [Wikipedia] (英語) などの埋め込みモデル、または OpenAI の text-embedding-ada-002
は、単語、文、または段落をこれらのベクトル埋め込みに変換するために使用されます。
ベクトルデータベースのロールは、これらの埋め込みの類似性検索を保存し、容易にすることです。埋め込み自体は生成しません。ベクトル埋め込みを作成するには、EmbeddingModel
を利用する必要があります。
インターフェースの similaritySearch
メソッドを使用すると、指定されたクエリ文字列に類似したドキュメントを取得できます。これらのメソッドは、次のパラメーターを使用して微調整できます。
k
: 返される類似ドキュメントの最大数を指定する整数。これは、「上位 K」検索、または「K 最近傍」(KNN) と呼ばれることがよくあります。threshold
: 0 ~ 1 の範囲の double 値。1 に近い値ほど類似性が高いことを示します。デフォルトでは、たとえば、しきい値を 0.75 に設定すると、この値を超える類似性を持つドキュメントのみが返されます。Filter.Expression
: SQL の 'where' 句と同様に機能する流れるような DSL (ドメイン固有言語) 式を渡すために使用されるクラスですが、Document
のメタデータのキーと値のペアにのみ適用されます。filterExpression
: フィルター式を文字列として受け入れる ANTLR4 に基づく外部 DSL。例: 国、年、isActive
などのメタデータキーでは、次のような式を使用できます。:country == 'UK' && year >= 2020 && isActive == true.
Filter.Expression
の詳細については、"メタデータフィルター" セクションを参照してください。
スキーマの初期化
一部のベクトルストアでは、使用前にバックエンドスキーマを初期化する必要があります。デフォルトでは初期化されません。適切なコンストラクター引数に boolean
を渡すか、Spring Boot を使用する場合は、適切な initialize-schema
プロパティを application.properties
または application.yml
の true
に設定して、オプトインする必要があります。特定のプロパティ名については、使用しているベクトルストアのドキュメントを確認してください。
バッチ戦略
ベクトルストアを使用する場合、大量のドキュメントを埋め込む必要があることがよくあります。1 回の呼び出しですべてのドキュメントを一度に埋め込むのは簡単なように思えるかもしれませんが、このアプローチは課題を引き起こす可能性があります。埋め込みモデルはテキストをトークンとして処理し、最大トークン制限 (コンテキストウィンドウサイズと呼ばれることが多い) があります。この制限により、1 回の埋め込みリクエストで処理できるテキストの量が制限されます。1 回の呼び出しで埋め込もうとするトークンが多すぎると、エラーが発生したり、埋め込みが切り捨てられたりする可能性があります。
このトークン制限に対処するために、Spring AI はバッチ処理戦略を実装しています。このアプローチでは、大量のドキュメントセットを、埋め込みモデルの最大コンテキストウィンドウ内に収まる小さなバッチに分割します。バッチ処理はトークン制限の課題を解決するだけでなく、パフォーマンスの向上や API レート制限のより効率的な使用にもつながります。
Spring AI は、BatchingStrategy
インターフェースを通じてこの機能を提供します。これにより、トークン数に基づいてサブバッチでドキュメントを処理できるようになります。
コア BatchingStrategy
インターフェースは次のように定義されます。
public interface BatchingStrategy {
List<List<Document>> batch(List<Document> documents);
}
このインターフェースは、ドキュメントのリストを受け取り、ドキュメントバッチのリストを返す単一のメソッド batch
を定義します。
デフォルトの実装
Spring AI は、TokenCountBatchingStrategy
と呼ばれるデフォルトの実装を提供します。この戦略は、トークン数に基づいてドキュメントをバッチ処理し、各バッチが計算された最大入力トークン数を超えないようにします。
TokenCountBatchingStrategy
の主な特徴:
デフォルトの上限として OpenAI の最大入力トークン数 (英語) (8191) を使用します。
潜在的なオーバーヘッドのためのバッファを提供するために、予約率 (デフォルトは 10%) を組み込みます。
実際の最大入力トークン数を次のように計算します:
actualMaxInputTokenCount = originalMaxInputTokenCount * (1 - RESERVE_PERCENTAGE)
この戦略では、各ドキュメントのトークン数を推定し、最大入力トークン数を超えないようにバッチにグループ化し、単一のドキュメントがこの制限を超えた場合は例外をスローします。
また、TokenCountBatchingStrategy
をカスタマイズして、特定の要件に適合させることもできます。これは、Spring Boot @Configuration
クラスでカスタムパラメーターを使用して新しいインスタンスを作成することで実行できます。
カスタム TokenCountBatchingStrategy
Bean を作成する方法の例を次に示します。
@Configuration
public class EmbeddingConfig {
@Bean
public BatchingStrategy customTokenCountBatchingStrategy() {
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE, // Specify the encoding type
8000, // Set the maximum input token count
0.1 // Set the reserve percentage
);
}
}
この構成では、次のようになります。
EncodingType.CL100K_BASE
: トークン化に使用するエンコード型を指定します。このエンコード型は、JTokkitTokenCountEstimator
によってトークン数を正確に推定するために使用されます。8000
: 最大入力トークン数を設定します。この値は、埋め込みモデルの最大コンテキストウィンドウサイズ以下にする必要があります。0.1
: 予約率を設定します。最大入力トークン数から予約するトークンの割合。これにより、処理中にトークン数が増加する可能性に備えてバッファが作成されます。
デフォルトでは、このコンストラクターはコンテンツのフォーマットに Document.DEFAULT_CONTENT_FORMATTER
を使用し、メタデータの処理に MetadataMode.NONE
を使用します。これらのパラメーターをカスタマイズする必要がある場合は、追加のパラメーターを含む完全なコンストラクターを使用できます。
定義されると、このカスタム TokenCountBatchingStrategy
Bean は、デフォルトの戦略を置き換えて、アプリケーション内の EmbeddingModel
実装によって自動的に使用されます。
TokenCountBatchingStrategy
は、効率的なバッチ処理のためにトークン数を計算するために、内部的に TokenCountEstimator
(具体的には JTokkitTokenCountEstimator
) を使用します。これにより、指定されたエンコード型に基づいて正確なトークン推定が保証されます。
さらに、TokenCountBatchingStrategy
は、TokenCountEstimator
インターフェースの独自の実装を渡すことができるため、柔軟性が高まります。この機能により、特定のニーズに合わせたカスタムトークンカウント戦略を使用できます。例:
TokenCountEstimator customEstimator = new YourCustomTokenCountEstimator();
TokenCountBatchingStrategy strategy = new TokenCountBatchingStrategy(
this.customEstimator,
8000, // maxInputTokenCount
0.1, // reservePercentage
Document.DEFAULT_CONTENT_FORMATTER,
MetadataMode.NONE
);
自動切り捨ての操作
Vertex AI テキスト埋め込みなどの一部の埋め込みモデルは、auto_truncate
機能をサポートしています。この機能を有効にすると、モデルは最大サイズを超えるテキスト入力を暗黙的に切り捨てて処理を続行します。無効にすると、入力が大きすぎる場合に明示的なエラーが発生します。
バッチ戦略で自動切り捨てを使用する場合は、バッチ戦略の入力トークン数をモデルの実際の最大値よりもはるかに大きく設定する必要があります。これにより、バッチ戦略が大きなドキュメントに対して例外を発生させるのを防ぎ、埋め込みモデルが内部的に切り捨てを処理できるようになります。
自動切り捨ての設定
自動切り捨てを有効にする場合、バッチ処理戦略の最大入力トークン数をモデルの実際の制限よりもはるかに高く設定してください。これにより、バッチ処理戦略が大きなドキュメントに対して例外を発生させるのを防ぎ、埋め込みモデルが内部的に切り捨てを処理できるようになります。
自動切り捨て機能付きの Vertex AI とカスタム BatchingStrategy
を使用し、PgVectorStore で使用する構成例を次に示します。
@Configuration
public class AutoTruncationEmbeddingConfig {
@Bean
public VertexAiTextEmbeddingModel vertexAiEmbeddingModel(
VertexAiEmbeddingConnectionDetails connectionDetails) {
VertexAiTextEmbeddingOptions options = VertexAiTextEmbeddingOptions.builder()
.model(VertexAiTextEmbeddingOptions.DEFAULT_MODEL_NAME)
.autoTruncate(true) // Enable auto-truncation
.build();
return new VertexAiTextEmbeddingModel(connectionDetails, options);
}
@Bean
public BatchingStrategy batchingStrategy() {
// Only use a high token limit if auto-truncation is enabled in your embedding model.
// Set a much higher token count than the model actually supports
// (e.g., 132,900 when Vertex AI supports only up to 20,000)
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE,
132900, // Artificially high limit
0.1 // 10% reserve
);
}
@Bean
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, BatchingStrategy batchingStrategy) {
return PgVectorStore.builder(jdbcTemplate, embeddingModel)
// other properties omitted here
.build();
}
}
この構成では、次のようになります。
埋め込みモデルでは自動切り捨てが有効になっているため、サイズが大きすぎる入力を適切に処理できます。
バッチ処理戦略では、実際のモデル制限 (20,000) よりもはるかに大きい、人為的に高いトークン制限 (132,900) を使用します。
ベクトルストアは、構成された埋め込みモデルとカスタム
BatchingStrategy
Bean を使用します。
なぜこれが機能するのか
このアプローチが機能する理由は次のとおりです。
TokenCountBatchingStrategy
は、単一のドキュメントが設定された最大値を超えているかどうかを確認し、超過している場合はIllegalArgumentException
をスローします。バッチ処理戦略で非常に高い制限を設定することにより、このチェックが失敗しないことが保証されます。
モデルの制限を超えるドキュメントまたはバッチは、埋め込みモデルの自動切り捨て機能によって自動的に切り捨てられ、処理されます。
ベストプラクティス
自動切り捨てを使用する場合:
バッチ戦略による早期の例外を回避するには、バッチ戦略の最大入力トークン数をモデルの実際の制限より少なくとも 5-10x 大きく設定します。
埋め込みモデルからの切り捨て警告がないかログを監視します (注: すべてのモデルが切り捨てイベントをログに記録するわけではありません)。
サイレントトランケーションが埋め込み品質に与える影響を考慮してください。
切り捨てられた埋め込みが要件を満たしていることを確認するために、サンプルドキュメントでテストします。
この設定は非標準なので、将来のメンテナーのためにドキュメント化してください。
自動切り捨てはエラーを防ぎますが、埋め込みが不完全になる可能性があります。長いドキュメントの末尾にある重要な情報が失われる可能性があります。アプリケーションですべてのコンテンツを埋め込む必要がある場合は、埋め込む前にドキュメントを小さなチャンクに分割してください。 |
Spring Boot 自動構成
Spring Boot 自動構成を使用している場合は、Spring AI に付属するデフォルトの BatchingStrategy
Bean をオーバーライドするために、カスタムの BatchingStrategy
Bean を提供する必要があります。
@Bean
public BatchingStrategy customBatchingStrategy() {
// This bean will override the default BatchingStrategy
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE,
132900, // Much higher than model's actual limit
0.1
);
}
アプリケーションコンテキストにこの Bean が存在すると、すべてのベクトルストアで使用されるデフォルトのバッチ処理戦略が自動的に置き換えられます。
カスタム実装
TokenCountBatchingStrategy
は堅牢なデフォルト実装を提供しますが、特定のニーズに合わせてバッチ処理戦略をカスタマイズできます。これは、Spring Boot の自動構成を通じて実行できます。
バッチ処理戦略をカスタマイズするには、Spring Boot アプリケーションで BatchingStrategy
Bean を定義します。
@Configuration
public class EmbeddingConfig {
@Bean
public BatchingStrategy customBatchingStrategy() {
return new CustomBatchingStrategy();
}
}
このカスタム BatchingStrategy
は、アプリケーション内の EmbeddingModel
実装によって自動的に使用されます。
Spring AI でサポートされているベクトルストアは、デフォルトの TokenCountBatchingStrategy を使用するように構成されています。SAP Hana ベクトルストアは現在、バッチ処理用に構成されていません。 |
VectorStore の実装
VectorStore
インターフェースの利用可能な実装は次のとおりです。
Azure ベクトル検索 - Azure (英語) ベクトルストア。
Apache Cassandra - Apache Cassandra (英語) ベクトルストア。
Chroma ベクトルストア - Chroma (英語) ベクトルストア。
Elasticsearch ベクトルストア - Elasticsearch (英語) ベクトルストア。
GemFire ベクトルストア - GemFire (英語) ベクトルストア。
MariaDB ベクトルストア - MariaDB (英語) ベクトルストア。
Milvus ベクトルストア - Milvus (英語) ベクトルストア。
MongoDB Atlas ベクトルストア - MongoDB Atlas (英語) ベクトルストア。
Neo4j ベクトルストア - Neo4j (英語) ベクトルストア。
OpenSearch ベクトルストア - OpenSearch (英語) ベクトルストア。
Oracle ベクトルストア - Oracle データベース (英語) ベクトルストア。
PgVector ストア - PostgreSQL/PGVector [GitHub] (英語) ベクトルストア。
Pinecone ベクトルストア - PineCone (英語) ベクトルストア。
Qdrant ベクトルストア - Qdrant (英語) ベクトルストア。
Redis ベクトルストア - Redis (英語) ベクトルストア。
SAP ハナベクトルストア - SAP HANA (英語) ベクトルストア。
Typesense ベクトルストア - Typesense (英語) ベクトルストア。
Weaviate ベクトルストア - Weaviate (英語) ベクトルストア。
SimpleVectorStore [GitHub] (英語) - 永続的なベクトルストレージのシンプルな実装であり、教育目的に適しています。
将来のリリースでは、さらに多くの実装がサポートされる可能性があります。
Spring AI でサポートする必要があるベクトルデータベースがある場合は、GitHub で課題をオープンするか、実装を含めたプルリクエストを送信するとさらに良いでしょう。
VectorStore
の各実装に関する情報は、この章のサブセクションに記載されています。
使用例
ベクトルデータベースの埋め込みを計算するには、使用されている高レベルの AI モデルと一致する埋め込みモデルを選択する必要があります。
例: OpenAI の ChatGPT では、OpenAiEmbeddingModel
と text-embedding-ada-002
というモデルを使用します。
Spring Boot スターターの OpenAI 用の自動構成により、依存性注入のために Spring アプリケーションコンテキストで EmbeddingModel
の実装が利用できるようになります。
ベクトルストアへの書き込み
ベクトルストアにデータをロードする一般的な使用方法は、最初に Spring AI の Document
クラスにデータをロードし、次に VectorStore
インターフェースの add
メソッドを呼び出すことによって、バッチのようなジョブで行うものです。
ベクトルデータベースにロードしたいデータを含む JSON ファイルを表すソースファイルへの String
参照が与えられると、Spring AI の JsonReader
を使って JSON の特定のフィールドをロードし、それを小片に分割してベクトルストアの実装に渡します。VectorStore
の実装は埋め込みを計算し、JSON と埋め込みをベクトルデータベースに格納します。
@Autowired
VectorStore vectorStore;
void load(String sourceFile) {
JsonReader jsonReader = new JsonReader(new FileSystemResource(sourceFile),
"price", "name", "shortDescription", "description", "tags");
List<Document> documents = jsonReader.get();
this.vectorStore.add(documents);
}
ベクトルストアからの読み取り
その後、ユーザーの質問が AI モデルに渡されると、類似性検索が実行され、類似のドキュメントが取得され、ユーザーの質問のコンテキストとしてプロンプトに「詰め込まれ」ます。
読み取り専用操作の場合は、VectorStore
インターフェースまたはより重点を置いた VectorStoreRetriever
インターフェースのいずれかを使用できます。
@Autowired
VectorStoreRetriever retriever; // Could also use VectorStore here
String question = "<question from user>";
List<Document> similarDocuments = retriever.similaritySearch(question);
// Or with more specific search parameters
SearchRequest request = SearchRequest.builder()
.query(question)
.topK(5) // Return top 5 results
.similarityThreshold(0.7) // Only return results with similarity score >= 0.7
.build();
List<Document> filteredDocuments = retriever.similaritySearch(request);
追加のオプションを similaritySearch
メソッドに渡して、取得するドキュメントの数と類似性検索のしきい値を定義できます。
Separation of Read and Write Operations
Using the separate interfaces allows you to clearly define which components need write access and which only need read access:
// Write operations in a service that needs full access
@Service
class DocumentIndexer {
private final VectorStore vectorStore;
DocumentIndexer(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}
public void indexDocuments(List<Document> documents) {
vectorStore.add(documents);
}
}
// Read-only operations in a service that only needs retrieval
@Service
class DocumentRetriever {
private final VectorStoreRetriever retriever;
DocumentRetriever(VectorStoreRetriever retriever) {
this.retriever = retriever;
}
public List<Document> findSimilar(String query) {
return retriever.similaritySearch(query);
}
}
This separation of concerns helps create more maintainable and secure applications by limiting access to mutation operations only to components that truly need them.
Retrieval Operations with VectorStoreRetriever
The VectorStoreRetriever
interface provides a read-only view of a vector store, exposing only the similarity search functionality. This follows the principle of least privilege and is particularly useful in RAG (Retrieval-Augmented Generation) applications where you only need to retrieve documents without modifying the underlying data.
Benefits of Using VectorStoreRetriever
Separation of Concerns : Clearly separates read operations from write operations.
Interface Segregation : Clients that only need retrieval functionality aren’t exposed to mutation methods.
Functional Interface : Can be implemented with lambda expressions or method references for simple use cases.
Reduced Dependencies : Components that only need to perform searches don’t need to depend on the full
VectorStore
interface.
使用例
You can use VectorStoreRetriever
directly when you only need to perform similarity searches:
@Service
public class DocumentRetrievalService {
private final VectorStoreRetriever retriever;
public DocumentRetrievalService(VectorStoreRetriever retriever) {
this.retriever = retriever;
}
public List<Document> findSimilarDocuments(String query) {
return retriever.similaritySearch(query);
}
public List<Document> findSimilarDocumentsWithFilters(String query, String country) {
SearchRequest request = SearchRequest.builder()
.query(query)
.topK(5)
.filterExpression("country == '" + country + "'")
.build();
return retriever.similaritySearch(request);
}
}
In this example, the service only depends on the VectorStoreRetriever
interface, making it clear that it only performs retrieval operations and doesn’t modify the vector store.
Integration with RAG Applications
The VectorStoreRetriever
interface is particularly useful in RAG applications, where you need to retrieve relevant documents to provide context for an AI model:
@Service
public class RagService {
private final VectorStoreRetriever retriever;
private final ChatModel chatModel;
public RagService(VectorStoreRetriever retriever, ChatModel chatModel) {
this.retriever = retriever;
this.chatModel = chatModel;
}
public String generateResponse(String userQuery) {
// Retrieve relevant documents
List<Document> relevantDocs = retriever.similaritySearch(userQuery);
// Extract content from documents to use as context
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
// Generate response using the retrieved context
String prompt = "Context information:\n" + context + "\n\nUser query: " + userQuery;
return chatModel.generate(prompt);
}
}
This pattern allows for a clean separation between the retrieval component and the generation component in RAG applications.
メタデータフィルター
このセクションでは、クエリの結果に対して使用できるさまざまなフィルターについて説明します。
フィルターストリング
SQL のようなフィルター式を String
として similaritySearch
オーバーロードの 1 つに渡すことができます。
次の例を考えてみましょう。
"country == 'BG'"
"genre == 'drama' && year >= 2020"
"genre in ['comedy', 'documentary', 'drama']"
Filter.Expression
スムーズな API を公開する FilterExpressionBuilder
を使用して Filter.Expression
のインスタンスを作成できます。簡単な例は次のとおりです。
FilterExpressionBuilder b = new FilterExpressionBuilder();
Expression expression = this.b.eq("country", "BG").build();
次の演算子を使用して、洗練された式を作成できます。
EQUALS: '=='
MINUS : '-'
PLUS: '+'
GT: '>'
GE: '>='
LT: '<'
LE: '<='
NE: '!='
次の演算子を使用して式を組み合わせることができます。
AND: 'AND' | 'and' | '&&';
OR: 'OR' | 'or' | '||';
次の例を考えてみましょう。
Expression exp = b.and(b.eq("genre", "drama"), b.gte("year", 2020)).build();
次の演算子も使用できます。
IN: 'IN' | 'in';
NIN: 'NIN' | 'nin';
NOT: 'NOT' | 'not';
次の例を考えてみましょう。
Expression exp = b.and(b.in("genre", "drama", "documentary"), b.not(b.lt("year", 2020))).build();
ベクトルストアからドキュメントを削除する
ベクトルストアインターフェースには、ドキュメントを削除するための複数のメソッドが用意されており、特定のドキュメント ID またはフィルター式を使用してデータを削除できます。
ドキュメント ID による削除
ドキュメントを削除する最も簡単な方法は、ドキュメント ID のリストを提供することです。
void delete(List<String> idList);
このメソッドは、指定されたリスト内の ID と一致するすべてのドキュメントを削除します。リスト内の ID がストアに存在しない場合は無視されます。
// Create and add document
Document document = new Document("The World is Big",
Map.of("country", "Netherlands"));
vectorStore.add(List.of(document));
// Delete document by ID
vectorStore.delete(List.of(document.getId()));
フィルター式による削除
より複雑な削除条件の場合は、フィルター式を使用できます。
void delete(Filter.Expression filterExpression);
このメソッドは、削除するドキュメントの条件を定義する Filter.Expression
オブジェクトを受け入れます。これは、メタデータプロパティに基づいてドキュメントを削除する必要がある場合に特に便利です。
// Create test documents with different metadata
Document bgDocument = new Document("The World is Big",
Map.of("country", "Bulgaria"));
Document nlDocument = new Document("The World is Big",
Map.of("country", "Netherlands"));
// Add documents to the store
vectorStore.add(List.of(bgDocument, nlDocument));
// Delete documents from Bulgaria using filter expression
Filter.Expression filterExpression = new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key("country"),
new Filter.Value("Bulgaria")
);
vectorStore.delete(filterExpression);
// Verify deletion with search
SearchRequest request = SearchRequest.builder()
.query("World")
.filterExpression("country == 'Bulgaria'")
.build();
List<Document> results = vectorStore.similaritySearch(request);
// results will be empty as Bulgarian document was deleted
文字列フィルター式による削除
便宜上、文字列ベースのフィルター式を使用してドキュメントを削除することもできます。
void delete(String filterExpression);
このメソッドは、提供された文字列フィルターを内部的に Filter.Expression
オブジェクトに変換します。文字列形式のフィルター条件がある場合に便利です。
// Create and add documents
Document bgDocument = new Document("The World is Big",
Map.of("country", "Bulgaria"));
Document nlDocument = new Document("The World is Big",
Map.of("country", "Netherlands"));
vectorStore.add(List.of(bgDocument, nlDocument));
// Delete Bulgarian documents using string filter
vectorStore.delete("country == 'Bulgaria'");
// Verify remaining documents
SearchRequest request = SearchRequest.builder()
.query("World")
.topK(5)
.build();
List<Document> results = vectorStore.similaritySearch(request);
// results will only contain the Netherlands document
削除 API を呼び出すときのエラー処理
すべての削除メソッドは、エラーが発生した場合に例外をスローする可能性があります。
ベストプラクティスは、削除操作を try-catch ブロックでラップすることです。
try {
vectorStore.delete("country == 'Bulgaria'");
}
catch (Exception e) {
logger.error("Invalid filter expression", e);
}
ドキュメントのバージョン管理のユースケース
一般的なシナリオは、ドキュメントのバージョン管理で、古いバージョンを削除しながら新しいバージョンのドキュメントをアップロードする必要がある場合です。フィルター式を使用してこれを処理する方法は次のとおりです。
// Create initial document (v1) with version metadata
Document documentV1 = new Document(
"AI and Machine Learning Best Practices",
Map.of(
"docId", "AIML-001",
"version", "1.0",
"lastUpdated", "2024-01-01"
)
);
// Add v1 to the vector store
vectorStore.add(List.of(documentV1));
// Create updated version (v2) of the same document
Document documentV2 = new Document(
"AI and Machine Learning Best Practices - Updated",
Map.of(
"docId", "AIML-001",
"version", "2.0",
"lastUpdated", "2024-02-01"
)
);
// First, delete the old version using filter expression
Filter.Expression deleteOldVersion = new Filter.Expression(
Filter.ExpressionType.AND,
Arrays.asList(
new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key("docId"),
new Filter.Value("AIML-001")
),
new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key("version"),
new Filter.Value("1.0")
)
)
);
vectorStore.delete(deleteOldVersion);
// Add the new version
vectorStore.add(List.of(documentV2));
// Verify only v2 exists
SearchRequest request = SearchRequest.builder()
.query("AI and Machine Learning")
.filterExpression("docId == 'AIML-001'")
.build();
List<Document> results = vectorStore.similaritySearch(request);
// results will contain only v2 of the document
文字列フィルター式を使用して同じことを実現することもできます。
// Delete old version using string filter
vectorStore.delete("docId == 'AIML-001' AND version == '1.0'");
// Add new version
vectorStore.add(List.of(documentV2));