エージェント駆動型メタデータフィルタリング
この例では、Mastra、OpenAIの埋め込み、そしてベクトルストレージとしてPGVectorを使用して、検索拡張生成(RAG)システムを実装する方法を示します。 このシステムは、エージェントがユーザーのクエリからメタデータフィルターを構築し、ベクトルストア内で関連するチャンクを検索することで、返される結果の量を減らします。
概要
このシステムは、Mastra と OpenAI を使用してメタデータフィルタリングを実装しています。主な機能は以下の通りです。
- gpt-4o-mini を用いた Mastra エージェントをセットアップし、クエリを理解してフィルタ要件を特定します
- メタデータフィルタリングとセマンティック検索を処理するベクトルクエリツールを作成します
- ドキュメントをメタデータと埋め込みを持つチャンクに分割して処理します
- 効率的な検索のために、ベクトルとメタデータの両方を PGVector に保存します
- メタデータフィルタとセマンティック検索を組み合わせてクエリを処理します
ユーザーが質問をするとき:
- エージェントがクエリを分析し、意図を理解します
- 適切なメタデータフィルタ(例:トピック、日付、カテゴリ別)を構築します
- ベクトルクエリツールを使って最も関連性の高い情報を見つけます
- フィルタリングされた結果に基づいてコンテキストに合った回答を生成します
セットアップ
環境設定
環境変数を必ず設定してください:
OPENAI_API_KEY=your_openai_api_key_here
POSTGRES_CONNECTION_STRING=your_connection_string_here
依存関係
次に、必要な依存関係をインポートします:
import { openai } from "@ai-sdk/openai";
import { Mastra } from "@mastra/core";
import { Agent } from "@mastra/core/agent";
import { PgVector, PGVECTOR_PROMPT } from "@mastra/pg";
import { createVectorQueryTool, MDocument } from "@mastra/rag";
import { embedMany } from "ai";
ベクタークエリツールの作成
@mastra/rag からインポートした createVectorQueryTool を使用すると、メタデータフィルタリングを可能にするツールを作成できます。各ベクターストアには、サポートされているフィルター演算子と構文を定義する独自のプロンプトがあります。
const vectorQueryTool = createVectorQueryTool({
id: "vectorQueryTool",
vectorStoreName: "pgVector",
indexName: "embeddings",
model: openai.embedding("text-embedding-3-small"),
enableFilter: true,
filterPrompt: PGVECTOR_PROMPT, // Use the prompt for your vector store
});
各プロンプトには以下が含まれます:
- サポートされている演算子(比較、配列、論理、要素)
- 各演算子の使用例
- ストア固有の制限事項やルール
- 複雑なクエリの例
ドキュメント処理
ドキュメントを作成し、メタデータ付きでチャンクに分割します:
const doc = MDocument.fromText(
`The Impact of Climate Change on Global Agriculture...`,
);
const chunks = await doc.chunk({
strategy: "recursive",
size: 512,
overlap: 50,
separator: "\n",
extract: {
keywords: true, // Extracts keywords from each chunk
},
});
チャンクをメタデータに変換する
チャンクをフィルタリング可能なメタデータに変換します:
const chunkMetadata = chunks?.map((chunk: any, index: number) => ({
text: chunk.text,
...chunk.metadata,
nested: {
keywords: chunk.metadata.excerptKeywords
.replace("KEYWORDS:", "")
.split(",")
.map((k) => k.trim()),
id: index,
},
}));
エージェントの設定
エージェントは、ユーザーのクエリを理解し、適切なメタデータフィルターに変換するように設定されています。
エージェントには、ベクトルクエリツールと、以下を含むシステムプロンプトの両方が必要です。
- 利用可能なフィルターフィールドのメタデータ構造
- フィルター操作と構文のためのベクトルストアプロンプト
export const ragAgent = new Agent({
name: "RAG Agent",
model: openai("gpt-4o-mini"),
instructions: `
You are a helpful assistant that answers questions based on the provided context. Keep your answers concise and relevant.
Filter the context by searching the metadata.
The metadata is structured as follows:
{
text: string,
excerptKeywords: string,
nested: {
keywords: string[],
id: number,
},
}
${PGVECTOR_PROMPT}
Important: When asked to answer a question, please base your answer only on the context provided in the tool.
If the context doesn't contain enough information to fully answer the question, please state that explicitly.
`,
tools: { vectorQueryTool },
});
エージェントの指示は、以下を目的としています。
- ユーザーのクエリを処理し、フィルター要件を特定する
- メタデータ構造を利用して関連情報を見つける
- vectorQueryToolと提供されたベクトルストアプロンプトを通じて適切なフィルターを適用する
- フィルターされたコンテキストに基づいて応答を生成する
注: ベクトルストアごとに利用可能なプロンプトが異なります。詳細はVector Store Promptsをご覧ください。
PgVectorとMastraのインスタンス化
コンポーネントを使用してPgVectorとMastraをインスタンス化します:
const pgVector = new PgVector({ connectionString: process.env.POSTGRES_CONNECTION_STRING! });
export const mastra = new Mastra({
agents: { ragAgent },
vectors: { pgVector },
});
const agent = mastra.getAgent("ragAgent");
埋め込みの作成と保存
埋め込みを生成し、メタデータと一緒に保存します:
const { embeddings } = await embedMany({
model: openai.embedding("text-embedding-3-small"),
values: chunks.map((chunk) => chunk.text),
});
const vectorStore = mastra.getVector("pgVector");
await vectorStore.createIndex({
indexName: "embeddings",
dimension: 1536,
});
// Store both embeddings and metadata together
await vectorStore.upsert({
indexName: "embeddings",
vectors: embeddings,
metadata: chunkMetadata,
});
upsert
操作は、ベクトル埋め込みとそれに関連付けられたメタデータの両方を保存し、セマンティック検索とメタデータフィルタリングの機能を組み合わせて利用できるようにします。
メタデータベースのクエリ
メタデータフィルターを使ってさまざまなクエリを試してみましょう:
const queryOne = "What are the adaptation strategies mentioned?";
const answerOne = await agent.generate(queryOne);
console.log("\nQuery:", queryOne);
console.log("Response:", answerOne.text);
const queryTwo =
'Show me recent sections. Check the "nested.id" field and return values that are greater than 2.';
const answerTwo = await agent.generate(queryTwo);
console.log("\nQuery:", queryTwo);
console.log("Response:", answerTwo.text);
const queryThree =
'Search the "text" field using regex operator to find sections containing "temperature".';
const answerThree = await agent.generate(queryThree);
console.log("\nQuery:", queryThree);
console.log("Response:", answerThree.text);