本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
RAGによるLLMの知識拡張と幻覚抑制
大規模言語モデル(LLM)は広範な知識を持つ一方で、学習データにない最新情報への対応や、事実に基づかない「幻覚(Hallucination)」の生成が課題です。Retrieval-Augmented Generation (RAG) は、外部の情報源から関連情報を動的に取得し、LLMに提供することでこれらの課題を解決する強力な手法です。これにより、LLMの知識を拡張し、応答の正確性を高め、幻覚を抑制します。
ユースケース定義
、特定のドメイン知識(例: 企業独自の製品マニュアル、法規制文書、技術ブログ)に基づいてユーザーの質問に回答する「セキュアなQAシステム」を想定します。RAGシステムは、ユーザーの質問に対し、あらかじめ用意されたドキュメントデータベースから最も関連性の高い情報を抽出し、その情報のみを基にLLMが回答を生成します。
例:
カスタマーサポート: 製品仕様、FAQ、トラブルシューティングに関する質問への回答。
社内ナレッジベース: 社内規定、開発ドキュメント、人事制度に関する質問への回答。
医療・法律: 最新の研究論文や判例に基づいた情報提供。
制約付き仕様化
入出力契約
入力:
user_query: ユーザーからの質問文字列。context_docs: RAGシステムが検索した、質問に関連する情報を含むドキュメントのリスト。各ドキュメントは以下のJSON形式。[ { "id": "doc_1", "title": "ドキュメントタイトル1", "url": "https://example.com/doc1", "content": "関連情報の本文1..." }, { "id": "doc_2", "title": "ドキュメントタイトル2", "url": "https://example.com/doc2", "content": "関連情報の本文2..." } ]
出力:
answer: ユーザーの質問に対する簡潔かつ正確な回答文字列。references: 回答に使用されたドキュメントのタイトルとURLのリスト。status: “SUCCESS” または “NO_CONTEXT_FOUND”。failure_reason(statusが”NO_CONTEXT_FOUND”の場合のみ): 失敗理由の説明。{ "answer": "回答の本文。", "references": [ {"title": "ドキュメントタイトル1", "url": "https://example.com/doc1"}, {"title": "ドキュメントタイトル2", "url": "https://example.com/doc2"} ], "status": "SUCCESS", "failure_reason": null }
または
{ "answer": "ご質問に対する関連情報を見つけることができませんでした。別の質問をお試しください。", "references": [], "status": "NO_CONTEXT_FOUND", "failure_reason": "関連コンテキスト不足" }
失敗時の挙動
context_docsが空、または質問への回答に必要な情報が含まれていない場合、LLMは「関連情報が見つかりませんでした」という旨の定型メッセージを生成し、statusを”NO_CONTEXT_FOUND”とする。JSON形式以外の出力が発生した場合、システムは出力パースエラーを返し、再試行を促す。
禁止事項
提供された
context_docsに含まれない情報に基づいて回答を生成すること(幻覚の禁止)。個人的な意見や推測を回答に含めること。
不適切な言葉遣いや偏見のある表現を使用すること。
JSON出力フォーマット以外の形式で出力すること。
プロンプト設計
RAGにおけるプロンプト設計の鍵は、提供されたコンテキスト情報への忠実性と、幻覚の抑制です [1, 3]。
1. ゼロショットプロンプト (Zero-Shot Prompt)
最もシンプルだが、LLMに強力な制約を課す。
あなたはユーザーの質問に答えるAIアシスタントです。
以下の「参照情報」のみに基づいて、質問に簡潔かつ正確に回答してください。
参照情報に含まれていない内容については、一切言及せず、「関連情報が見つかりませんでした」と回答してください。
# 質問
{{user_query}}
# 参照情報
{{context_docs_combined}}
# 回答
2. 少数例プロンプト (Few-Shot Prompt)
期待する入力・出力のペアをいくつか示すことで、LLMの挙動をより厳密にガイドする。
あなたはユーザーの質問に答えるAIアシスタントです。
提供された「参照情報」のみに基づいて、質問に簡潔かつ正確に回答してください。
参照情報にない内容を推測したり、補完したりしないでください。情報がない場合は「関連情報が見つかりませんでした」と回答してください。
出力は必ずJSON形式で、`answer`と`references`(タイトルとURLのリスト)を含めてください。
### 例1
# 質問
RAGの主な利点は何ですか?
# 参照情報
[{"id": "doc_A", "title": "RAG概要", "url": "http://example.com/raga", "content": "RAGはLLMの知識を拡張し、幻覚を抑制します。最新情報への対応も可能です。"}]
# 回答
```json
{
"answer": "RAGの主な利点は、LLMの知識を拡張し、幻覚を抑制する点です。また、最新情報にも対応できます。",
"references": [{"title": "RAG概要", "url": "http://example.com/raga"}]
}
例2
質問
今日の天気はどうですか?
参照情報
[]
回答
{
"answer": "関連情報が見つかりませんでした。",
"references": []
}
あなたのタスク
質問
{{user_query}}
参照情報
{{context_docs_combined}}
回答
### 3. Chain-of-Thought制約型プロンプト (Chain-of-Thought Constrained Prompt)
LLMに思考プロセスを段階的に開示させ、コンテキストへの忠実性を高める。まず参照情報からキーポイントを抽出し、次にそれらを基に回答を構築するよう指示する。
```text
あなたはユーザーの質問に答えるAIアシスタントです。
以下の手順に従って、質問に回答してください。
1. **参照情報の分析**: 提供された「参照情報」から、質問に関連する主要な事実やキーワードを抽出してください。
2. **回答の生成**: 抽出した事実のみを基に、質問に簡潔かつ正確に回答してください。
3. **幻覚抑制**: 参照情報に記載されていない内容は、絶対に回答に含めないでください。情報がない場合は「関連情報が見つかりませんでした」と回答してください。
4. **出力形式**: 回答と参照情報(タイトルとURLのリスト)をJSON形式で出力してください。
# 質問
{{user_query}}
# 参照情報
{{context_docs_combined}}
# 回答の思考プロセス
- 質問: ...
- 関連する事実: ... (参照情報から抽出)
- 回答草稿: ...
# 最終回答
評価
RAGシステムの評価には、リトリーバル品質と生成品質の両方が重要です [4]。特に生成品質では、幻覚の有無、回答の関連性、忠実性が評価されます。
評価シナリオ
正例:
質問: 「Geminiの主な特徴は何ですか?」
コンテキスト: Geminiの具体的な特徴が明確に記述されたドキュメント。
期待される回答: コンテキストに基づいた正確な特徴リストと参照。
難例:
質問: 「2024年7月以降に発表されたGeminiの最新機能で、特に注目すべきものは?」
コンテキスト: 複数のリリースノートが混在しており、日付と機能が複雑に記述されているドキュメント。一部のリリースノートは質問範囲外。
期待される回答: 最新情報を正確に抽出し、複数のソースを統合した回答。質問範囲外の情報を無視すること。
コーナーケース:
質問: 「宇宙人はいますか?」
コンテキスト: 製品マニュアルや技術文書。宇宙人に関する記述は一切ない。
期待される回答: 「関連情報が見つかりませんでした。」
自動評価の擬似コード
Pythonを想定した擬似コード。採点ルーブリック、正規表現、関数評価を組み合わせます。
import re
import json
def evaluate_rag_output(user_query: str, retrieved_context: str, llm_output: str, expected_answer_keywords: list = None) -> dict:
"""
RAGシステムからのLLM出力を自動評価する擬似コード。
Args:
user_query: ユーザーの質問。
retrieved_context: RAGが取得したコンテキスト文字列。
llm_output: LLMが生成したJSON形式の出力文字列。
expected_answer_keywords: 期待される回答に含まれるべきキーワードのリスト (正答性の評価用)。
Returns:
評価結果を格納した辞書。
"""
score = 0
feedback = []
try:
output_data = json.loads(llm_output)
answer = output_data.get("answer", "")
references = output_data.get("references", [])
status = output_data.get("status", "UNKNOWN")
# 1. JSON形式のチェック
if not isinstance(answer, str) or not isinstance(references, list):
feedback.append("ERROR: JSONの形式が不正です。")
return {"score": 0, "feedback": feedback, "details": output_data}
score += 10 # 形式が正しければ基本点
# 2. 幻覚抑制 (Contextual Grounding)
# 簡易的な幻覚チェック: 回答中の固有名詞や主要キーワードがコンテキストに存在するか
is_grounded = True
if status == "SUCCESS" and answer != "関連情報が見つかりませんでした。":
answer_keywords = re.findall(r'\b[A-Z][a-z0-9]+\b|\b[ぁ-んァ-ヶ一-龠]{2,}\b', answer) # 大文字開始単語や2文字以上の日本語
for keyword in set(answer_keywords):
if len(keyword) > 1 and keyword not in ["です", "ます", "が", "の", "を"] and keyword not in retrieved_context:
# 完全に一致しないと厳しすぎるので、部分一致でも良いが、ここでは厳しく設定
# より高度な評価は、NLIモデルなどで回答とコンテキストの矛盾を検出
is_grounded = False
# feedback.append(f"WARNING: 回答中のキーワード '{keyword}' がコンテキストに見当たりません。")
if is_grounded:
score += 30
feedback.append("PASS: 回答はコンテキストに根ざしているようです。")
else:
score -= 10 # 幻覚は減点
feedback.append("FAIL: 回答にコンテキスト外の可能性のある情報が含まれています。")
elif status == "NO_CONTEXT_FOUND" and "関連情報が見つかりませんでした" in answer:
if not retrieved_context.strip(): # コンテキストが本当に空なら
score += 40
feedback.append("PASS: コンテキスト不在時の適切な応答。")
else:
score -= 20
feedback.append("WARNING: コンテキストがあるにも関わらず、情報なしと回答しました。")
# 3. 回答の関連性・完全性 (Relevance & Completeness)
if expected_answer_keywords and status == "SUCCESS":
matched_keywords = [k for k in expected_answer_keywords if k.lower() in answer.lower()]
if len(matched_keywords) == len(expected_answer_keywords):
score += 30
feedback.append("PASS: 期待されるキーワードが全て含まれています。")
elif len(matched_keywords) > 0:
score += 15
feedback.append(f"PARTIAL: 期待されるキーワードの一部が含まれています ({len(matched_keywords)}/{len(expected_answer_keywords)})。")
else:
feedback.append("FAIL: 期待されるキーワードが含まれていません。")
# 4. 参照の適切性 (Reference Accuracy)
if status == "SUCCESS" and references:
# 簡易チェック: 参照URLが形式として正しいか、コンテキストで示唆されたドキュメントと一致するか
valid_ref_count = 0
for ref in references:
if "url" in ref and re.match(r"https?://[^\s/$.?#].[^\s]*$", ref["url"]):
valid_ref_count += 1
if valid_ref_count == len(references):
score += 10
feedback.append("PASS: 参照情報が適切です。")
else:
feedback.append("WARNING: 参照情報の一部が不適切または不足しています。")
except json.JSONDecodeError:
feedback.append("ERROR: LLM出力が有効なJSONではありません。")
score = 0
except Exception as e:
feedback.append(f"ERROR: 評価中に予期せぬエラーが発生しました: {e}")
score = 0
return {"score": max(0, score), "feedback": feedback, "details": {"answer": answer, "references": references, "status": status}}
# 評価例
# context_docs_combined = "[{\"id\": \"doc_1\", \"title\": \"RAG概要\", \"url\": \"https://example.com/rag_overview\", \"content\": \"RAGは、検索エンジンから関連情報を取得し、その情報に基づいてLLMが応答を生成する技術です。これにより、幻覚を抑制し、LLMの知識を最新の状態に保つことができます。\"}]"
# llm_output_success = '{"answer": "RAGは、検索エンジンから情報を取得し、LLMが応答を生成することで、幻覚抑制と知識の最新性維持に貢献します。", "references": [{"title": "RAG概要", "url": "https://example.com/rag_overview"}], "status": "SUCCESS"}'
# eval_result = evaluate_rag_output("RAGの利点は?", context_docs_combined, llm_output_success, expected_answer_keywords=["幻覚抑制", "知識", "最新性"])
# print(eval_result)
プロンプト→モデル→評価→改良のループ
RAGシステムの性能向上は、プロンプトの設計、LLMの応答、評価フィードバックが相互に作用する反復的なプロセスによって実現されます。
graph TD
A["ユーザープロンプトとRAGコンテキスト"] --> B{LLM}
B -- 生成 --> C["LLM応答"]
C -- 評価 --> D{"評価モジュール"}
D -- スコアとフィードバック --> E["評価結果分析"]
E -- 課題特定/改善案 --> F["プロンプト改良"]
E -- 課題特定/改善案 --> G["RAGシステム改良"]
F --> A
G --> A
A[ユーザープロンプトとRAGコンテキスト]: ユーザーからの質問と、RAGシステムによって取得された関連情報が結合され、LLMへの入力プロンプトが構築されます。
B{LLM}: 構築されたプロンプトを受け取り、回答を生成します。
C[LLM応答]: LLMが生成した生の回答(ここではJSON形式)。
D{評価モジュール}: 生成されたLLM応答を自動評価します。忠実性、関連性、形式、幻覚の有無などをチェックし、スコアと詳細なフィードバックを生成します。
E[評価結果分析]: 評価モジュールからのスコアとフィードバックを人間または自動システムが分析し、改善すべき点(幻覚の発生、形式崩れ、関連性不足など)を特定します。
F[プロンプト改良]: 特定された課題に基づき、LLMへの指示をより明確にしたり、Few-Shot例を追加したり、CoTプロンプトの構造を見直したりしてプロンプトを改善します。
G[RAGシステム改良]: 評価結果から、RAGシステム自体の問題(例: 不適切なドキュメントチャンキング、検索アルゴリズムの不備、データ鮮度の問題)が示唆される場合、その改善を行います。例えば、埋め込みモデルの変更、リトリーバル戦略(リランキング、クエリ拡張)の導入、ドキュメントデータベースの更新などです [1, 5]。
このループを繰り返すことで、RAGシステムの全体的なパフォーマンスを継続的に向上させます。
失敗モードと抑制手法
| 失敗モード | 説明 | 抑制手法 |
|---|---|---|
| 幻覚 (Hallucination) | 提供されたコンテキストにない情報を捏造して回答する。 | – System指示: 「提供された情報のみで回答せよ」「推測はするな」と明確に指示する。 – 検証ステップ: 回答中の主要キーワードがコンテキスト内に存在するかを自動評価で確認する。 – Few-shot例: 幻覚が発生しない模範的な回答例をプロンプトに含める。 – Chain-of-Thought: 回答生成前に「参照情報から事実を抽出する」ステップを強制し、思考プロセスを明示させる。 |
| 様式崩れ (Format Breakdown) | 要求されたJSON形式やMarkdownリストなどの出力フォーマットが崩れる。 | – プロンプトの明確化: 出力形式をプロンプト内で厳密に定義し、強調する。 – Few-shot例: 期待する出力形式の成功例を複数提示する。 – 出力パースとリトライ: 生成された出力をシステム側でパースし、失敗した場合はLLMにエラーメッセージとともに再試行させる。 – 正規表現チェック: 出力後の検証で正規表現を用いてフォーマットをチェックする。 |
| 脱線 (Topic Derailment) | ユーザーの質問や提供されたコンテキストから逸脱した内容を生成する。 | – 役割指示: 「あなたは〇〇の専門家である」のように、LLMの役割とスコープを明確に定義する。 – 禁止事項の明示: プロンプト内で「〇〇については回答しない」と具体的に禁止事項を列挙する。 – 事前フィルタリング: 関連性の低い質問はLLMに渡す前にブロックする。 |
| 禁止事項違反 (Prohibition Violation) | センシティブな内容、不適切な言葉遣い、個人的見解の表明など、プロンプトで禁じられた行為を行う。 | – Systemレベルでの制限: モデル全体に安全ポリシーやガードレールを設定する。 – プロンプトでの強調: プロンプトの冒頭や末尾で、遵守すべき倫理的ガイドラインや禁止事項を再確認させる。 – 外部モデレーションAPI: LLMの出力後に別途モデレーションAPIを用いて不適切コンテンツを検出・フィルタリングする。 |
改良
上記「プロンプト→モデル→評価→改良のループ」に基づき、評価結果から得られた知見を用いて、主に以下の点を改良します。
プロンプトの洗練: 幻覚が多く発生する場合は、コンテキストへの忠実性を強調する指示やCoTステップを強化します。形式崩れが多い場合は、Few-Shot例を増やす、または出力形式の記述をより具体的にします。
RAGコンポーネントの最適化: 関連性の低いコンテキストが頻繁に渡されている場合、ドキュメントのチャンクサイズや重複戦略を見直します。検索アルゴリズム(例: BM25から埋め込み検索、ハイブリッド検索)やクエリリランキング手法を導入し、リトリーバル精度を高めます [1, 5]。
モデルの調整: 特定のユースケースに特化したドメインデータでファインチューニングを行うことで、RAGの効率と出力品質を向上させることも検討します(ただし、RAGの利点である頻繁なモデル更新不要という点は薄れる)。
再評価
改良が適用された後、同じ評価シナリオと自動評価スクリプトを用いて、システムのパフォーマンスを再測定します。これにより、改良が意図した効果をもたらしたかを確認し、新たな課題が生まれていないかをチェックします。特に、幻覚抑制の指標(例: 忠実性スコア)や出力形式の適合率に注目します。
まとめ
RAGは、LLMの知識を動的に拡張し、幻覚を抑制するための非常に効果的なアプローチです。プロンプトエンジニアリングの工夫(ゼロショット、Few-Shot、CoT制約型)、厳密な入出力契約、そして自動評価と継続的な改良のループを組み合わせることで、高精度で信頼性の高いQAシステムを構築できます。特に、プロンプト内で「提供された情報のみに基づいて回答する」という制約を徹底することが、幻覚抑制の鍵となります。これらの実践により、LLMの持つ可能性を最大限に引き出しつつ、その弱点を補完することが可能です。
参考文献
[1] Yunfan Shao et al. “Retrieval-Augmented Generation (RAG) for Large Language Models: A Survey”. arXiv:2312.10997. Submitted 2023年12月18日 (JST).
[2] Google Cloud. “RAG vs Fine-tuning: Which One to Choose for Your LLM Application”. Google Cloud Blog. Published 2023年10月26日 (JST). URL: https://cloud.google.com/blog/products/ai-machine-learning/rag-vs-fine-tuning-which-one-to-choose-for-your-llm-application
[3] Google AI Studio. “Best practices for prompt engineering with Gemini models”. Google AI Studio Docs. Last updated 2024年4月25日 (JST). URL: https://ai.google.com/docs/prompt-guidance
[4] Saurabh Kumar. “Evaluating RAG Systems: Metrics and Best Practices”. Towards Data Science (Medium). Published 2024年1月19日 (JST). URL: https://towardsdatascience.com/evaluating-rag-systems-metrics-and-best-practices-28751e16f398
[5] Anyscale. “Query Reranking in RAG”. Anyscale Blog. Published 2024年3月14日 (JST). URL: https://www.anyscale.com/blog/query-reranking-in-rag-part-1

