T-CREATOR

RAG スタック比較:ベクタ DB(Pinecone/Weaviate/pgvector)× GPT-5 の相性と指標

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 サポート★★☆
5GPT-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 の基本スペック

それぞれのベクタデータベースの基本的なスペックを表で比較してみましょう。

#項目PineconeWeaviatepgvector
1デプロイ形式SaaSSaaS / Self-hostedSelf-hosted
2ライセンスプロプライエタリBSD-3-ClausePostgreSQL
3主要言語GoGoC / SQL
4距離関数Cosine, Euclidean, DotCosine, L2, Hamming 等L2, Inner product, Cosine
5最大次元数20,00065,53616,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)142ms95 パーセンタイル
3QPS約 1,200 queries/secStandard Pod の場合
4Recall@100.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)280ms95 パーセンタイル
3QPS約 800 queries/sec標準構成
4Recall@100.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)420ms95 パーセンタイル
3QPS約 400 queries/sec単一インスタンス
4Recall@100.91IVFFlat インデックス
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 との組み合わせにおける総合評価です。

#評価項目PineconeWeaviatepgvector
1検索速度★★★★★☆★☆☆
2検索精度(Recall)★★☆★★★★★☆
3レイテンシ安定性★★★★★☆★☆☆
4トークン効率性★★☆★★★★★☆
5セットアップ容易性★★★★★☆★★★
6スケーラビリティ★★★★★☆★☆☆
7コスト効率★☆☆★★☆★★★
8総合スコア18/2117/2114/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 スタックを構築していただければ幸いです。

以下の表は、選択の指針となる簡易チェックリストです。

#選択基準PineconeWeaviatepgvector
1とにかく高速な検索が必要×
2検索精度を最優先したい
3コストを抑えたい×
4既存の PostgreSQL 環境がある××
5運用負荷を最小化したい×
6柔軟なフィルタリングが必要
7自動スケーリングが必要×

記号: ○=適している、△=条件次第、×=不向き

関連リンク

各ベクタデータベースの公式ドキュメントや関連リソースです。