RAGシステムにおける検索
埋め込みを保存した後、ユーザーの質問に答えるために関連するチャンクを取得する必要があります。
Mastraはセマンティック検索、フィルタリング、リランキングに対応し、柔軟な検索オプションを提供します。
検索の仕組み
- ユーザーのクエリは、ドキュメント埋め込みと同じモデルでベクトルに変換されます
- このベクトルは、ベクトル類似度で保存済みの埋め込みと比較されます
- 最も類似したチャンクが取得され、必要に応じて次の処理を行えます:
- メタデータによるフィルタリング
- 関連性向上のための再ランキング
- ナレッジグラフを用いた処理
基本的なリトリーバル
最もシンプルな方法は、直接的なセマンティック検索です。この方法では、ベクトル類似度を用いて、クエリと意味的に近いチャンクを見つけます。
import { openai } from "@ai-sdk/openai";
import { embed } from "ai";
import { PgVector } from "@mastra/pg";
// クエリを埋め込みに変換
const { embedding } = await embed({
value: "What are the main points in the article?",
model: openai.embedding("text-embedding-3-small"),
});
// ベクターストアを検索
const pgVector = new PgVector({
connectionString: process.env.POSTGRES_CONNECTION_STRING,
});
const results = await pgVector.query({
indexName: "embeddings",
queryVector: embedding,
topK: 10,
});
// 結果を表示
console.log(results);
結果にはテキスト内容と類似度スコアの両方が含まれます。
[
{
text: "Climate change poses significant challenges...",
score: 0.89,
metadata: { source: "article1.txt" },
},
{
text: "Rising temperatures affect crop yields...",
score: 0.82,
metadata: { source: "article1.txt" },
},
// ... more results
];
基本的なリトリーバルの使い方については、Retrieve Results を参照してください。
高度な検索オプション
メタデータフィルタリング
メタデータフィールドに基づいて結果をフィルタリングし、検索範囲を絞り込みます。これは、異なるソースや期間、特定の属性を持つドキュメントがある場合に有用です。Mastra は、サポートされているすべてのベクターストアで動作する、統一された MongoDB 風のクエリ構文を提供します。
利用可能な演算子と構文の詳細は、Metadata Filters Referenceを参照してください。
基本的なフィルタリング例:
// Simple equality filter
const results = await pgVector.query({
indexName: "embeddings",
queryVector: embedding,
topK: 10,
filter: {
source: "article1.txt",
},
});
// Numeric comparison
const results = await pgVector.query({
indexName: "embeddings",
queryVector: embedding,
topK: 10,
filter: {
price: { $gt: 100 },
},
});
// Multiple conditions
const results = await pgVector.query({
indexName: "embeddings",
queryVector: embedding,
topK: 10,
filter: {
category: "electronics",
price: { $lt: 1000 },
inStock: true,
},
});
// Array operations
const results = await pgVector.query({
indexName: "embeddings",
queryVector: embedding,
topK: 10,
filter: {
tags: { $in: ["sale", "new"] },
},
});
// Logical operators
const results = await pgVector.query({
indexName: "embeddings",
queryVector: embedding,
topK: 10,
filter: {
$or: [{ category: "electronics" }, { category: "accessories" }],
$and: [{ price: { $gt: 50 } }, { price: { $lt: 200 } }],
},
});
メタデータフィルタリングの一般的なユースケース:
- ドキュメントの出典や種類でフィルタリング
- 日付範囲でフィルタリング
- 特定のカテゴリやタグでフィルタリング
- 数値範囲(例: 価格、評価)でフィルタリング
- 複数の条件を組み合わせて精密にクエリ
- ドキュメント属性(例: 言語、著者)でフィルタリング
メタデータフィルタリングの使用例は、Hybrid Vector Searchを参照してください。
ベクタークエリツール
エージェントにベクターデータベースへ直接クエリする機能を持たせたい場合があります。ベクタークエリツールを使うと、ユーザーのニーズに対するエージェントの理解に基づき、セマンティック検索に加えて任意のフィルタリングやリランキングを組み合わせながら、どの情報を取得するかの判断をエージェント自身が行えるようになります。
const vectorQueryTool = createVectorQueryTool({
vectorStoreName: "pgVector",
indexName: "embeddings",
model: openai.embedding("text-embedding-3-small"),
});
ツールを作成する際は、ツール名と説明を特に工夫してください。これらは、エージェントが取得機能をいつ、どのように使うべきかを理解する助けになります。たとえば、名前を “SearchKnowledgeBase”、説明を “ドキュメント群を検索して X に関する関連情報を見つけます” のようにできます。
これは次のような場合に特に有用です:
- エージェントがどの情報を取得するかを動的に判断する必要があるとき
- 取得プロセスに複雑な意思決定が伴うとき
- 文脈に応じて複数の取得戦略を組み合わせたいとき
データベース固有の設定
Vector Query Tool は、各種ベクターストアの固有機能や最適化を活用できる、データベース固有の設定をサポートします。
// Pinecone with namespace
const pineconeQueryTool = createVectorQueryTool({
vectorStoreName: "pinecone",
indexName: "docs",
model: openai.embedding("text-embedding-3-small"),
databaseConfig: {
pinecone: {
namespace: "production" // 環境ごとにデータを分離
}
}
});
// pgVector with performance tuning
const pgVectorQueryTool = createVectorQueryTool({
vectorStoreName: "postgres",
indexName: "embeddings",
model: openai.embedding("text-embedding-3-small"),
databaseConfig: {
pgvector: {
minScore: 0.7, // 低品質な結果を除外
ef: 200, // HNSW の検索パラメータ
probes: 10 // IVFFlat のプローブ数
}
}
});
// Chroma with advanced filtering
const chromaQueryTool = createVectorQueryTool({
vectorStoreName: "chroma",
indexName: "documents",
model: openai.embedding("text-embedding-3-small"),
databaseConfig: {
chroma: {
where: { "category": "technical" },
whereDocument: { "$contains": "API" }
}
}
});
// LanceDB with table specificity
const lanceQueryTool = createVectorQueryTool({
vectorStoreName: "lance",
indexName: "documents",
model: openai.embedding("text-embedding-3-small"),
databaseConfig: {
lance: {
tableName: "myVectors", // クエリ対象のテーブルを指定
includeAllColumns: true // すべてのメタデータ列を結果に含める
}
}
});
主な利点:
- Pinecone の namespace: テナント・環境・データ種別ごとにベクターを整理
- pgVector の最適化: ef/probes パラメータで検索精度と速度を調整
- 品質フィルタリング: 最小類似度しきい値を設定して結果の関連性を向上
- LanceDB のテーブル: テーブル分割で整理性とパフォーマンスを向上
- ランタイムの柔軟性: コンテキストに応じて設定を動的に上書き可能
一般的なユースケース:
- Pinecone の namespace を用いたマルチテナントアプリケーション
- 高負荷時のパフォーマンス最適化
- 環境別の設定(dev/staging/prod)
- 品質基準を設けた検索結果
- エッジ配備向けの LanceDB による組み込み・ファイルベースのベクターストレージ
これらの設定は、ランタイムコンテキストを用いて実行時に上書きすることもできます。
import { RuntimeContext } from '@mastra/core/runtime-context';
const runtimeContext = new RuntimeContext();
runtimeContext.set('databaseConfig', {
pinecone: {
namespace: 'runtime-namespace'
}
});
await pineconeQueryTool.execute({
context: { queryText: 'search query' },
mastra,
runtimeContext
});
詳細な設定オプションや高度な使用方法については、Vector Query Tool Reference を参照してください。
ベクターストア用プロンプト
ベクターストア用プロンプトは、各ベクターデータベース実装におけるクエリパターンとフィルタリング機能を定義します。 フィルタリングを実装する際には、各ベクターストア実装で有効な演算子や構文を明確にするため、これらのプロンプトをエージェントの指示に含める必要があります。
Pg Vector
import { openai } from '@ai-sdk/openai';
import { PGVECTOR_PROMPT } from "@mastra/pg";
export const ragAgent = new Agent({
name: 'RAGエージェント',
model: openai('gpt-4o-mini'),
instructions: `
提供されたコンテキストを用いてクエリを処理してください。応答は簡潔かつ関連性の高い構成にしてください。
${PGVECTOR_PROMPT}
`,
tools: { vectorQueryTool },
});
再ランキング
初期のベクトル類似度検索では、微妙な関連性を見落とすことがあります。再ランキングは計算コストはかかるものの、より高精度なアルゴリズムで、次の点によって結果を改善します:
- 語順や厳密な一致を考慮する
- より洗練された関連度スコアリングを適用する
- クエリとドキュメント間のクロスアテンションと呼ばれる手法を用いる
再ランキングの使い方は次のとおりです:
import { openai } from "@ai-sdk/openai";
import {
rerankWithScorer as rerank,
MastraAgentRelevanceScorer
} from "@mastra/rag";
// Get initial results from vector search
const initialResults = await pgVector.query({
indexName: "embeddings",
queryVector: queryEmbedding,
topK: 10,
});
// Create a relevance scorer
const relevanceProvider = new MastraAgentRelevanceScorer('relevance-scorer', openai("gpt-4o-mini"));
// Re-rank the results
const rerankedResults = await rerank({
results: initialResults,
query,
provider: relevanceProvider,
options: {
topK: 10,
},
);
Note: 再ランキング時にセマンティック・スコアリングが正しく機能するためには、各結果にテキストコンテンツを
metadata.text
フィールドとして含めておく必要があります。
Cohere や ZeroEntropy など、他の関連度スコア提供プロバイダーも利用できます:
const relevanceProvider = new CohereRelevanceScorer('rerank-v3.5');
const relevanceProvider = new ZeroEntropyRelevanceScorer('zerank-1');
再ランキングされた結果は、ベクトル類似度と意味理解を組み合わせて、検索品質を向上させます。
再ランキングの詳細については、rerank() メソッドを参照してください。
再ランキング手法の使用例については、Re-ranking Results をご覧ください。
グラフベースの検索
複雑な関係を持つドキュメントでは、グラフベースの検索によりチャンク間のつながりをたどれます。次のような場合に有効です:
- 情報が複数のドキュメントにまたがっている
- ドキュメント同士が参照し合っている
- 完全な回答を得るために関係をたどる必要がある
セットアップ例:
const graphQueryTool = createGraphQueryTool({
vectorStoreName: "pgVector",
indexName: "embeddings",
model: openai.embedding("text-embedding-3-small"),
graphOptions: {
threshold: 0.7,
},
});
グラフベースの検索の詳細は、GraphRAG クラスと createGraphQueryTool() 関数を参照してください。
グラフベースの検索手法の使用例は、Graph-based Retrieval を参照してください。