2026年2月13日
RAG
RAG実装におけるベクトルインデックス:HNSWとIVFFlatの比較と活用ガイド
RAG(Retrieval-Augmented Generation)を本番環境で運用する際、ベクトル検索のパフォーマンスを左右するのがインデックスの選択です。現在、主要なベクトルインデックスとして

RAGの概要
RAGとは
RAG(Retrieval-Augmented Generation)は、LLM(大規模言語モデル)に外部知識を与えて回答精度を向上させるアーキテクチャです。LLM単体では学習データに含まれない情報には回答できませんが、RAGを使うことで社内ドキュメントやFAQなど独自のデータを検索・参照した上で回答を生成できます。
RAGの処理フロー
RAGは大きく2つのフェーズに分かれます。
インデックス構築フェーズ(事前処理)
- ドキュメントをチャンク(断片)に分割する
- 各チャンクをEmbeddingモデルでベクトル化する
- ベクトルをデータベースに格納し、インデックスを構築する
検索・生成フェーズ(リアルタイム)
- ユーザーの質問をベクトル化する
- インデックスを使って類似度の高いチャンクを検索する
- 取得したチャンクをコンテキストとしてLLMに渡し、回答を生成する
この「2. インデックスを使って類似度の高いチャンクを検索する」部分で、HNSWやIVFFlatといったインデックスの選択がパフォーマンスに直結します。
なぜインデックスが必要なのか
ベクトル検索の最も単純な方法は**総当たり検索(Flat Search)**です。全ベクトルと順番にコサイン類似度を計算し、最も近いものを返します。正確ですが、データ量が増えると計算量がO(N)で増大し、実用的な速度を維持できなくなります。
近似最近傍探索(ANN: Approximate Nearest Neighbor)インデックスを使うことで、多少の精度を犠牲にしつつ、桁違いの速度で検索が可能になります。
ベクトル検索の主要パラメータ
インデックスの比較に入る前に、ベクトル検索で共通して理解すべきパラメータを整理します。
距離関数
ベクトル間の「近さ」を測る関数です。RAGではコサイン類似度が最も一般的です。
| 距離関数 | 概要 | 主な用途 |
|---|---|---|
| コサイン類似度 | ベクトルの方向の近さ(-1〜1) | テキスト埋め込み全般 |
| L2距離(ユークリッド) | ベクトル間の直線距離 | 画像、音声 |
| 内積 | 正規化済みベクトルで有効 | 一部のEmbeddingモデル |
pgvectorでは、それぞれ vector_cosine_ops、vector_l2_ops、vector_ip_ops としてインデックスに指定します。
Embeddingモデルと次元数
Embeddingモデルによって出力ベクトルの次元数が異なり、インデックスのメモリ使用量と検索速度に影響します。
| モデル | 次元数 | 特徴 |
|---|---|---|
| text-embedding-ada-002 (OpenAI) | 1536 | 英語に強い、API依存 |
| text-embedding-3-small (OpenAI) | 1536 | ada-002の後継 |
| multilingual-e5-large-instruct | 1024 | 多言語対応、オープンソース |
| nomic-embed-text | 768 | 軽量、ローカル実行可能 |
| embeddinggemma (Google) | 768 | 日本語対応、ローカル実行可能 |
次元数が大きいほどメモリ消費が増えます。エッジサーバーと組み合わせる場合は768〜1024次元のモデルが実用的です。
similarity_threshold(類似度閾値)
検索結果のうち、一定以上の類似度を持つチャンクだけを採用するためのフィルタです。この値は下限値として機能し、閾値未満のチャンクは回答生成に使用されません。
重要なのは、最適な閾値はEmbeddingモデルによって大きく異なるという点です。
| モデル | 推奨閾値の目安 | スコア分布の傾向 |
|---|---|---|
| embeddinggemma | 0.20〜0.30 | スコアが低めに分布 |
| nomic-embed-text | 0.40〜0.50 | 中程度に分布 |
| multilingual-e5-large | 0.70〜0.85 | スコアが高めに集中 |
固定値をハードコードするのではなく、フロントエンドやデータベースの設定値から動的に変更できる設計が望ましいです。
top_k
類似度順にソートした上位何件を返すかの指定です。一般的にはk=3〜10程度で、LLMのコンテキスト長とのバランスで調整します。チャンクサイズが小さければkを大きめに、チャンクサイズが大きければkを小さめにするのが基本です。
HNSWとIVFFlatの比較
HNSWとは
HNSW(Hierarchical Navigable Small World)は、グラフベースの近似最近傍探索アルゴリズムです。データを多層のグラフ構造として管理し、上位層から下位層へ「飛び石」のようにナビゲーションしながら最近傍を探索します。
仕組み:
- 各ベクトルをグラフのノードとして挿入
- 複数の階層を構築(上位層は粗く、下位層は密に接続)
- 検索時は最上位層のエントリポイントから開始し、層を降りながら近傍を絞り込む
主要パラメータ:
| パラメータ | デフォルト値 | 説明 |
|---|---|---|
m | 16 | 各ノードの最大接続数。大きいほど精度向上、メモリ増 |
ef_construction | 64 | インデックス構築時の探索幅。大きいほど構築が遅く精度向上 |
ef_search | 40 | 検索時の探索幅。大きいほど検索が遅く精度向上 |
多くの場合、デフォルト値で十分な精度が得られます。チューニングはデータ量や精度要件に応じて後から行えばよく、初期段階でこれらを変更する必要はほとんどありません。
IVFFlatとは
IVFFlat(Inverted File with Flat Quantization)は、クラスタリングベースの近似最近傍探索アルゴリズムです。データをk-meansで複数のクラスタ(リスト)に分割し、検索時は関連するクラスタのみを走査します。
仕組み:
- k-meansでベクトルを
lists個のクラスタに分割 - 各ベクトルを最も近いクラスタに割り当て
- 検索時は、クエリベクトルに最も近い
probes個のクラスタのみを走査
主要パラメータ:
| パラメータ | 推奨値 | 説明 |
|---|---|---|
lists | 行数に依存(後述) | クラスタ数。データ量に応じて設定が必要 |
probes | 1(デフォルト) | 検索時に走査するクラスタ数。精度に直結 |
listsの推奨値はデータ量に依存します:
- 100万行未満:
rows / 1000 - 100万行以上:
sqrt(rows)
比較表
| 項目 | HNSW | IVFFlat |
|---|---|---|
| アルゴリズム | グラフベース | クラスタリングベース |
| 検索精度(Recall) | 非常に高い(95%以上) | 中〜高(probes依存) |
| 検索速度 | 高速 | 高速(probes次第) |
| インデックス構築速度 | 遅い | 速い |
| メモリ使用量 | 多い(グラフ構造を保持) | 少ない |
| 空テーブルへの構築 | 可能 | 非推奨(データが必要) |
| データ追加時 | 即時反映、リバランス不要 | クラスタ偏りが発生しうる |
| パラメータ調整 | デフォルトで高精度 | lists/probesの適切な設定が必要 |
| フィルタ付き検索 | iterative_scan対応(v0.8.0+) | probes増で対応 |
| 適したデータ規模 | 数千〜数百万件 | 数十万〜数千万件 |
HNSWを選ぶべきケース
- 新規プロジェクトで迷っている → 現在の標準推奨はHNSW
- データが随時追加される → 再インデックス不要
- マルチテナント(ユーザーごとにフィルタ) → iterative_scanとの相性が良い
- 運用コストを最小化したい → パラメータ調整がほぼ不要
- テーブル作成時にインデックスも構築したい → 空テーブルでもOK
IVFFlatを選ぶべきケース
- ベクトルが数百万件以上 → メモリ制約が厳しい環境
- インデックス構築の速度を優先 → 大量データの初期ロード時
- メモリが限られたサーバー → グラフ構造を持たない分軽量
ライブラリ・データベースごとの搭載状況
ベクトル検索を提供するライブラリとデータベースは複数ありますが、それぞれ対応するインデックスが異なります。
主要ライブラリの比較
| ライブラリ / DB | HNSW | IVFFlat | Flat(総当たり) | 特徴 |
|---|---|---|---|---|
| pgvector | v0.5.0〜 | v0.1.0〜 | 常に利用可 | PostgreSQL拡張、SQLと統合 |
| FAISS | IndexHNSWFlat | IndexIVFFlat | IndexFlatL2 | Meta開発、GPU対応 |
| Chroma | デフォルト | - | - | hnswlib使用、手軽 |
| Pinecone | 独自実装 | - | - | マネージドサービス |
| Weaviate | デフォルト | - | - | GraphQLインターフェース |
| Milvus | 対応 | 対応 | 対応 | 多種インデックス対応 |
| Qdrant | デフォルト | - | - | Rust製、高速 |
LangChainとの組み合わせ
LangChainはオーケストレーションフレームワークであり、ベクトルストア自体ではありません。LangChainでどのインデックスが使われるかは、バックエンドの選択で決まります。
# LangChain + FAISS(デフォルトはFlat = 総当たり) from langchain_community.vectorstores import FAISS vectorstore = FAISS.from_documents(docs, embeddings) # LangChain + pgvector(HNSWインデックスはDB側で管理) from langchain_community.vectorstores import PGVector vectorstore = PGVector.from_documents(docs, embeddings, connection_string=conn) # LangChain + Chroma(デフォルトでHNSW) from langchain_community.vectorstores import Chroma vectorstore = Chroma.from_documents(docs, embeddings)
LangChain + FAISSの組み合わせは手軽ですが、デフォルトではIndexFlatL2(総当たり)が使われるため、大量データでは性能が劣化します。本番環境ではpgvectorやChromaなど、専用のインデックスを持つバックエンドの使用が推奨されます。
pgvectorを選ぶメリット
- 既存のPostgreSQLと同居できる → ユーザー管理や設定などのRDBデータとベクトルデータを同じDBに格納
- SQLの機能がそのまま使える → JOINやWHERE句によるフィルタリングが標準SQL
- 追加インフラが不要 → 別途ベクトルDBを立てる必要がない
- トランザクション対応 → データの整合性が保証される
- バックアップ・レプリケーション → PostgreSQLの運用ツールがそのまま使える
実装例:pgvectorでのHNSWインデックス構築
テーブルとインデックスの作成
-- pgvector拡張を有効化(初回のみ、スーパーユーザー権限が必要) CREATE EXTENSION IF NOT EXISTS vector; -- スキーマとテーブルの作成 CREATE SCHEMA IF NOT EXISTS pgvector; CREATE TABLE IF NOT EXISTS pgvector.embeddings ( id SERIAL PRIMARY KEY, bot_id VARCHAR(255) NOT NULL, user_id VARCHAR(255) NOT NULL, document_id VARCHAR(255) NOT NULL, chunk_id INTEGER NOT NULL, content TEXT NOT NULL, embedding vector, metadata JSONB, created_at TIMESTAMP DEFAULT NOW() ); -- B-treeインデックス(フィルタ用) CREATE INDEX IF NOT EXISTS idx_bot_user ON pgvector.embeddings (bot_id, user_id); CREATE INDEX IF NOT EXISTS idx_document ON pgvector.embeddings (document_id); -- HNSWインデックス(ベクトル検索用) CREATE INDEX IF NOT EXISTS idx_embeddings_hnsw ON pgvector.embeddings USING hnsw (embedding vector_cosine_ops);
embedding vector と次元数を指定しないことで、異なる次元のEmbeddingモデルに対応できます。ただし、同一テーブル内で異なる次元のベクトルを混在させて検索することはできません(同じモデルで生成されたベクトル同士でのみ比較可能です)。
HNSWパラメータのカスタマイズ(必要な場合のみ)
デフォルト値で始めて、パフォーマンスに問題がある場合のみ調整します。
-- インデックス構築パラメータの変更(再構築が必要) CREATE INDEX idx_embeddings_hnsw ON pgvector.embeddings USING hnsw (embedding vector_cosine_ops) WITH (m = 24, ef_construction = 128); -- 検索パラメータの変更(セッション単位、即時反映) SET hnsw.ef_search = 100;
まとめ
インデックス選択のフローチャート
- データ量は100万件以上か?
- No → HNSW(デフォルト設定で開始)
- Yes → 次へ
- メモリに余裕があるか?
- Yes → HNSW
- No → IVFFlat(lists/probesの調整が必要)
大半のRAGユースケースでは、ドキュメント数は数百〜数万件、チャンク数にして数千〜数十万件程度です。この規模ではHNSWのデフォルト設定で十分な性能が得られます。
実装時のポイント
- インデックスはデフォルト値で始める — HNSWのm=16、ef_construction=64は多くのケースで最適に近い値です。事前の過剰なチューニングは避けましょう。
- similarity_thresholdはモデルに合わせて設定する — Embeddingモデルごとにスコア分布が異なるため、固定値ではなく設定で変更できる仕組みにしておきます。
- フィルタ用のB-treeインデックスも忘れずに — マルチテナント環境では、ベクトルインデックスだけでなくbot_idやuser_idに対するB-treeインデックスが検索速度に大きく影響します。
- Embeddingモデルの変更時はベクトルの再構築が必要 — 異なる次元のベクトルは同一テーブル内で比較できません。モデルを変更する場合は、既存のベクトルを削除して再度埋め込みを行う必要があります。
- パフォーマンス計測は本番データで — 少量のテストデータでは総当たり検索でも十分速いため、インデックスの効果はデータ量が増えてから現れます。