この回のゴール
- 文書コレクションを 埋め込みベクトル に変換して保存する
- FAISS で大規模ベクトル検索を実装する
- Top-k 検索 で関連文書を自動で取り出す
- 検索結果の 質 を自分で評価できるようになる
1. 全体像 — インデックスと検索
User: 明日のランチの予定を教えて
Claude: 明日は 12 時にアリスとイタリアンレストランです。
2. 埋め込みモデルの選び方
2026 年時点でよく使われるモデル:
| モデル | 提供 | 次元 | 用途 |
|---|---|---|---|
paraphrase-multilingual-MiniLM-L12-v2 |
HF | 384 | 軽量・多言語 |
text-embedding-3-small |
OpenAI | 1536 | バランス |
text-embedding-3-large |
OpenAI | 3072 | 高精度 |
voyage-3 |
Voyage AI | 1024 | 2024 年時点の最強クラス |
注意: Claude API 自体は埋め込み専用 API を提供していません(2026 年現在)。RAG の検索部分は別途用意します。
3. FAISS — なぜベクトル DB が必要か
ナイーブ実装(小規模向け)
{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
- 文書数 N が小さい(〜1 万)なら問題なし
- 大規模では遅くなる
FAISS の役割
- 大量のベクトルを高速検索(100 万件で数ミリ秒)
- 近似最近傍探索 (IVF, HNSW) で $O(\sqrt{N})$ や $O(\log N)$
- 正確検索(
IndexFlatIP,IndexFlatL2)も可能
{"name": "Alice", "age": 30} ✅
{"name": "Alice"} ❌ age 欠けている
{"name": "Alice", "age": "三十"} ❌ age が文字列
4. メタデータと組み合わせる
ベクトル DB は通常「ベクトル → ID」しか返しません。メタデータ は別途管理:
wzxhzdk:3
5. 正規化とコサイン類似度
wzxhzdk:4
正規化すれば、内積 = コサイン類似度 になります:
$$ \cos(\vec{a}, \vec{b}) = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|} \xrightarrow{\text{正規化後}} \vec{a} \cdot \vec{b} $$
だから FAISS の IndexFlatIP(内積)が cos 類似度検索になります。
6. 検索品質の評価
- Recall@k: 本当に関連する文書が、top-k に含まれている割合
- Precision@k: top-k のうち関連文書の割合
- MRR (Mean Reciprocal Rank): 関連文書が何番目に出たか
7. Top-k の選び方
| k | 効果 | 欠点 |
|---|---|---|
| k=1 | トークン少、ピンポイント | 関連文書を逃すと即アウト |
| k=3〜5 | 最もバランス(実務) | 適度なトークン量 |
| k=10+ | 取りこぼしは少ない | トークン肥大、ノイズ混入 |
実務の定番は k=3〜5 です。
まとめ
- ベクトルストア(FAISS など)で 大量文書の高速類似検索 を実現
- 埋め込みベクトルを L2 正規化 すれば内積 = コサイン類似度
- メタデータは別管理(出典・更新日・カテゴリなど)
- 検索品質は Recall@k / Precision@k / MRR で評価
- 実務の定番は Top-3〜5
この回の限界(次への動機)
長文を「1 つの文書」としてそのまま埋め込むと、意味が平均化されて検索ヒットしない。長文をどう区切る? 👉 次回「チャンク分割戦略」で、固定長・段落・再帰・セマンティックの 4 戦略を比較します。
参考文献
- FAISS 公式ドキュメント
- sentence-transformers
- Reimers & Gurevych (2019) Sentence-BERT