社内ナレッジ検索やRAG(検索拡張生成、LLMに外部情報を検索させて回答精度を上げる手法)基盤を運用している方に向けた内容です。Qdrant(オープンソースのベクトル検索エンジン)のような仕組みを自己ホスト運用していると、ある日突然「全く関係のない情報が高スコアで返ってくる」という障害に遭遇します。原因の切り分け方と監視の組み方を整理しました。
何が起きるか:無関係な情報が「正しい結果」として返る
個人用のAIメモ基盤で「最近のプロジェクトの進捗は?」と質問したところ、買い物リスト(卵とヨーグルトとオーツミルク)が返ってきた、という事例があります。
これはバグでもハルシネーション(AIが事実でない内容を生成する現象)でもありません。
コサイン類似度(2つのベクトルの向きの近さを測る指標)のスコアは0.847と非常に高く、システムとしては「数学的に正しい」回答を返しています。
厄介なのは、エラーログにも例外にも残らない点です。
検索結果はhttp 200で返り、スコアも高く、システム的には「成功」として記録されます。
運用側から見ると、これは典型的なサイレント障害(エラーとして検知されないのに結果が誤っている状態)です。
監視ダッシュボードのレイテンシやエラー率だけを見ていては、この種の障害には一生気づけません。
なぜ起きるか:原因を3段階に分解する
段階1: 類似度と関連性の混同
コサイン類似度は「意味的な近さ」を測る指標であり、「検索意図に対する正しさ」を測る指標ではありません。
買い物リストに含まれる「items」「pending」「need to check」「follow up」といった単語は、プロジェクトの進捗メモと語彙的な指紋が酷似していました。
embeddingモデル(テキストを数値ベクトルに変換する機械学習モデル)は、文脈の種類ではなく表層的な語彙パターンに基づいてベクトル空間上の位置を決めます。
「似ている」と「正しい」は別の軸である、という前提を持たずに運用設計をすると、このズレに気づけません。
段階2: コレクション設計でドメインを分離していない
該当の事例では、個人ナレッジベースと技術メモを同じコレクション(Qdrantにおけるデータの論理的な集合単位)に格納していました。
ドメインが異なるデータを1つのベクトル空間に押し込むと、全く別の文脈のデータ同士が近傍(ベクトル空間上で距離が近い点)になり得ます。
リレーショナルDBで言えば、用途の異なるテーブルを無理やり1つに結合しているようなものです。
段階3: チャンクサイズとメタデータの設計不足
ドキュメントをベクトル化する前には、テキストをチャンク(検索・embedding生成のための分割単位)に分割します。
このチャンクサイズの設計が甘いと、文脈の切れ目が不自然になり、意味的なノイズが増えます。
さらに、ドメインやソース種別を示すメタデータ(データ自体に付随する属性情報)を付与していないと、検索時にフィルタリングする手段がなくなります。
自分のプロジェクトが該当するか確認する方法
本番でRAGやベクトル検索を運用している場合、以下を確認してください。
# Qdrantのコレクション一覧とポイント数を確認
curl -s http://localhost:6333/collections | jq
# 特定コレクションの設定(ベクトル次元・距離関数)を確認
curl -s http://localhost:6333/collections/{collection_name} | jq '.result.config'確認すべきポイントは次の3つです。
- コレクションが用途やデータソースごとに分離されているか(1コレクションに雑多な情報が混在していないか)
- 各ポイントに
domainやsource_typeのようなメタデータフィールドが付与されているか - 距離関数(distance)がコサイン類似度(Cosine)固定で、閾値(スコアの足切りライン)を設定せず全件返していないか
メタデータが入っていない、あるいはコレクションが1つしかない場合は、今回説明したリスクが顕在化しやすい状態です。
運用コストの観点では、コレクション分割やフィルタ処理によるクエリの複雑化が、クラウド上のマネージドベクトルDB(Qdrant Cloudや類似サービス)の課金体系(クエリ数やノード数課金が多い)に影響する点も確認しておくと安心です。
対策の手順
1. ドメインフィルタを導入する
コレクションを分けるか、既存コレクションにメタデータでドメインを付与し、検索時にフィルタをかけます。
# ドメインを限定した検索の例(Qdrant REST API)
curl -X POST http://localhost:6333/collections/{collection_name}/points/search \
-H 'Content-Type: application/json' \
-d '{
"vector": [0.12, 0.08, "..."],
"filter": {
"must": [{ "key": "domain", "match": { "value": "project_notes" } }]
},
"limit": 5,
"with_payload": true
}'これにより、買い物リストのような無関係ドメインのデータは最初から検索対象から除外できます。
2. スコア閾値をアラート条件に組み込む
監視設計の観点では、検索結果のスコア分布を可観測性(システム内部の状態を外部から把握できる度合い)の対象に含めるべきです。
上位結果のスコアが一定閾値(例えば0.75未満)の場合は「低信頼度の回答」としてログに明示し、ユーザー向けには注意喚起を出す設計が有効です。
これはAPM(アプリケーション性能監視)ツールのレイテンシ監視と同じ発想で、「検索品質」も監視対象のメトリクスとして扱う、という考え方です。
3. チャンクサイズとオーバーラップを見直す
明確な「正解のチャンクサイズ」は存在しませんが、384トークン前後・オーバーラップ15%程度を起点に、実際のクエリで検証するのが現実的です。
Qdrantの管理画面やログで、どのチャンクが頻繁にヒットしているかを定期的に確認し、不自然に汎用的な文言のチャンクが上位に来ていないかをレビューします。
4. 障害訓練として「誤検索シナリオ」を定期実行する
オンプレで動いていたキーワード検索システムをクラウド上のベクトル検索に移行した場合、この種の誤検索は移行後にはじめて顕在化するケースがあります。
本番投入前に、意図的に語彙が似ているが文脈の異なる複数ドメインのデータを投入し、誤検索が起きないかをテストしておくと安心です。
まとめ:ベクトル検索は「記憶」ではなく「最近傍探索」である
ベクトルDBは人間のような記憶を持っているわけではなく、最近傍探索(クエリに最も近いベクトルを探す処理)を行っているだけです。
この前提を運用チーム全員が共有できているかどうかが、障害対応の初動速度を大きく左右します。
次の一歩としては、まずcurlでコレクション設定とメタデータの有無を確認し、ドメインフィルタが未導入であれば優先的に実装することをおすすめします。
あわせて、検索スコアの分布を監視メトリクスに追加し、低スコア時のアラートルールを運用フローに組み込んでおくと、次に同様の「もっともらしい誤答」が発生したときの発見が早くなります。