TL;DR
- RAG は「PoC で動く」と「本番で1年運用する」の間に大きな谷がある
- 本番品質を守るのは精度劣化検出 → embedding 更新 → 再ランク調整の評価ループ
- 単一の精度指標(recall@k)だけでは劣化を見抜けない、ゴールデンセット+自動評価の二段構えが必須
- embedding 更新は「全件再生成」より「差分更新+影響範囲評価」が運用コスト面で勝つ
- 再ランクの導入で精度は伸びるが、レイテンシ+コストとのトレードオフが明確に
この記事の目的と成功基準
- 目的: RAG を本番運用に乗せる際の評価・更新・再ランク設計を、具体的な実装手順で整理する
- 想定読者: RAG プロダクトを本番運用しているエンジニア、品質劣化に困っているチーム
- 成功基準: 「RAG 本番運用」関連クエリでの流入、LLM本番運用・LLMオブザーバビリティ への回遊
RAG の本番運用が難しい理由
AI 実践ガイド2026 でも触れられているように、RAG は「動くまで」は速いが「動き続ける」は難しい。
理由:
- データ側の変化: 知識ベースの更新で embedding が古くなる
- クエリ側の変化: ユーザーの問い方が時間とともに変わる
- モデル側の変化: embedding モデルの更新で表現が変わる
- 評価の薄さ: PoC では eye-balling、本番では数千〜数万件の継続評価が必要
これらが揃わないと、リリース3か月後に「なんとなく悪くなった」気がするのに原因が特定できない状態になる。
評価ループの構造
ゴールデンセット
「この問いにはこの文書が出るべき」をマニュアル整備したセット。
- 規模:機能あたり 100〜500件
- 更新頻度:月1で見直し、四半期で大幅更新
- カバレッジ:定型問い・難問・反例を含める
自動評価指標
- Recall@k: k 件の取得で正解文書が含まれる割合
- MRR(Mean Reciprocal Rank): 正解の順位の逆数
- NDCG@k: ランキング品質
- End-to-end accuracy: 最終回答の正しさ(LLM-as-judge)
retrieval の指標と生成の指標を分けて計測する。retrieval は良いのに最終出力が悪い場合、prompt 側の問題と切り分けられる。
LLM-as-judge
ゴールデン回答との一致を LLM で判定する。
def judge(question: str, golden: str, generated: str) -> float:
prompt = f"""次の生成回答が、正解回答と意味的に一致するか0〜1で評価。
Q: {question}
正解: {golden}
生成: {generated}
評価:"""
return float(judge_model.complete(prompt))
判定の偏りを抑えるため、judge model はターゲット model と別系統を選ぶ。
定期実行と CI 統合
- 日次:軽量サブセット(100件)で迅速検出
- 週次:フルセットで品質トレンド把握
- リリース時:プロンプト・モデル変更の PR で自動評価、閾値割れでブロック
LLM解釈可能性入門 で扱った機能別 eval ハーネスの応用形。
embedding 更新戦略
差分更新が基本
全件再生成は高コスト。差分更新を基本にする。
- 新規ドキュメント → 即時 embedding 化
- 更新ドキュメント → 変更検出後に再生成
- 削除ドキュメント → ベクター DB からも削除
def update_document(doc_id: str, new_content: str):
old = db.get(doc_id)
if old and old.content_hash == hash(new_content):
return # 変更なし、スキップ
embedding = embed_model.embed(new_content)
db.upsert(doc_id, content=new_content, embedding=embedding, hash=hash(new_content))
metrics.inc("embedding.updated")
embedding モデル変更
モデル変更(OpenAI v3 → v4 等)時は全件再生成が必要。
- 旧モデルの index と新モデルの index を並列稼働
- A/B 評価で品質確認
- 切替えは flag で段階移行
- 切替え完了後に旧 index を廃止
影響範囲評価
embedding 更新が retrieval 結果に与える影響を測る。
- 更新前後で top-k 結果がどれだけ変化したか
- 変化が大きい問いの傾向(特定トピック?)
- ゴールデンセットでの retrieval 指標変化
「変化が大きすぎる」場合は更新自体を疑う。
再ランクの設計
retrieval で粗く広く取り、再ランクで精度を上げる構造。
二段構成
- 第一段(retrieval): vector search で top-k(k=50〜100)を粗く取得
- 第二段(再ランク): cross-encoder か LLM で top-n(n=5〜10)に絞り込み
再ランクの選択肢
- Cross-encoder: BGE-reranker、Cohere Rerank。速い・安い
- LLM-as-reranker: GPT-5 / Claude で関連性スコアを生成。高精度・高コスト
- ハイブリッド: 一段目を cross-encoder、最終的に LLM で確認
レイテンシとコストのトレードオフ
- 再ランクのレイテンシ:50ms〜500ms(モデル次第)
- 再ランクのコスト:retrieval 単独の2〜10倍
- 精度向上:5〜30%(タスク次第)
重要機能のみ再ランクを入れ、軽量機能は retrieval のみ、という分け方が現実的。
監視と障害対応
監視すべき指標
- 日次の eval スコア(trend で見る)
- 平均応答時間(retrieval / rerank / generation 別)
- 「該当文書なし」率
- ユーザーフィードバック(thumbs up/down)
これらを LLMオブザーバビリティ のスタックに集約する。
障害対応
- 急激な精度劣化 → embedding 更新ロールバック
- 特定トピックの劣化 → ゴールデンセット拡充 + 再評価
- レイテンシ悪化 → vector DB のスケール・再ランク段の調整
アンチパターン
- 「動いたから完了」: PoC ベースの評価で本番投入すると3か月で劣化を見逃す
- embedding 全件再生成を高頻度に: コストが膨らみ続ける、差分更新を基本に
- 再ランクを全機能に入れる: レイテンシとコストが破綻、機能別に判断
- ゴールデンセットを更新しない: ユーザーの問い方の変化に追従できない
FAQ
Q. vector DB は何を選ぶべきですか? A. 規模 < 100万件なら Postgres + pgvector で十分、それ以上は Pinecone / Weaviate / Qdrant / Milvus が選択肢です。マネージドかセルフホストの判断が主軸。
Q. ハイブリッド検索(BM25 + vector)は必要ですか? A. 固有名詞・型番・コードの検索が含まれる場合は推奨です。意味検索だけだと exact match が弱くなります。
Q. embedding モデルの選び方は? A. 多言語対応・コスト・dimension のトレードオフ。日本語が多いなら multilingual-e5、コスト優先なら OpenAI text-embedding-3-small が定番です。
まとめ
RAG の本番運用は、評価ループ・embedding 更新・再ランクの3点を運用に乗せて初めて成立する。ゴールデンセット+自動評価で劣化を早期検出、差分更新でコストを抑え、再ランクは機能別に導入する。観測スタックと SLO に組み込んで運用すれば、PoC からの卒業ラインを超えられる。
