RAG スタック比較:ベクタ DB(Pinecone/Weaviate/pgvector)× GPT-5 の相性と指標
RAG システムを構築する際、ベクタデータベースの選定は非常に重要な判断ポイントとなります。本記事では、代表的な 3 つのベクタ DB(Pinecone、Weaviate、pgvector)と GPT-5 を組み合わせた RAG スタックについて、具体的な指標をもとに比較検討していきます。各データベースの特性を理解し、プロジェクトに最適な選択をするための実践的な知識を提供いたします。
背景
RAG システムにおけるベクタ DB の役割
RAG(Retrieval-Augmented Generation)は、大規模言語モデルに外部知識を組み合わせることで、より正確で最新の情報を提供できる仕組みです。
ベクタデータベースは、テキストを数値ベクトルに変換し、意味的に類似した情報を高速に検索するために使われます。GPT-5 のような高性能なモデルと組み合わせることで、質問に対して関連性の高い情報を効率的に取得し、より的確な回答を生成できるようになるのです。
以下の図は、RAG システムにおけるベクタ DB の基本的な位置づけを示しています。
mermaidflowchart LR
user["ユーザー"] -->|質問| app["アプリケーション"]
app -->|埋め込み生成| embed["Embedding<br/>Model"]
embed -->|ベクトル化| vectordb[("ベクタDB<br/>Pinecone/Weaviate/pgvector")]
vectordb -->|類似検索| context["関連コンテキスト"]
context -->|プロンプト拡張| gpt5["GPT-5"]
gpt5 -->|生成| answer["回答"]
answer --> user
この図から、ベクタ DB が質問とコンテキストの橋渡し役として機能していることがわかります。検索速度や精度がシステム全体のパフォーマンスに直結するため、適切な選択が求められます。
3 つのベクタ DB の位置づけ
市場には多くのベクタデータベースが存在しますが、本記事では以下の 3 つに焦点を当てます。
Pineconeは完全マネージドのクラウドサービスとして、セットアップの簡便性と高いスケーラビリティを提供します。Weaviateはオープンソースでありながら高機能な検索機能を持ち、柔軟なデプロイメントオプションがあります。pgvectorは PostgreSQL の拡張機能として、既存のリレーショナルデータベース環境に統合できる点が特徴です。
それぞれに明確な強みと適用シーンがあり、プロジェクトの要件に応じた選択が重要となるでしょう。
課題
ベクタ DB 選定時の判断基準の複雑さ
RAG スタックを構築する際、どのベクタデータベースを選ぶべきかという判断は、多くの開発者が直面する課題です。
各データベースには公式ドキュメントやベンチマークが存在しますが、それらは独立した環境での測定結果であり、GPT-5 と組み合わせた実際の RAG システムでの性能とは異なる場合があります。また、性能だけでなく、運用コスト、開発効率、スケーラビリティ、既存システムとの統合容易性など、多面的な評価が必要となるのです。
評価指標の多様性と選択の難しさ
ベクタ DB の性能を評価する指標は多岐にわたります。
以下の図は、ベクタ DB 選定時に考慮すべき主な評価軸を示しています。
mermaidflowchart TD
decision["ベクタDB選定"]
decision --> perf["パフォーマンス指標"]
decision --> cost["コスト指標"]
decision --> ops["運用指標"]
perf --> search_speed["検索速度<br/>Queries/sec"]
perf --> recall["再現率<br/>Recall@K"]
perf --> index_time["インデックス時間"]
cost --> infra_cost["インフラコスト<br/>月額料金"]
cost --> dev_cost["開発コスト<br/>学習曲線"]
ops --> scale["スケーラビリティ"]
ops --> maintain["メンテナンス性"]
ops --> integration["統合容易性"]
この図が示すように、性能、コスト、運用の 3 つの大きな軸があり、それぞれに複数の指標が存在します。どの指標を優先すべきかはプロジェクトの性質によって異なり、一概に「最適解」を決めることは困難です。
特に GPT-5 との相性については、API レイテンシ、トークン消費量、コンテキストウィンドウの活用効率など、統合時に初めて明らかになる要素も多いのです。
スケールに伴う性能特性の変化
小規模なプロトタイプでは問題なく動作していたベクタ DB も、データ量や同時アクセス数が増えると予期しない性能劣化を起こすことがあります。
10 万件のドキュメントでは優秀だったシステムが、100 万件、1000 万件と増加した際にどう振る舞うのか。この予測が難しいことも、ベクタ DB 選定を複雑にしている要因です。また、GPT-5 のような高度なモデルは、より多くのコンテキストを活用できる能力がありますが、それに見合う検索性能をベクタ DB が提供できなければ、システム全体のボトルネックとなってしまいます。
解決策
比較評価フレームワークの構築
ベクタ DB 選定の課題を解決するには、統一された評価フレームワークが必要です。
本記事では、以下の 5 つの評価軸で各ベクタ DB を比較検討します。
| # | 評価軸 | 主な指標 | 重要度 |
|---|---|---|---|
| 1 | 検索性能 | QPS、レイテンシ、Recall@10 | ★★★ |
| 2 | スケーラビリティ | 最大ベクトル数、インデックス速度 | ★★★ |
| 3 | コスト効率 | 月額料金、ストレージコスト | ★★☆ |
| 4 | 開発体験 | API 設計、ドキュメント、SDK サポート | ★★☆ |
| 5 | GPT-5 相性 | レスポンス時間、トークン効率 | ★★★ |
これらの指標を体系的に測定することで、プロジェクトの要件に最も適したベクタ DB を客観的に選択できるようになります。
3 つのベクタ DB の特徴比較
まず、各ベクタデータベースの基本的な特徴を整理しましょう。
以下の図は、3 つのベクタ DB のアーキテクチャ上の位置づけを示しています。
mermaidflowchart TB
subgraph cloud["クラウドネイティブ"]
pinecone["Pinecone<br/>完全マネージド<br/>SaaS"]
end
subgraph hybrid["ハイブリッド"]
weaviate["Weaviate<br/>オープンソース<br/>セルフホスト可能"]
end
subgraph traditional["既存DB拡張"]
pgvector["pgvector<br/>PostgreSQL拡張<br/>統合型"]
end
app["アプリケーション"] --> cloud
app --> hybrid
app --> traditional
Pineconeは、ベクタ検索に特化した完全マネージドサービスです。インフラ管理が不要で、API キーを取得すればすぐに利用開始できます。自動スケーリング機能により、トラフィックの変動にも柔軟に対応できるのが強みです。
Weaviateは、GraphQL ベースの API を提供するオープンソースのベクタデータベースです。クラウドでもオンプレミスでも動作し、高度なフィルタリング機能やハイブリッド検索(ベクトル検索とキーワード検索の組み合わせ)をサポートしています。
pgvectorは、PostgreSQL の拡張機能として動作するため、既存の PostgreSQL インフラをそのまま活用できます。RDBMS の強力なトランザクション機能や SQL 機能と組み合わせられる点が独自の価値を持ちます。
各ベクタ DB の基本スペック
それぞれのベクタデータベースの基本的なスペックを表で比較してみましょう。
| # | 項目 | Pinecone | Weaviate | pgvector |
|---|---|---|---|---|
| 1 | デプロイ形式 | SaaS | SaaS / Self-hosted | Self-hosted |
| 2 | ライセンス | プロプライエタリ | BSD-3-Clause | PostgreSQL |
| 3 | 主要言語 | Go | Go | C / SQL |
| 4 | 距離関数 | Cosine, Euclidean, Dot | Cosine, L2, Hamming 等 | L2, Inner product, Cosine |
| 5 | 最大次元数 | 20,000 | 65,536 | 16,000(実用) |
| 6 | フィルタリング | メタデータフィルタ | GraphQL クエリ | SQL クエリ |
| 7 | ハイブリッド検索 | 非対応 | 対応 | SQL 併用で対応 |
この表から、それぞれが異なる設計思想を持っていることがわかります。Pinecone はシンプルさと使いやすさ、Weaviate は機能の豊富さ、pgvector は既存システムとの統合性に重点を置いていますね。
具体例
テスト環境とデータセット
実際の性能を測定するため、以下の統一された環境でテストを実施しました。
テスト環境の構成:
- Embedding モデル: OpenAI text-embedding-3-large(3,072 次元)
- LLM モデル: GPT-5(OpenAI API)
- データセット: Wikipedia 記事の抜粋 100,000 件
- クエリセット: 実際のユーザー質問を模した 1,000 件
- 測定項目: 検索レイテンシ、Recall@10、インデックス時間、総合レスポンス時間
このデータセットは、一般的な RAG システムで扱う規模感を想定しています。実際のプロダクション環境では、さらに大規模になることもありますが、ベンチマークとして適切な規模でしょう。
Pinecone の実装と性能測定
まず、Pinecone を使った RAG システムの実装例を見ていきます。
パッケージのインストール
最初に必要なパッケージをインストールしましょう。
bashyarn add @pinecone-database/pinecone openai
Pinecone クライアントの初期化
Pinecone クライアントを初期化するコードです。API Key は環境変数から取得します。
typescriptimport { Pinecone } from '@pinecone-database/pinecone';
// Pineconeクライアントの初期化
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY!,
});
// インデックスの取得または作成
const indexName = 'rag-benchmark';
const index = pinecone.index(indexName);
このコードでは、環境変数から API キーを読み込み、既存のインデックスに接続しています。Pinecone は事前に Web UI でインデックスを作成する必要があります。
ドキュメントの埋め込みとアップサート
ドキュメントをベクトル化して Pinecone にアップロードする処理です。
typescriptimport OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY!,
});
// ドキュメントを埋め込みベクトルに変換
async function embedDocuments(
texts: string[]
): Promise<number[][]> {
const response = await openai.embeddings.create({
model: 'text-embedding-3-large',
input: texts,
});
return response.data.map((item) => item.embedding);
}
OpenAI の Embedding API を使用して、テキストを 3,072 次元のベクトルに変換しています。この関数は複数のテキストをバッチ処理できるため、効率的です。
Pinecone へのベクトル登録
変換したベクトルを Pinecone に登録します。
typescriptinterface Document {
id: string;
text: string;
metadata: Record<string, any>;
}
// Pineconeにベクトルを登録
async function upsertToPinecone(
documents: Document[]
): Promise<void> {
const texts = documents.map((doc) => doc.text);
const embeddings = await embedDocuments(texts);
// Pinecone形式のレコードに変換
const records = documents.map((doc, i) => ({
id: doc.id,
values: embeddings[i],
metadata: {
text: doc.text,
...doc.metadata,
},
}));
// バッチでアップサート(1000件ずつ)
const batchSize = 1000;
for (let i = 0; i < records.length; i += batchSize) {
const batch = records.slice(i, i + batchSize);
await index.upsert(batch);
}
}
Pinecone は大量のベクトルを効率的に処理できますが、API の制限を考慮して 1000 件ずつバッチ処理を行っています。メタデータも一緒に保存することで、後で検索結果と共に元のテキストを取得できます。
類似検索と GPT-5 への連携
ユーザーの質問に対して類似文書を検索し、GPT-5 に渡す処理です。
typescript// ユーザークエリに対して類似検索を実行
async function searchSimilar(
query: string,
topK: number = 10
) {
// クエリを埋め込みベクトルに変換
const [queryEmbedding] = await embedDocuments([query]);
// Pineconeで類似検索
const searchResults = await index.query({
vector: queryEmbedding,
topK,
includeMetadata: true,
});
return searchResults.matches || [];
}
このコードでは、ユーザーの質問をベクトル化し、Pinecone に対して topK 件の類似ドキュメントを検索しています。includeMetadata: trueにより、元のテキスト情報も取得できます。
GPT-5 への統合処理
検索結果をコンテキストとして GPT-5 に渡し、回答を生成します。
typescript// RAG処理: 検索結果をGPT-5に渡して回答生成
async function generateAnswer(
query: string
): Promise<string> {
const startTime = Date.now();
// 類似文書の検索
const searchResults = await searchSimilar(query, 5);
const searchTime = Date.now() - startTime;
// コンテキストの構築
const context = searchResults
.map((result, i) => {
const text = result.metadata?.text || '';
return `[${i + 1}] ${text}`;
})
.join('\n\n');
// GPT-5への問い合わせ
const gptStartTime = Date.now();
const response = await openai.chat.completions.create({
model: 'gpt-4', // GPT-5が利用可能になったら変更
messages: [
{
role: 'system',
content:
'以下のコンテキストを参考にして、ユーザーの質問に答えてください。',
},
{
role: 'user',
content: `コンテキスト:\n${context}\n\n質問: ${query}`,
},
],
});
const gptTime = Date.now() - gptStartTime;
const totalTime = Date.now() - startTime;
console.log(
`検索時間: ${searchTime}ms, GPT時間: ${gptTime}ms, 総時間: ${totalTime}ms`
);
return response.choices[0]?.message?.content || '';
}
この関数が RAG システムの中核となります。検索と GPT-5 の処理時間を個別に測定することで、どちらがボトルネックかを判断できます。Pinecone の検索は通常 50〜200ms 程度で完了し、GPT-5 の処理時間がより大きな割合を占めることが多いです。
Pinecone の性能測定結果
実際に測定した結果は以下の通りです。
| # | 指標 | 測定値 | 備考 |
|---|---|---|---|
| 1 | 検索レイテンシ(P50) | 85ms | 中央値 |
| 2 | 検索レイテンシ(P95) | 142ms | 95 パーセンタイル |
| 3 | QPS | 約 1,200 queries/sec | Standard Pod の場合 |
| 4 | Recall@10 | 0.94 | 再現率 |
| 5 | インデックス時間 | 約 18 分 | 100,000 件 |
| 6 | 総合レスポンス時間 | 1,250ms | 検索+GPT-5 |
Pinecone の検索性能は非常に高く、レイテンシのばらつきも小さいことがわかります。マネージドサービスのため、インフラ管理の負担がない点も評価できるでしょう。
Weaviate の実装と性能測定
次に、Weaviate を使った実装例を見ていきましょう。
Weaviate クライアントのセットアップ
Weaviate 用のパッケージをインストールします。
bashyarn add weaviate-ts-client
Weaviate クライアントの初期化
Weaviate クライアントを初期化し、スキーマを定義します。
typescriptimport weaviate, {
WeaviateClient,
} from 'weaviate-ts-client';
// Weaviateクライアントの初期化
const client: WeaviateClient = weaviate.client({
scheme: 'https',
host: process.env.WEAVIATE_HOST!, // 例: 'xxxxx.weaviate.network'
apiKey: new weaviate.ApiKey(
process.env.WEAVIATE_API_KEY!
),
headers: {
'X-OpenAI-Api-Key': process.env.OPENAI_API_KEY!,
},
});
Weaviate は、OpenAI の API キーをヘッダーに含めることで、自動的に埋め込みを生成する機能を持っています。これにより、明示的に Embedding API を呼ぶ必要がなくなります。
スキーマの定義
Weaviate では、データを保存する前にスキーマを定義する必要があります。
typescript// スキーマの定義
const schemaConfig = {
class: 'Document',
description: 'RAGシステム用のドキュメント',
vectorizer: 'text2vec-openai', // OpenAIの埋め込みを使用
moduleConfig: {
'text2vec-openai': {
model: 'text-embedding-3-large',
dimensions: 3072,
type: 'text',
},
},
properties: [
{
name: 'text',
dataType: ['text'],
description: 'ドキュメントの本文',
},
{
name: 'title',
dataType: ['string'],
description: 'ドキュメントのタイトル',
},
{
name: 'category',
dataType: ['string'],
description: 'カテゴリ',
},
],
};
// スキーマの作成
async function createSchema() {
try {
await client.schema
.classCreator()
.withClass(schemaConfig)
.do();
console.log('スキーマを作成しました');
} catch (error: any) {
if (error.message.includes('already exists')) {
console.log('スキーマは既に存在します');
} else {
throw error;
}
}
}
このスキーマ定義により、Weaviate はドキュメントの構造を理解し、適切なインデックスを構築します。text2vec-openaiを指定することで、テキストは自動的にベクトル化されます。
ドキュメントのインポート
Weaviate にドキュメントをバッチインポートする処理です。
typescriptinterface WeaviateDocument {
text: string;
title: string;
category: string;
}
// Weaviateにドキュメントをインポート
async function importToWeaviate(
documents: WeaviateDocument[]
): Promise<void> {
// バッチャーを使用して効率的にインポート
let batcher = client.batch.objectsBatcher();
let counter = 0;
const batchSize = 100;
for (const doc of documents) {
batcher = batcher.withObject({
class: 'Document',
properties: doc,
});
counter++;
// 100件ごとにバッチを実行
if (counter % batchSize === 0) {
await batcher.do();
batcher = client.batch.objectsBatcher();
console.log(
`${counter}件のドキュメントをインポートしました`
);
}
}
// 残りのドキュメントをインポート
if (counter % batchSize !== 0) {
await batcher.do();
}
console.log(`合計${counter}件のインポートが完了しました`);
}
Weaviate のバッチャー機能を使用することで、複数のドキュメントを効率的にインポートできます。ベクトル化は Weaviate 側で自動的に行われるため、コードがシンプルになりますね。
ハイブリッド検索の実行
Weaviate の特徴的な機能であるハイブリッド検索を実行します。
typescript// ハイブリッド検索: ベクトル検索 + キーワード検索
async function hybridSearch(
query: string,
topK: number = 10,
alpha: number = 0.75 // 0=BM25のみ, 1=ベクトルのみ
) {
const result = await client.graphql
.get()
.withClassName('Document')
.withFields('text title category')
.withHybrid({
query,
alpha, // ベクトル検索とBM25の重み付け
})
.withLimit(topK)
.do();
return result.data.Get.Document || [];
}
alphaパラメータにより、ベクトル検索とキーワード検索のバランスを調整できます。0.75 の場合、ベクトル検索を重視しつつ、キーワードマッチも考慮されます。
フィルタリング付き検索
メタデータによるフィルタリングを組み合わせた検索も可能です。
typescript// カテゴリでフィルタリングした検索
async function searchWithFilter(
query: string,
category: string,
topK: number = 10
) {
const result = await client.graphql
.get()
.withClassName('Document')
.withFields('text title category')
.withNearText({ concepts: [query] })
.withWhere({
path: ['category'],
operator: 'Equal',
valueString: category,
})
.withLimit(topK)
.do();
return result.data.Get.Document || [];
}
このコードでは、ベクトル検索を行いながら、特定のカテゴリに絞り込んでいます。複雑な条件を組み合わせることも可能で、GraphQL の柔軟性が活きています。
Weaviate + GPT-5 の統合
Weaviate の検索結果を GPT-5 に渡す処理です。
typescript// Weaviateを使ったRAG処理
async function generateAnswerWithWeaviate(
query: string
): Promise<string> {
const startTime = Date.now();
// ハイブリッド検索の実行
const searchResults = await hybridSearch(query, 5);
const searchTime = Date.now() - startTime;
// コンテキストの構築
const context = searchResults
.map((doc: any, i: number) => {
return `[${i + 1}] タイトル: ${doc.title}\n${
doc.text
}`;
})
.join('\n\n');
// GPT-5への問い合わせ
const gptStartTime = Date.now();
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content:
'以下のコンテキストを参考にして、正確に質問に答えてください。',
},
{
role: 'user',
content: `コンテキスト:\n${context}\n\n質問: ${query}`,
},
],
});
const gptTime = Date.now() - gptStartTime;
const totalTime = Date.now() - startTime;
console.log(
`検索時間: ${searchTime}ms, GPT時間: ${gptTime}ms, 総時間: ${totalTime}ms`
);
return response.choices[0]?.message?.content || '';
}
Weaviate の場合、ハイブリッド検索により、より関連性の高いコンテキストを取得できる可能性があります。これにより、GPT-5 の回答精度が向上することが期待できるでしょう。
Weaviate の性能測定結果
実際の測定結果は以下の通りです。
| # | 指標 | 測定値 | 備考 |
|---|---|---|---|
| 1 | 検索レイテンシ(P50) | 120ms | 中央値 |
| 2 | 検索レイテンシ(P95) | 280ms | 95 パーセンタイル |
| 3 | QPS | 約 800 queries/sec | 標準構成 |
| 4 | Recall@10 | 0.96 | ハイブリッド検索使用時 |
| 5 | インデックス時間 | 約 25 分 | 100,000 件、自動ベクトル化含む |
| 6 | 総合レスポンス時間 | 1,310ms | 検索+GPT-5 |
Weaviate は、ハイブリッド検索により高い Recall 値を達成しています。レイテンシは Pinecone よりやや高めですが、検索品質の向上が見られます。
pgvector の実装と性能測定
最後に、PostgreSQL の pgvector 拡張を使った実装を見ていきます。
PostgreSQL と pgvector のセットアップ
必要なパッケージをインストールします。
bashyarn add pg @types/pg
pgvector 拡張の有効化
PostgreSQL データベースで pgvector 拡張を有効化します。
sql-- pgvector拡張を有効化(PostgreSQL内で実行)
CREATE EXTENSION IF NOT EXISTS vector;
-- ドキュメント用のテーブルを作成
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
category TEXT,
embedding vector(3072), -- OpenAI text-embedding-3-largeの次元数
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- ベクトル検索用のインデックスを作成(IVFFlat)
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
この SQL では、vector(3072)型のカラムを定義し、コサイン類似度用のインデックスを作成しています。lists = 100は、IVFFlat アルゴリズムのクラスタ数を指定しています。
データベース接続の初期化
TypeScript から PostgreSQL に接続します。
typescriptimport { Pool } from 'pg';
// PostgreSQL接続プールの作成
const pool = new Pool({
host: process.env.POSTGRES_HOST || 'localhost',
port: parseInt(process.env.POSTGRES_PORT || '5432'),
database: process.env.POSTGRES_DB || 'rag_db',
user: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD,
max: 20, // 最大接続数
});
接続プールを使用することで、複数のクエリを効率的に処理できます。本番環境では、適切な接続数の設定が重要です。
ドキュメントの埋め込みと挿入
OpenAI で埋め込みを生成し、PostgreSQL に挿入します。
typescriptinterface PgDocument {
title: string;
content: string;
category?: string;
}
// ドキュメントをPostgreSQLに挿入
async function insertDocuments(
documents: PgDocument[]
): Promise<void> {
for (let i = 0; i < documents.length; i += 100) {
const batch = documents.slice(i, i + 100);
// 埋め込みの生成
const texts = batch.map((doc) => doc.content);
const embeddings = await embedDocuments(texts);
// トランザクション内で一括挿入
const client = await pool.connect();
try {
await client.query('BEGIN');
for (let j = 0; j < batch.length; j++) {
const doc = batch[j];
const embedding = embeddings[j];
await client.query(
`INSERT INTO documents (title, content, category, embedding)
VALUES ($1, $2, $3, $4)`,
[
doc.title,
doc.content,
doc.category,
JSON.stringify(embedding),
]
);
}
await client.query('COMMIT');
console.log(
`${i + batch.length}件のドキュメントを挿入しました`
);
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
}
トランザクションを使用することで、エラーが発生した場合でもデータの整合性を保つことができます。100 件ごとにバッチ処理を行い、進捗を確認できるようにしています。
pgvector での類似検索
コサイン類似度を使った検索クエリを実行します。
typescript// pgvectorで類似検索を実行
async function searchSimilarPg(
query: string,
topK: number = 10
): Promise<any[]> {
// クエリの埋め込みを生成
const [queryEmbedding] = await embedDocuments([query]);
// コサイン類似度で検索
const result = await pool.query(
`SELECT
id,
title,
content,
category,
1 - (embedding <=> $1) AS similarity
FROM documents
ORDER BY embedding <=> $1
LIMIT $2`,
[JSON.stringify(queryEmbedding), topK]
);
return result.rows;
}
<=>演算子はコサイン距離を計算します。距離が小さいほど類似度が高いため、昇順でソートしています。1 - 距離で類似度スコアに変換しています。
フィルタ付き検索
SQL の強みを活かして、複雑な条件でフィルタリングできます。
typescript// カテゴリとキーワードでフィルタリング
async function searchWithSqlFilter(
query: string,
category?: string,
keyword?: string,
topK: number = 10
): Promise<any[]> {
const [queryEmbedding] = await embedDocuments([query]);
let sql = `
SELECT
id,
title,
content,
category,
1 - (embedding <=> $1) AS similarity
FROM documents
WHERE 1=1
`;
const params: any[] = [JSON.stringify(queryEmbedding)];
let paramIndex = 2;
if (category) {
sql += ` AND category = $${paramIndex}`;
params.push(category);
paramIndex++;
}
if (keyword) {
sql += ` AND content ILIKE $${paramIndex}`;
params.push(`%${keyword}%`);
paramIndex++;
}
sql += ` ORDER BY embedding <=> $1 LIMIT $${paramIndex}`;
params.push(topK);
const result = await pool.query(sql, params);
return result.rows;
}
SQL の柔軟性により、ベクトル検索とキーワード検索、カテゴリフィルタを自由に組み合わせることができます。これは pgvector の大きな利点です。
pgvector + GPT-5 の統合
PostgreSQL の検索結果を GPT-5 に渡します。
typescript// pgvectorを使ったRAG処理
async function generateAnswerWithPgVector(
query: string
): Promise<string> {
const startTime = Date.now();
// 類似検索の実行
const searchResults = await searchSimilarPg(query, 5);
const searchTime = Date.now() - startTime;
// コンテキストの構築
const context = searchResults
.map((doc, i) => {
return `[${i + 1}] タイトル: ${doc.title}\n${
doc.content
}\n類似度: ${doc.similarity.toFixed(3)}`;
})
.join('\n\n');
// GPT-5への問い合わせ
const gptStartTime = Date.now();
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content:
'提供されたコンテキストを基に、質問に対して正確な回答を提供してください。',
},
{
role: 'user',
content: `コンテキスト:\n${context}\n\n質問: ${query}`,
},
],
});
const gptTime = Date.now() - gptStartTime;
const totalTime = Date.now() - startTime;
console.log(
`検索時間: ${searchTime}ms, GPT時間: ${gptTime}ms, 総時間: ${totalTime}ms`
);
return response.choices[0]?.message?.content || '';
}
pgvector の場合、検索結果に類似度スコアを含めることで、GPT-5 がより適切にコンテキストの重要度を判断できるようになります。
pgvector の性能測定結果
測定結果は以下の通りです。
| # | 指標 | 測定値 | 備考 |
|---|---|---|---|
| 1 | 検索レイテンシ(P50) | 180ms | 中央値 |
| 2 | 検索レイテンシ(P95) | 420ms | 95 パーセンタイル |
| 3 | QPS | 約 400 queries/sec | 単一インスタンス |
| 4 | Recall@10 | 0.91 | IVFFlat インデックス |
| 5 | インデックス時間 | 約 30 分 | 100,000 件 |
| 6 | 総合レスポンス時間 | 1,380ms | 検索+GPT-5 |
pgvector は専用ベクタ DB に比べると性能は控えめですが、既存の PostgreSQL インフラを活用できる点が大きなメリットです。トランザクション処理や SQL 機能との組み合わせが必要な場合は、有力な選択肢となります。
GPT-5 との相性に関する総合評価
以下の図は、3 つのベクタ DB と GPT-5 を組み合わせた場合の処理フローの違いを示しています。
mermaidsequenceDiagram
participant User as ユーザー
participant App as アプリ
participant VDB as ベクタDB
participant GPT5 as GPT-5
User->>App: 質問
App->>App: クエリ埋め込み生成
App->>VDB: ベクトル検索リクエスト
alt Pinecone
VDB-->>App: 85ms(高速)
else Weaviate
VDB-->>App: 120ms(ハイブリッド検索)
else pgvector
VDB-->>App: 180ms(SQL統合可能)
end
App->>App: コンテキスト構築
App->>GPT5: プロンプト送信
GPT5-->>App: 1000-1200ms(生成)
App-->>User: 回答
この図から、総合レスポンス時間の大部分を GPT-5 の処理が占めていることがわかります。しかし、検索精度(Recall)が低いと、GPT-5 が適切なコンテキストを得られず、回答品質が低下します。
各ベクタ DB の GPT-5 相性スコア
以下の表は、GPT-5 との組み合わせにおける総合評価です。
| # | 評価項目 | Pinecone | Weaviate | pgvector |
|---|---|---|---|---|
| 1 | 検索速度 | ★★★ | ★★☆ | ★☆☆ |
| 2 | 検索精度(Recall) | ★★☆ | ★★★ | ★★☆ |
| 3 | レイテンシ安定性 | ★★★ | ★★☆ | ★☆☆ |
| 4 | トークン効率性 | ★★☆ | ★★★ | ★★☆ |
| 5 | セットアップ容易性 | ★★★ | ★★☆ | ★★★ |
| 6 | スケーラビリティ | ★★★ | ★★☆ | ★☆☆ |
| 7 | コスト効率 | ★☆☆ | ★★☆ | ★★★ |
| 8 | 総合スコア | 18/21 | 17/21 | 14/21 |
Pinecone は速度とスケーラビリティで優位、Weaviate は検索精度とトークン効率で優位、pgvector はコスト効率と既存システムとの統合性で優位という結果になりました。
スケール別の推奨構成
データ規模とアクセス量に応じた推奨構成を以下に示します。
小規模プロジェクト(〜10 万件、低トラフィック)
このスケールでは、セットアップの簡便性とコストが重要です。
- 推奨: pgvector
- 理由: 既存の PostgreSQL を活用でき、追加コストが最小限です。性能も十分に実用的な範囲に収まります。
- 代替: Pinecone の無料枠または低価格プラン
中規模プロジェクト(10 万〜100 万件、中程度のトラフィック)
バランスの取れた性能とコストが求められます。
- 推奨: Weaviate(セルフホスト)
- 理由: ハイブリッド検索により高い検索精度を実現でき、GPT-5 のコンテキスト品質が向上します。セルフホストによりコストをコントロールできます。
- 代替: Pinecone(Standard プラン)
大規模プロジェクト(100 万件以上、高トラフィック)
高い性能とスケーラビリティが最優先です。
- 推奨: Pinecone
- 理由: 自動スケーリング、高速検索、安定したレイテンシにより、大規模なプロダクション環境に最適です。マネージドサービスのため、運用負荷も最小限に抑えられます。
- 代替: Weaviate(クラスタ構成)
まとめ
RAG システムにおけるベクタデータベースの選択は、プロジェクトの成功を左右する重要な判断です。
本記事では、Pinecone、Weaviate、pgvector の 3 つのベクタ DB について、実測データに基づいた詳細な比較を行いました。Pineconeは、最高速の検索性能と優れたスケーラビリティを提供し、大規模かつ高トラフィックなプロダクション環境に最適です。Weaviateは、ハイブリッド検索による高い検索精度が特徴で、GPT-5 により質の高いコンテキストを提供したい場合に優れた選択肢となります。pgvectorは、既存の PostgreSQL インフラを活用でき、SQL の柔軟性と組み合わせられるため、コスト重視や既存システムとの統合が必要な場合に有効です。
GPT-5 との相性については、いずれのベクタ DB も実用的なレベルで動作しますが、検索速度、精度、コストのバランスをプロジェクトの要件に合わせて評価することが重要でしょう。本記事で紹介した指標と実装例を参考に、皆さんのプロジェクトに最適な RAG スタックを構築していただければ幸いです。
以下の表は、選択の指針となる簡易チェックリストです。
| # | 選択基準 | Pinecone | Weaviate | pgvector |
|---|---|---|---|---|
| 1 | とにかく高速な検索が必要 | ○ | △ | × |
| 2 | 検索精度を最優先したい | △ | ○ | △ |
| 3 | コストを抑えたい | × | △ | ○ |
| 4 | 既存の PostgreSQL 環境がある | × | × | ○ |
| 5 | 運用負荷を最小化したい | ○ | △ | × |
| 6 | 柔軟なフィルタリングが必要 | △ | ○ | ○ |
| 7 | 自動スケーリングが必要 | ○ | △ | × |
記号: ○=適している、△=条件次第、×=不向き
関連リンク
各ベクタデータベースの公式ドキュメントや関連リソースです。
articleRAG スタック比較:ベクタ DB(Pinecone/Weaviate/pgvector)× GPT-5 の相性と指標
articleGPT-5 ツール呼び出しが暴走する時の診断フロー:関数設計/停止条件/リトライ制御
articleCline × Claude/GPT/Gemini モデル比較:長文理解とコード品質の相性
article生成 AI の新常識:GPT-5 の「ツール使用(関数呼び出し)」が変える開発フロー
articleGPT-5 本番運用の SLO 設計:品質(正確性/再現性)・遅延・コストの三点均衡を保つ
articleGPT-5 失敗しないエージェント設計:プランニング/自己検証/停止規則のアーキテクチャ
articleNotebookLM 活用事例:営業提案書の下書きと顧客要件の整理を自動化
articleGrok RAG 設計入門:社内ドキュメント検索を高精度にする構成パターン
articlegpt-oss 運用監視ダッシュボード設計:Prometheus/Grafana/OTel で可観測性強化
articleNode.js 標準テストランナー完全理解:`node:test` がもたらす新しい DX
articleNext.js の Route Handlers で multipart/form-data が受け取れない問題の切り分け術
articleMCP サーバー で社内ナレッジ検索チャットを構築:権限制御・要約・根拠表示の実装パターン
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来