生成AIに自社の情報を答えさせたい。
RAGはその近道ですが、運用で一番厄介なのは"情報が更新された瞬間"です。
古い情報が残ると、検索は過去の文書を拾い、LLMはそれっぽい回答で誤魔化してしまいます。
本記事では「オフィス移転」を題材に、移転前後の住所と移転日をきちんと参照できる状態を、評価駆動でRAGを改善するアプローチを考え、検証します!
目次
- 検証シナリオ:オフィス移転情報の更新
- 前提知識:RAGとは何か、何が難しいのか
- 今回のアプローチ:評価駆動のHuman-in-the-loop改善サイクル
- システム構成
- 評価設計:ハイブリッド評価の採用
- 検証結果:2フェーズで何が起きたか
- 学んだこと・今後の展望
- 参考文献
1. 検証シナリオ:オフィス移転情報の更新
なぜこの題材か
「アイレット株式会社の東京本社が移転した」というシナリオを使います。
最もわかりやすくてタイムリーな変化が、オフィスの移転だったなんて言えない。
- 移転前: 東京都港区虎ノ門1-23-1(虎ノ門ヒルズ森タワー)
- 移転後: 東京都港区虎ノ門1-17-1(虎ノ門ヒルズビジネスタワー)
- 移転日: 2025年12月1日
検証に使用するクエリ
アイレット株式会社の東京本社の移転について、移転前と移転後それぞれの所在地と、移転日をまとめて説明してください。
このクエリは、移転前の所在地、移転後の所在地、移転日の3つを求めています。
旧住所だけの情報が知識ベースにある場合で、情報が不足していることを検知し改善に移行する仕組みを作ることが本ブログの目標です!
何を確かめたいのか
「更新が入ったときに参照がズレる」問題を、どこで検知し、どう直し、どこまで改善できるかを確かめるべく、検証は次の2フェーズで回します。
| フェーズ | やること | 検証する仮説 |
|---|---|---|
| Phase 1 | あえて不完全な知識ベースでRAG→評価 | 「欠落」や「混同」がスコアと内訳で可視化できるか? |
| Phase 2 | 人間の不足情報をチャンク化して追加→同一クエリで再評価 | ナレッジ追加でスコアが改善し、残課題も内訳で特定できるか? |
2. 前提知識:RAGとは何か、何が難しいのか
RAGの基本的な仕組み
RAG(Retrieval-Augmented Generation)は、LLM(大規模言語モデル)に「外部の知識」を与えて回答させる仕組みです。
ChatGPTなどのLLMは学習時点の情報しか知りません。
でもRAGを使えば、自社の最新情報や社内ドキュメントの内容を回答に含められます。
RAGで起きがちな3つの問題
RAGの構築は上手くいったものの、運用へ移行した場合にトラブルが起きる可能性が懸念されます。
① 情報の欠落 —知識ベースに情報がない/検索で拾えない
例:移転後の住所がコーパスにない → 新住所を聞かれても答えられない
② 古い情報の優先 — 新しい情報より古い文書が上位に来る
例:会社紹介が上位に来て、旧住所を「現在の住所」として語ってしまう
③ 知識にないことを断定する — 足りない部分をLLMが埋めてしまう
例:移転日が根拠にないのに日付を断言する(ハルシネーション)
厄介なのは、どれも「一見それっぽい回答をしてしまい見逃す可能性がある」ことです。
従来のアプローチの限界
「じゃあプロンプトを工夫しよう」「検索の設定をいじろう」と対処しがちですが、原因が見えないまま調整すると、何が効いたのか分からなくなりがちです。
- 知識が足りないのにLLM側をいじってしまう
- 順位の問題なのにナレッジだけを増やしてしまう
- 「良くなったかどうか」が感覚便りになってしまう
そのため、原因を切り分けられる評価を置き、改善の効果を同じ条件で測れるようにする必要があります。
今回の検証は、そのための最小アーキテクチャを作る、という位置づけです。
3. 今回のアプローチ:評価駆動のHuman-in-the-loop改善サイクル
移転前後の情報参照を、評価で回す
移転のような更新が入ると、RAGは「新住所が出ない」「旧住所を現住所として言う」「日付が落ちる」といった失敗を起こします。
ここでやりたいのは、追加したナレッジが"ちゃんと参照される"状態を、評価で確認しながら作ることです。
この検証では、ナレッジを勝手に増やすのではなく、人間が確認した情報だけを追加する前提にしました。
自動でWeb検索して補完するCRAGのようなアプローチもありますが[1]、企業情報では誤情報の混入が一番怖いからです。
Human-in-the-loop(HITL)は、安全性と精度を両立させるための定番の考え方です [3]。
このサイクルを回すために、ワークフローはLangGraphで管理し、各フェーズの入力/出力とスコアはLangfuseに記録します。
大事なのは、次の3点です。
- 改善前後を同じ条件で採点する(スコアと内訳)
- 問題を「知識不足(KNOWLEDGE GAP)」と「順位/設定」に分けて切り分ける
- 反映は人間が決める。ただし判断材料(証跡)は自動で揃える
4. システム構成
LLMが生成内容に問題があると判断した場合に、ユーザーやテストでのフィードバックを貰うように動作しチューニングする構成を考えました。
全体像は以下の通りです。
全体アーキテクチャ
使った技術スタック
| コンポーネント | 技術 | 役割 |
|---|---|---|
| LLM | Vertex AI (Gemini) | 回答生成 |
| Embeddings | Vertex AI Embeddings | テキストのベクトル化 |
| ベクトルDB | ChromaDB | 類似度検索 |
| ワークフロー | LangGraph | 状態機械としてフェーズを管理 |
| 観測 | Langfuse | トレース・スコア記録 |
| 評価 | HybridEvaluator | ルールベース + LLM-as-a-Judge |
| 問題検知 | IssueDetector | KNOWLEDGE GAPの分類 |
処理フロー(アクティビティ図)
5. 評価設計:ハイブリッド評価の採用
なぜハイブリッド評価なのか
RAG評価は大きく「ルールベース」と「LLM-as-a-Judge」に分けられます [5]。
住所や日付のような"事実"は前者で決定論的に見て、住所の混同や時系列の破綻のような"文脈依存"は後者で拾う、という役割分担がしやすいです。
| 手法 | 得意 | 苦手 |
|---|---|---|
| ルールベース | 高速・低コスト、同じ入力に同じ結果 | 文脈依存の判断、言い換え |
| LLM-as-a-Judge | ニュアンスの判断、説明文の生成 | コスト、遅延、非決定性 |
そのため、今回は両者の長所を活かすハイブリッド評価にしました。
配点の設計
最終スコア(0〜1)は、各評価項目の0/1に配点を掛けて足し合わせます。100点満点のテストで、各設問に点数が割り振られているイメージです。
| 評価タイプ | 評価項目 | 配点 | 評価方法 |
|---|---|---|---|
| ルールベース | 旧住所が含まれる | 20点 | 正規表現マッチ |
| 新住所が含まれる | 20点 | 正規表現マッチ | |
| 移転日が含まれる | 20点 | 正規表現マッチ | |
| LLM-as-a-Judge | 住所の混同がない | 20点 | Geminiによる判定 |
| 時系列が正しい | 20点 | Geminiによる判定 |
※ 各項目は0〜1で評価され、配点を掛けて合計。最終スコアは0〜1(0〜100%)で表現。
実装は以下のとおりで、ルールベース(事実チェック)とLLM-as-a-Judge(混同/時系列)を重み付きで合算しています。
WEIGHTS = {
"contains_old_address": 0.20,
"contains_new_address": 0.20,
"contains_date_after": 0.20,
"no_address_confusion": 0.20,
"correct_timeline": 0.20,
}
final_score = rule_based_score + llm_judge_score
配点の根拠:検証目的の設計判断
この配点(ルールベース60点、LLM-as-a-Judge 40点)は、本PoCの検証目的に基づく設計判断です。
RAG評価における配点の決め方に学術的な正解はありません。Databricksは自社のRAGチャットボット評価で「Correctness 60% / Comprehensiveness 20% / Readability 20%」という配分を採用し、その根拠を以下のように説明しています [5]:
We chose this weighting scheme to reflect our preference for Correctness in the generated answers. Other applications may tune these weights differently.
(この配点は、生成された回答における正確性への優先度を反映するために選択しました。他のアプリケーションでは異なる配点に調整する可能性があります。)
本プロジェクトでも同様のアプローチを採り、以下の検証目的に基づいて配点を設計しました。
| 設計判断 | 理由 |
|---|---|
| 事実情報の検証を優先(60点) | 住所・日付の有無は「改善サイクルが機能するか」を検証する上で最も明確な指標 |
| 複雑な判断はLLMに委ねる(40点) | 「住所の混同」はルールベースでは検出困難であり、LLM-as-a-Judgeの有効性を検証したい |
| ルールベース内で等配分(各20点) | 3つの事実情報(旧住所、新住所、移転日)を同等に扱う |
| LLM-as-a-Judge内で等配分(各20点) | 2つの文脈判断(混同、時系列)を同等に扱う |
本番環境での運用では、ビジネス要件に応じてこの配点を調整することを想定しています。 例えば、より厳密な説明責任が求められる場合は、AHP(階層分析法)によるステークホルダー間の合意形成や、A/Bテストによるデータドリブンな最適化も選択肢となります。
LLM-as-a-Judgeの実装
住所混同の評価プロンプトは、期待値(旧住所/新住所)と検索コンテキストも渡して、混同の有無を判定します。
prompt = f"""あなたは回答品質評価の専門家です。以下の回答が、旧住所と新住所を混同していないか評価してください。
**評価基準**:
- 1.0: 旧住所と新住所が明確に区別されている
- 0.8: 概ね区別されているが、一部曖昧な表現がある
- 0.6: 混同の可能性がある表現が含まれる
- 0.4: 明らかに混同している
- 0.2: 完全に混同している
- 0.0: 旧住所を新住所として誤って説明している
**期待値**:
- 旧住所: {expected_old_address}
- 新住所: {expected_new_address}
**回答**:
{answer}
**コンテキスト**:
{context}
JSON形式で回答してください:
{{"score": <0.0-1.0>, "explanation": "<評価理由>"}}"""
時系列評価も同様に採点しています。実装ではJSONパースに失敗した場合は 0.5 でフォールバックします。
LLMは数値スコアだけでなく、評価理由の説明文も返します。これがLangfuseに記録され、後からデバッグや分析に活用できます。
6. 検証結果:2フェーズで何が起きたか
結果サマリー
| フェーズ | スコア | 何をしたか | 改善 |
|---|---|---|---|
| Phase 1 | 0.300 | 不完全な知識ベースでRAG→評価 | - |
| Phase 2 | 1.000 | フィードバックをチャンク化して追加→再評価 | +0.700 |
総合改善: +233%(0.300 → 1.000)
Phase 2で完璧スコア1.000を達成しました!ルールベースでは旧住所、新住所、移転日の3項目すべてを検出し(60点/60点)、LLM-as-a-Judgeも住所混同なし・時系列正確と完璧判定(40点/40点)を出しています。
Phase 1: 欠落の検知
状況設定
- 知識ベースには「旧住所」の情報のみ
- 新住所・移転日の情報はなし
評価結果
| 評価項目 | 配点 | Phase 1の結果 | 評価方法 |
|---|---|---|---|
| 旧住所が含まれる | 20点 | 1.0 | ルールベース |
| 新住所が含まれる | 20点 | 0.0 | ルールベース |
| 移転日が含まれる | 20点 | 0.0 | ルールベース |
| 住所の混同がない | 20点 | 0.0 | LLM-as-a-Judge |
| 時系列が正しい | 20点 | 0.5 | LLM-as-a-Judge |
結果: スコア 0.300
ルールベースでは旧住所のみ検出(20点/60点)しました。LLM-as-a-Judgeは住所混同あり(0点/20点)、時系列は0.5(10点/20点)となり、計10点/40点でした。
以下のLangfuse画面で、LLM-as-a-Judgeの評価詳細を確認できます。
図:Phase 1のトレース詳細。メタデータに評価結果が記録されている
図:Phase 1のスコア一覧。contains_old_address: 1.0、contains_new_address: 0.0、contains_date_after: 0.0、no_address_confusion: 0.0、correct_timeline: 0.5、最終スコア0.30
Phase 2: 知識の追加(Human-in-the-loop)
Phase 2では、以下のフローでナレッジの追加と検証を行います。
人間が追加した情報
IssueDetectorからの通知を受けて、人間が以下の情報を提供しました。
アイレット株式会社の東京本社は、2025年11月30日までは
東京都港区虎ノ門1-23-1 虎ノ門ヒルズ森タワー7F・24Fに所在していましたが、
2025年12月1日より、東京都港区虎ノ門1-17-1 虎ノ門ヒルズビジネスタワー29Fに移転しました。
検証プロセス
追加したナレッジは、まずテスト環境で検証されます。
-
追加確認: ベクトルDBの件数と
doc_idを記録し、追加できたことを検証 - 検索分析: 追加チャンクがTop-kに入るかを確認
- 同一クエリで再評価: RAGを再実行し、Hybridで採点してLangfuseに記録
※ 運用では、この後に「閾値」や「承認フロー」を置いて本番反映を制御します(本PoCでは反映は行いません)。
Langfuseには、この検証プロセスの証跡も記録されます。
{
"ingestion_verification": {
"verification_passed": true,
"stats_before": {"total_documents": 1},
"stats_after": {"total_documents": 2},
"doc_id": "eylet_hq_feedback_<feedback_id>"
}
}
評価結果
| 評価項目 | 配点 | Phase 2の結果 | Phase 1→2の変化 |
|---|---|---|---|
| 旧住所が含まれる | 20点 | 1.0 | +0.0 |
| 新住所が含まれる | 20点 | 1.0 | +1.0 |
| 移転日が含まれる | 20点 | 1.0 | +1.0 |
| 住所の混同がない | 20点 | 1.0 | +1.0 |
| 時系列が正しい | 20点 | 1.0 | +0.5 |
結果: スコア 1.000(完璧)
ルールベースでは、旧住所、新住所、移転日の3項目すべてを検出し60点/60点を達成しました。LLM-as-a-Judgeも混同なし・時系列正確と完璧判定(40点/40点)を出しています。Phase 1の0.3から1.0まで改善しました。
以下のLangfuse画面で、改善後の評価詳細を確認できます。
図:Phase 2のトレース詳細。ingestion_verificationやretrieval_analysisが記録され、フィードバック追加の証跡が残る
図:Phase 2のスコア一覧。contains_old_address: 1.0、contains_new_address: 1.0、contains_date_after: 1.0、LLM-as-a-Judge完璧判定、最終スコア1.00
スコア改善の内訳
- ルールベース部分: 20点 → 60点(+40点)
- LLM-as-a-Judge部分: 10点 → 40点(+30点)
ルールベースでは新住所と移転日を新たに検出し、各項目を達成しました。LLM-as-a-Judgeも満点です。評価内訳により「何が改善されたか」が明確に分かります。
Langfuseに残った記録
各フェーズの実行結果がLangfuseに記録され、以下を後から確認できます:
-
スコア内訳:
rule_based_scoreとllm_judge_score、各コンポーネント(住所/日付/混同/時系列) -
説明文:
hybrid_evaluation.confusion_explanation,hybrid_evaluation.timeline_explanation -
追加検証(Phase 2):
ingestion_verification,retrieval_analysis - 入力/出力: クエリと生成回答
これにより、「なぜこの改善を行ったのか」「何が効いたのか」を証跡付きで共有・説明できます。
7. 学んだこと・今後の展望
学んだこと
移転のように更新が入るケースでは、まず「参照できているか」を評価で可視化し、足りないものは追加する。今回の検証は、この順番が運用として回ることを確認するものでした。
- ルールベースは欠落を素早く見つける:住所や日付の有無は決定論的にチェック可能
- LLM-as-a-Judgeは文脈依存を拾える:「混同」や「時系列」のような複雑な判断はLLMに委ねる
- スコアに内訳があると改善点が具体化する:5つの評価項目により、どこが達成できてどこが課題かが明確
- 評価結果と説明文がLangfuseに残ると議論が速い:「なぜ変えたか」を後から追える
- 配点は設計判断として文書化する:RAG評価に正解はなく、検証目的に基づいて設計し根拠を残す [5]
今後の展望
フィードバック駆動Rerankerの実現
今回の検証では、ナレッジ追加による改善に焦点を当てました。しかし検索順位の問題は残っています。
Phase 2で人間が提供したフィードバックは、「このクエリに対して、この新チャンクが正解(ポジティブサンプル)」という学習データとしても活用できます。現状、システムはこのペアを自動でJSONL形式で蓄積していますが、まだRerankerの学習には使っていません。
次のステップとして、以下の実装を計画しています:
-
フィードバックから学習データを自動蓄積(既に実装済み)
- (query, positive_chunk, negative_chunks) のペアをJSONL保存
- Phase 2のフィードバックごとに学習データが増える
-
Cross-Encoderの学習型Rerankerを導入
- ルールベース設定(recency重視、metadata boost等)ではなく、実際のフィードバックから学習
- ドメイン固有の検索順位を最適化
-
A/Bテストでルールベース vs 学習型を比較
- 従来のルールベースReranker(recency/metadata重み付け)
- 学習型Reranker(Cross-Encoder fine-tuned on feedback)
- どちらが実際のクエリに対して効くかをデータで選ぶ
この approach により、人間のフィードバックを起点に、検索精度も自動改善できる仕組みを目指します。
その他の展望
- テストケースを増やし回帰を継続的に見る:改善で別ケースが悪化しないかを確認
- 評価をドメインごとにテンプレ化:移転以外のシナリオ(製品仕様変更、価格改定等)にも適用
- 共通指標の併用を検討:Groundednessなどの汎用指標と組み合わせる
- 閾値と承認フローをCI/CDに寄せる:反映プロセスを自動化しつつ安全性を担保
おわりに
RAGの品質改善は、プロンプト調整だけだと「何が原因で、何が効いたのか」が曖昧になりがちです。
移転シナリオで試したように、まず評価で欠落/混同を見える化し、足りない知識を追加する。この順番で回すと、更新が入っても改善を再現しやすくなります。
Langfuseに入力/出力とスコア、説明が残るので、後から検証の根拠を辿れる点も運用上のメリットでした。
8. 参考文献
[1] Yan, S., Gu, J., Zhu, Y., & Ling, Z. (2024). Corrective Retrieval Augmented Generation. arXiv:2401.15884. https://arxiv.org/abs/2401.15884
※本システムとは異なるアプローチ(自動修正型)だが、RAG品質改善の文脈で参考になる
[2] Langfuse Blog. (2024). OpenTelemetry (OTel) for LLM Observability. https://langfuse.com/blog/2024-10-opentelemetry-for-llm-observability
[3] IBM. (2024). What Is Human In The Loop (HITL)? https://www.ibm.com/think/topics/human-in-the-loop
[4] Neptune.ai. (2024). LLM Observability: Fundamentals, Practices, and Tools. https://neptune.ai/blog/llm-observability
[5] Databricks. (2023). Best Practices for LLM Evaluation of RAG Applications. https://www.databricks.com/blog/LLM-auto-eval-best-practices-RAG
※RAG評価における配点設計の実例。「設計判断として文書化する」アプローチの参考



