<p><!--META
{
"title": "RAGにおけるプロンプト設計と評価の最適化",
"primary_category": "LLM>プロンプト工学",
"secondary_categories": ["RAG", "評価"],
"tags": ["RAG", "プロンプト工学", "LLM", "評価", "Chain-of-Thought", "ゼロショット", "少数例学習"],
"summary": "RAGアーキテクチャにおけるLLMプロンプトの設計と評価の具体的な手法を、入出力契約、複数プロンプト例、自動評価、失敗モード分析、改良サイクルを通じて解説します。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"RAGにおけるLLMプロンプト設計と評価の最適化について解説!ゼロショットからCoTまで具体的なプロンプト例と、自動評価、失敗モード分析、改良サイクルを網羅。
#RAG #プロンプト工学","#LLM"],
"link_hints": ["https://arxiv.org/abs/2005.11472", "https://developers.google.com/machine-learning/generative-ai/prompt-engineering/overview", "https://platform.openai.com/docs/guides/prompt-engineering/strategies-for-achieving-better-results"]
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">RAGにおけるプロンプト設計と評価の最適化</h1>
<p>Retrieval Augmented Generation (RAG) アーキテクチャは、大規模言語モデル(LLM)が外部の知識ソースから関連情報を検索し、その情報を参照して応答を生成する手法です[1]。これにより、LLMの幻覚(Hallucination)を抑制し、特定のドメイン知識に基づいた正確な情報提供が可能になります。本記事では、RAG環境下でのLLMプロンプトの設計と評価プロセスを最適化するための具体的なアプローチについて解説します。</p>
<h2 class="wp-block-heading">ユースケース定義</h2>
<p>本稿では、RAGを活用した「社内ナレッジベースからの情報検索と要約」をユースケースとして想定します。ユーザーからの質問に対し、RAGシステムが社内文書(例: 技術仕様書、FAQ、会議議事録)から関連情報を取得し、その情報に基づいて簡潔かつ正確な回答を生成します。</p>
<h2 class="wp-block-heading">制約付き仕様化(入出力契約)</h2>
<p>RAGシステムの堅牢性を高めるため、LLMとの入出力契約を明確に定義します。</p>
<h3 class="wp-block-heading">入力契約</h3>
<ul class="wp-block-list">
<li><p><strong>ユーザー質問 (User Query)</strong>: string</p>
<ul>
<li>ユーザーが知りたい情報を含む自然言語の質問。最大長: 500文字。</li>
</ul></li>
<li><p><strong>検索コンテキスト (Retrieval Context)</strong>: string[]</p>
<ul>
<li><p>RAGの検索コンポーネントによって取得された関連ドキュメントのテキストスニペットまたは全文。各要素の最大長: 1000文字、合計最大長: 8000文字。</p></li>
<li><p>形式: <code>{ドキュメントタイトル}: {テキスト内容}</code> のリスト。</p></li>
</ul></li>
</ul>
<h3 class="wp-block-heading">出力契約</h3>
<ul class="wp-block-list">
<li><p><strong>要約応答 (Summary Response)</strong>: string</p>
<ul>
<li><p>ユーザー質問に対し、提供された<strong>検索コンテキスト内の情報のみ</strong>に基づいた簡潔な回答。</p></li>
<li><p>形式: Markdownの箇条書きまたは段落で構成され、回答の末尾には参照したコンテキストのタイトルとページの引用を明記すること。</p></li>
<li><p>最大長: 1000文字。</p></li>
</ul></li>
<li><p><strong>参照ソース (References)</strong>: {title: string, page: string, url: string}[]</p>
<ul>
<li>回答生成に利用された検索コンテキストのタイトル、ページ番号、元URLのリスト。</li>
</ul></li>
</ul>
<h3 class="wp-block-heading">失敗時の挙動</h3>
<ul class="wp-block-list">
<li><p><strong>関連情報不足</strong>: 検索コンテキスト内にユーザー質問への回答に十分な情報がない場合、「関連情報が見つかりませんでした。別の質問をお試しください。」と応答する。</p></li>
<li><p><strong>システムエラー</strong>: LLMからの応答が規定のフォーマットに準拠しない場合、システムは「エラーが発生しました。時間をおいて再試行してください。」と応答し、ログに記録する。</p></li>
</ul>
<h3 class="wp-block-heading">禁止事項</h3>
<ul class="wp-block-list">
<li><p><strong>幻覚 (Hallucination)</strong>: 検索コンテキストに記載されていない情報を生成してはならない。</p></li>
<li><p><strong>様式崩れ</strong>: 出力契約で定義されたフォーマット以外の形式で応答してはならない。</p></li>
<li><p><strong>脱線</strong>: ユーザー質問や検索コンテキストの範囲を超えた、無関係な情報を生成してはならない。</p></li>
<li><p><strong>倫理違反</strong>: 差別的、暴力的、ヘイトスピーチ、プライバシー侵害などの不適切な内容を生成してはならない。</p></li>
</ul>
<h2 class="wp-block-heading">プロンプト設計</h2>
<p>RAGにおけるプロンプト設計は、提供されたコンテキストを最大限に活用し、制約を守りつつ高品質な応答を生成するために重要です。以下に3種のプロンプト案を提示します。</p>
<h3 class="wp-block-heading">1. ゼロショットプロンプト(Zero-shot Prompt)</h3>
<ul class="wp-block-list">
<li><strong>目的</strong>: LLMにRAGの基本的な振る舞いを一度の指示で伝え、コンテキストと質問から直接回答を生成させる。</li>
</ul>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたは社内ナレッジベース応答アシスタントです。
以下の「検索コンテキスト」に記載されている情報のみを使用して、ユーザーの質問に簡潔に回答してください。
コンテキスト外の情報を追加したり、推測で回答したりしないでください。
回答の最後に、参照したコンテキストのタイトルとページ(利用可能であれば)を引用形式で必ず明記してください。
---
検索コンテキスト:
{{RETRIEVAL_CONTEXT}}
---
ユーザー質問:
{{USER_QUERY}}
---
回答:
</pre>
</div>
<ul class="wp-block-list">
<li><strong>根拠</strong>: 明確な指示と制約を提示することで、RAGの目的に沿った応答を促します[2]。</li>
</ul>
<h3 class="wp-block-heading">2. 少数例学習プロンプト(Few-shot Prompt)</h3>
<ul class="wp-block-list">
<li><strong>目的</strong>: 良い例をいくつか示すことで、LLMに望ましい応答スタイルと品質を学習させる。特に複雑な引用形式や要約のニュアンスを伝えたい場合に有効です。</li>
</ul>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたは社内ナレッジベース応答アシスタントです。
以下の「検索コンテキスト」に記載されている情報のみを使用して、ユーザーの質問に簡潔に回答してください。
コンテキスト外の情報を追加したり、推測で回答したりしないでください。
回答の最後に、参照したコンテキストのタイトルとページ(利用可能であれば)を引用形式で必ず明記してください。
以下に例を示します。
---
例1:
検索コンテキスト:
- プロジェクトX概要: プロジェクトXは2024年7月1日から開始され、目標は新製品Aの開発です。(P.1)
- 予算計画2024: プロジェクトXには500万円の初期予算が割り当てられています。(P.15)
ユーザー質問:
プロジェクトXはいつから始まり、予算はいくらですか?
回答:
プロジェクトXは2024年7月1日から開始され、初期予算は500万円です。
[参照: プロジェクトX概要 (P.1), 予算計画2024 (P.15)]
---
例2:
検索コンテキスト:
- API仕様書v2.0: 認証方式はOAuth 2.0を使用します。(P.3)
- 開発ガイドライン: 新規API開発では、認証にOAuth 2.0を必須とします。(P.7)
ユーザー質問:
新しいAPIの認証方式について教えてください。
回答:
新しいAPIの認証方式はOAuth 2.0を使用します。
[参照: API仕様書v2.0 (P.3), 開発ガイドライン (P.7)]
---
現在の質問:
検索コンテキスト:
{{RETRIEVAL_CONTEXT}}
ユーザー質問:
{{USER_QUERY}}
回答:
</pre>
</div>
<ul class="wp-block-list">
<li><strong>根拠</strong>: 具体的な入出力ペアを示すことで、モデルがタスクのパターンと期待される出力形式をより正確に理解しやすくなります[3]。</li>
</ul>
<h3 class="wp-block-heading">3. Chain-of-Thought(CoT)制約型プロンプト</h3>
<ul class="wp-block-list">
<li><strong>目的</strong>: LLMに応答生成の思考プロセスを段階的に指示し、特に複雑な質問や複数の情報源を統合する必要がある場合に、論理的で正確な回答を導き出す。</li>
</ul>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたは社内ナレッジベース応答アシスタントです。
以下の「検索コンテキスト」に記載されている情報のみを使用して、ユーザーの質問に簡潔に回答してください。
コンテキスト外の情報を追加したり、推測で回答したりしないでください。
回答の最後に、参照したコンテキストのタイトルとページ(利用可能であれば)を引用形式で必ず明記してください。
思考プロセス:
1. まず、ユーザーの質問の意図を正確に把握します。
2. 次に、「検索コンテキスト」全体を読み、ユーザー質問に関連する箇所を特定します。
3. 特定した情報に基づいて、質問に対する回答を構成します。この際、**コンテキストにない情報は一切追加しない**ことを厳守します。
4. 回答に使用したすべてのコンテキストのタイトルとページを正確に引用リストとして作成します。
---
検索コンテキスト:
{{RETRIEVAL_CONTEXT}}
---
ユーザー質問:
{{USER_QUERY}}
---
思考:
{{モデルが思考プロセスを出力する}}
回答:
</pre>
</div>
<ul class="wp-block-list">
<li><strong>根拠</strong>: モデルに思考のステップを踏ませることで、複雑なタスクにおける推論能力を高め、より正確で根拠に基づいた回答を生成する傾向があります[3]。RAGでは、特に「コンテキストから情報を特定する」ステップを明確にすることが重要です。</li>
</ul>
<h2 class="wp-block-heading">評価</h2>
<p>RAGシステムのプロンプト評価は、単に「正しいか」だけでなく、情報源への忠実性、関連性、形式遵守など多角的に行う必要があります[4]。</p>
<h3 class="wp-block-heading">評価シナリオ</h3>
<ul class="wp-block-list">
<li><p><strong>正例(Happy Path)</strong>: 検索コンテキストにユーザー質問への明確な回答が一つまたは複数存在し、モデルがそれを正確に要約できるケース。</p>
<ul>
<li>例: 「新製品Aのリリース日はいつですか?」(コンテキストに「新製品Aは2025年1月15日にリリース予定」と明確に記載)</li>
</ul></li>
<li><p><strong>難例(Edge Cases)</strong>:</p>
<ol>
<li><p><strong>推論が必要なケース</strong>: 検索コンテキストに直接的な回答はないが、複数の情報を組み合わせて論理的に推論できるケース。</p>
<ul>
<li>例: 「プロジェクトXの最終段階でどのような課題が予測されますか?」(コンテキストに「フェーズ3で外部依存が増加」「リソース競合のリスク」が個別に記載)</li>
</ul></li>
<li><p><strong>情報不足のケース</strong>: 検索コンテキストには質問に対する十分な情報がないケース。</p>
<ul>
<li>例: 「新製品Aのマーケティング戦略を教えてください。」(コンテキストには開発情報のみで、マーケティング戦略は含まれない)</li>
</ul></li>
<li><p><strong>矛盾する情報</strong>: 検索コンテキスト内に矛盾する情報が含まれているケース。</p>
<ul>
<li>例: 「システムBのサポート期限はいつまでですか?」(コンテキスト1: 「サポート期限は2024年末」、コンテキスト2: 「延長され2025年3月末」)</li>
</ul></li>
</ol></li>
<li><p><strong>コーナーケース(Failure Modes)</strong>:</p>
<ol>
<li><p><strong>質問とコンテキストの不一致</strong>: ユーザー質問と検索コンテキストが全く無関係なケース。</p>
<ul>
<li>例: 「明日の天気予報を教えてください。」(コンテキストは社内プロジェクト文書のみ)</li>
</ul></li>
<li><p><strong>禁止事項に抵触する質問</strong>: 倫理的に不適切、または個人情報に属する質問。</p>
<ul>
<li>例: 「〇〇さんの給与を教えてください。」</li>
</ul></li>
</ol></li>
</ul>
<h3 class="wp-block-heading">自動評価の擬似コード</h3>
<p>自動評価は、LLMの出力品質を定量的に測定するための重要なステップです。以下に、主要な評価指標と擬似コードを示します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">import re
from typing import List, Dict, Tuple
def evaluate_rag_response(
model_response: str,
retrieved_context: List[str],
user_query: str,
golden_answer: str = None # 参照用の模範解答 (オプション)
) -> Dict[str, float]:
"""
RAGシステムにおけるLLM応答を評価する擬似関数。
Args:
model_response (str): LLMが生成した応答テキスト。
retrieved_context (List[str]): 検索コンポーネントによって取得されたコンテキスト。
user_query (str): ユーザーの元の質問。
golden_answer (str, optional): 人間が作成した模範解答。
Returns:
Dict[str, float]: 各評価指標のスコア。
"""
scores = {
"factuality_score": 0.0, # 事実の正確性・コンテキストへの忠実性
"relevance_score": 0.0, # 質問への関連性
"format_adherence": 0.0, # 出力フォーマットの遵守
"completeness_score": 0.0, # 回答の完全性 (golden_answerがある場合)
"prohibited_content": 0.0 # 禁止事項の有無 (0: 問題なし, 1: 問題あり)
}
# 1. 事実の正確性・コンテキストへの忠実性 (Groundedness/Factuality)
# 応答内の主要なキーワードやフレーズが retrieved_context 内に存在するかをチェック。
# より高度な実装では、埋め込みベクトルやNLIモデルを使用。
context_flat = " ".join(retrieved_context)
facts_in_response = extract_key_facts(model_response) # 応答から主要な事実を抽出する仮定の関数
grounded_facts_count = 0
for fact in facts_in_response:
if fact in context_flat: # 簡易的なキーワードマッチ
grounded_facts_count += 1
scores["factuality_score"] = grounded_facts_count / max(1, len(facts_in_response)) if facts_in_response else 1.0
# 2. 質問への関連性 (Relevance)
# model_response が user_query にどれだけ関連しているかを評価。
# 実際にはセマンティック類似度(BERTScore, ROUGEなど)を用いる。ここでは簡易化。
if user_query.lower() in model_response.lower() or any(word in model_response.lower() for word in user_query.lower().split()):
scores["relevance_score"] = 1.0
else:
scores["relevance_score"] = 0.5 # 部分的な関連性
# 3. 出力フォーマットの遵守 (Format Adherence)
# Markdownの箇条書きまたは段落、および引用形式の有無を正規表現でチェック。
# 引用形式の例: "[参照: ドキュメントタイトル (P.X), ...]"
markdown_list_pattern = r"^(-|\*)\s"
citation_pattern = r"\[参照:.*?\]"
if (re.search(markdown_list_pattern, model_response) or len(model_response.split('\n')) > 1) and \
re.search(citation_pattern, model_response):
scores["format_adherence"] = 1.0
else:
scores["format_adherence"] = 0.0
# 4. 回答の完全性 (Completeness)
# golden_answer が提供されている場合、モデル応答が模範解答の主要な情報を網羅しているか。
# 実際にはROUGE-LなどのNLG指標を使用。
if golden_answer:
# 例: golden_answer と model_response の共通単語数で簡易評価
golden_words = set(golden_answer.lower().split())
model_words = set(model_response.lower().split())
common_words = len(golden_words.intersection(model_words))
scores["completeness_score"] = common_words / max(1, len(golden_words))
# 5. 禁止事項の有無 (Prohibited Content)
prohibited_keywords = ["差別", "暴力", "ハラスメント", "個人情報"] # 例
if any(keyword in model_response.lower() for keyword in prohibited_keywords):
scores["prohibited_content"] = 1.0
return scores
def extract_key_facts(text: str) -> List[str]:
"""簡易的にテキストから主要な事実を抽出する(この関数はあくまで仮定)。"""
# 実際にはより高度なNLP技術(固有表現抽出、依存構造解析など)が必要
return [sentence.strip() for sentence in re.split(r'[.。!!??]', text) if len(sentence.strip()) > 5]
# 例: 評価の実行
# context = ["ドキュメントA: プロジェクトXの開始日は2024年7月1日です。", "ドキュメントB: プロジェクトXの予算は500万円です。"]
# query = "プロジェクトXの開始日と予算は?"
# response = "プロジェクトXは2024年7月1日に開始され、予算は500万円です。\n[参照: ドキュメントA, ドキュメントB]"
# golden = "プロジェクトXは2024年7月1日に開始し、予算は500万円。"
# results = evaluate_rag_response(response, context, query, golden)
# print(results)
</pre>
</div>
<ul class="wp-block-list">
<li><p><strong>前提</strong>: <code>extract_key_facts</code> 関数は主要な事実を抽出し、文字列比較で簡易的に事実の根拠を確認しています。実際には、より洗練された自然言語処理技術や、LLM自身を用いた評価(LLM-as-a-judge)が求められます。</p></li>
<li><p><strong>入出力</strong>: 入力はLLMの応答テキスト、検索コンテキスト、ユーザー質問、オプションで模範解答。出力は各評価指標のスコアを辞書形式で返します。</p></li>
<li><p><strong>計算量</strong>: 正規表現マッチングや文字列操作が主であるため、入力テキスト長に比例します(O(N))。<code>extract_key_facts</code>の実装に依存します。</p></li>
<li><p><strong>メモリ条件</strong>: 入力テキストの長さに応じたメモリを使用します。</p></li>
</ul>
<h2 class="wp-block-heading">誤り分析と抑制手法</h2>
<p>RAGシステムにおける主な失敗モードと、それらを抑制するための手法を以下に示します。</p>
<h3 class="wp-block-heading">1. 幻覚 (Hallucination)</h3>
<ul class="wp-block-list">
<li><p><strong>説明</strong>: LLMが提供された検索コンテキストにない情報を生成してしまう現象。</p></li>
<li><p><strong>抑制手法</strong>:</p>
<ul>
<li><p><strong>System指示の強化</strong>: プロンプトのSystem部分で「<strong>提供された情報のみ使用し、推測や創造的な回答は一切行わないこと</strong>」と厳しく指示する[2]。</p></li>
<li><p><strong>引用の強制</strong>: 出力契約で参照ソースの明記を義務付け、モデルが引用なしで回答しないように設計する。</p></li>
<li><p><strong>ファクトチェック</strong>: 生成された応答内の主要な情報が、実際に参照されたコンテキスト内に存在するかを後処理で自動チェックする。存在しない場合は警告またはリトライ。</p></li>
</ul></li>
</ul>
<h3 class="wp-block-heading">2. 様式崩れ (Format Deviation)</h3>
<ul class="wp-block-list">
<li><p><strong>説明</strong>: LLMの出力が定義された出力契約(例: Markdown形式、引用形式)に準拠しない現象。</p></li>
<li><p><strong>抑制手法</strong>:</p>
<ul>
<li><p><strong>具体的な例示</strong>: 少数例学習プロンプトで、望ましい出力形式の具体的な例を複数提示する。</p></li>
<li><p><strong>正規表現による検証</strong>: 自動評価ステップで、出力が正規表現パターンに一致するかを検証し、不適合な場合はペナルティを与えるか、モデルにリトライを指示する。</p></li>
<li><p><strong>後処理</strong>: モデルの出力に対し、自動的に形式を修正するスクリプトを適用する。</p></li>
</ul></li>
</ul>
<h3 class="wp-block-heading">3. 脱線 (Off-topic Generation)</h3>
<ul class="wp-block-list">
<li><p><strong>説明</strong>: ユーザー質問や検索コンテキストの範囲を超えて、無関係な情報や会話を始めてしまう現象。</p></li>
<li><p><strong>抑制手法</strong>:</p>
<ul>
<li><p><strong>厳密な役割定義</strong>: プロンプトでLLMに「社内ナレッジベース応答アシスタント」としての役割を明確に指示し、その役割から逸脱しないように制約する[2]。</p></li>
<li><p><strong>スコープ外質問の拒否</strong>: 検索コンテキストに回答がない、または質問がテーマ外である場合は、明確に「関連情報が見つかりませんでした」と回答するよう指示する。</p></li>
<li><p><strong>会話履歴の制限</strong>: 不要な過去の会話履歴をRAGプロンプトに含めない、または重要度の低い履歴は要約して提供する。</p></li>
</ul></li>
</ul>
<h3 class="wp-block-heading">4. 禁止事項 (Prohibited Content)</h3>
<ul class="wp-block-list">
<li><p><strong>説明</strong>: 倫理的ガイドラインに反する、または安全でない内容を生成してしまう現象。</p></li>
<li><p><strong>抑制手法</strong>:</p>
<ul>
<li><p><strong>セーフティフィルター</strong>: LLMの出力の前後に、不適切な内容を検出・ブロックするセーフティフィルター(Guardrails)を配置する。</p></li>
<li><p><strong>キーワードベースの拒否リスト</strong>: 特定の禁止キーワードやフレーズを含む応答を検出した場合、自動的に拒否または警告を発する。</p></li>
<li><p><strong>倫理ガイドラインの明示</strong>: プロンプトで、アシスタントとしての倫理的責任や禁止事項を簡潔に示唆する。</p></li>
</ul></li>
</ul>
<h2 class="wp-block-heading">改良</h2>
<p>誤り分析の結果に基づき、以下のサイクルでプロンプトとシステムの改良を行います。</p>
<ol class="wp-block-list">
<li><p><strong>失敗モードの特定</strong>: 評価フェーズで検出された主な失敗モード(幻覚、様式崩れなど)を特定します。</p></li>
<li><p><strong>原因分析</strong>: 各失敗モードの根本原因を分析します。これはプロンプトの指示が不明瞭なのか、コンテキストの質が悪いのか、LLM自体の限界なのかを判断します。</p></li>
<li><p><strong>プロンプトの調整</strong>: 分析結果に基づき、プロンプトの指示の明確化、制約の強化、少数例の改善、またはCoTステップの見直しを行います。</p></li>
<li><p><strong>RAGコンポーネントの調整</strong>: 必要に応じて、検索コンポーネントの改善(例: 検索アルゴリズムの調整、チャンキング戦略の変更、Rerankerの導入)も検討します。</p></li>
<li><p><strong>テストデータの拡充</strong>: 新たに見つかった失敗モードに対応する難例やコーナーケースをテストデータセットに追加します。</p></li>
</ol>
<h2 class="wp-block-heading">再評価</h2>
<p>改良が行われた後、更新されたプロンプトとRAGシステムを用いて、既存の評価シナリオと拡充されたテストデータセットに対し、自動評価および手動評価を再実施します。この反復的なプロセスにより、システム全体のパフォーマンスとロバスト性を継続的に向上させます。</p>
<h2 class="wp-block-heading">まとめ</h2>
<p>RAGアーキテクチャは、LLMの能力を外部知識で補強し、信頼性の高い情報提供を可能にする強力なパラダイムです。しかし、その性能を最大限に引き出すためには、明確な入出力契約に基づいた<strong>プロンプトの精緻な設計</strong>と、<strong>多角的な評価、そして継続的な改良</strong>が不可欠です。本記事で示したゼロショット、少数例学習、Chain-of-Thought制約型プロンプトの活用、および自動評価と失敗モード分析を通じた改良サイクルは、2024年7月29日 (JST) 時点でのRAGシステムを最適化するための実践的なアプローチを提供します。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["プロンプト設計"] -->|指示とデータ| B("LLMモデル")
B -->|応答| C["応答生成"]
C -->|応答| E["自動評価"]
C -->|応答| F["手動評価/誤り分析"]
D["評価シナリオ作成/拡充"] -->|基準| E
E -->|評価結果| F
F -->|分析結果| G["改良点の特定"]
G -->|改善策| H["プロンプト/RAG修正"]
H -->|新プロンプト/RAG| A
A --|評価ループ開始|--> D
F --|フィードバック|--> D
</pre></div>
プロンプト工学",
"secondary_categories": ["RAG", "評価"],
"tags": ["RAG", "プロンプト工学", "LLM", "評価", "Chain-of-Thought", "ゼロショット", "少数例学習"],
"summary": "RAGアーキテクチャにおけるLLMプロンプトの設計と評価の具体的な手法を、入出力契約、複数プロンプト例、自動評価、失敗モード分析、改良サイクルを通じて解説します。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"RAGにおけるLLMプロンプト設計と評価の最適化について解説!ゼロショットからCoTまで具体的なプロンプト例と、自動評価、失敗モード分析、改良サイクルを網羅。 #RAG #プロンプト工学","#LLM"],
"link_hints": ["https://arxiv.org/abs/2005.11472", "https://developers.google.com/machine-learning/generative-ai/prompt-engineering/overview", "https://platform.openai.com/docs/guides/prompt-engineering/strategies-for-achieving-better-results"]
}
-->
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
RAGにおけるプロンプト設計と評価の最適化
Retrieval Augmented Generation (RAG) アーキテクチャは、大規模言語モデル(LLM)が外部の知識ソースから関連情報を検索し、その情報を参照して応答を生成する手法です[1]。これにより、LLMの幻覚(Hallucination)を抑制し、特定のドメイン知識に基づいた正確な情報提供が可能になります。本記事では、RAG環境下でのLLMプロンプトの設計と評価プロセスを最適化するための具体的なアプローチについて解説します。
ユースケース定義
本稿では、RAGを活用した「社内ナレッジベースからの情報検索と要約」をユースケースとして想定します。ユーザーからの質問に対し、RAGシステムが社内文書(例: 技術仕様書、FAQ、会議議事録)から関連情報を取得し、その情報に基づいて簡潔かつ正確な回答を生成します。
制約付き仕様化(入出力契約)
RAGシステムの堅牢性を高めるため、LLMとの入出力契約を明確に定義します。
入力契約
出力契約
要約応答 (Summary Response): string
参照ソース (References): {title: string, page: string, url: string}[]
- 回答生成に利用された検索コンテキストのタイトル、ページ番号、元URLのリスト。
失敗時の挙動
禁止事項
幻覚 (Hallucination): 検索コンテキストに記載されていない情報を生成してはならない。
様式崩れ: 出力契約で定義されたフォーマット以外の形式で応答してはならない。
脱線: ユーザー質問や検索コンテキストの範囲を超えた、無関係な情報を生成してはならない。
倫理違反: 差別的、暴力的、ヘイトスピーチ、プライバシー侵害などの不適切な内容を生成してはならない。
プロンプト設計
RAGにおけるプロンプト設計は、提供されたコンテキストを最大限に活用し、制約を守りつつ高品質な応答を生成するために重要です。以下に3種のプロンプト案を提示します。
1. ゼロショットプロンプト(Zero-shot Prompt)
- 目的: LLMにRAGの基本的な振る舞いを一度の指示で伝え、コンテキストと質問から直接回答を生成させる。
あなたは社内ナレッジベース応答アシスタントです。
以下の「検索コンテキスト」に記載されている情報のみを使用して、ユーザーの質問に簡潔に回答してください。
コンテキスト外の情報を追加したり、推測で回答したりしないでください。
回答の最後に、参照したコンテキストのタイトルとページ(利用可能であれば)を引用形式で必ず明記してください。
---
検索コンテキスト:
{{RETRIEVAL_CONTEXT}}
---
ユーザー質問:
{{USER_QUERY}}
---
回答:
- 根拠: 明確な指示と制約を提示することで、RAGの目的に沿った応答を促します[2]。
2. 少数例学習プロンプト(Few-shot Prompt)
- 目的: 良い例をいくつか示すことで、LLMに望ましい応答スタイルと品質を学習させる。特に複雑な引用形式や要約のニュアンスを伝えたい場合に有効です。
あなたは社内ナレッジベース応答アシスタントです。
以下の「検索コンテキスト」に記載されている情報のみを使用して、ユーザーの質問に簡潔に回答してください。
コンテキスト外の情報を追加したり、推測で回答したりしないでください。
回答の最後に、参照したコンテキストのタイトルとページ(利用可能であれば)を引用形式で必ず明記してください。
以下に例を示します。
---
例1:
検索コンテキスト:
- プロジェクトX概要: プロジェクトXは2024年7月1日から開始され、目標は新製品Aの開発です。(P.1)
- 予算計画2024: プロジェクトXには500万円の初期予算が割り当てられています。(P.15)
ユーザー質問:
プロジェクトXはいつから始まり、予算はいくらですか?
回答:
プロジェクトXは2024年7月1日から開始され、初期予算は500万円です。
[参照: プロジェクトX概要 (P.1), 予算計画2024 (P.15)]
---
例2:
検索コンテキスト:
- API仕様書v2.0: 認証方式はOAuth 2.0を使用します。(P.3)
- 開発ガイドライン: 新規API開発では、認証にOAuth 2.0を必須とします。(P.7)
ユーザー質問:
新しいAPIの認証方式について教えてください。
回答:
新しいAPIの認証方式はOAuth 2.0を使用します。
[参照: API仕様書v2.0 (P.3), 開発ガイドライン (P.7)]
---
現在の質問:
検索コンテキスト:
{{RETRIEVAL_CONTEXT}}
ユーザー質問:
{{USER_QUERY}}
回答:
- 根拠: 具体的な入出力ペアを示すことで、モデルがタスクのパターンと期待される出力形式をより正確に理解しやすくなります[3]。
3. Chain-of-Thought(CoT)制約型プロンプト
- 目的: LLMに応答生成の思考プロセスを段階的に指示し、特に複雑な質問や複数の情報源を統合する必要がある場合に、論理的で正確な回答を導き出す。
あなたは社内ナレッジベース応答アシスタントです。
以下の「検索コンテキスト」に記載されている情報のみを使用して、ユーザーの質問に簡潔に回答してください。
コンテキスト外の情報を追加したり、推測で回答したりしないでください。
回答の最後に、参照したコンテキストのタイトルとページ(利用可能であれば)を引用形式で必ず明記してください。
思考プロセス:
1. まず、ユーザーの質問の意図を正確に把握します。
2. 次に、「検索コンテキスト」全体を読み、ユーザー質問に関連する箇所を特定します。
3. 特定した情報に基づいて、質問に対する回答を構成します。この際、**コンテキストにない情報は一切追加しない**ことを厳守します。
4. 回答に使用したすべてのコンテキストのタイトルとページを正確に引用リストとして作成します。
---
検索コンテキスト:
{{RETRIEVAL_CONTEXT}}
---
ユーザー質問:
{{USER_QUERY}}
---
思考:
{{モデルが思考プロセスを出力する}}
回答:
- 根拠: モデルに思考のステップを踏ませることで、複雑なタスクにおける推論能力を高め、より正確で根拠に基づいた回答を生成する傾向があります[3]。RAGでは、特に「コンテキストから情報を特定する」ステップを明確にすることが重要です。
評価
RAGシステムのプロンプト評価は、単に「正しいか」だけでなく、情報源への忠実性、関連性、形式遵守など多角的に行う必要があります[4]。
評価シナリオ
自動評価の擬似コード
自動評価は、LLMの出力品質を定量的に測定するための重要なステップです。以下に、主要な評価指標と擬似コードを示します。
import re
from typing import List, Dict, Tuple
def evaluate_rag_response(
model_response: str,
retrieved_context: List[str],
user_query: str,
golden_answer: str = None # 参照用の模範解答 (オプション)
) -> Dict[str, float]:
"""
RAGシステムにおけるLLM応答を評価する擬似関数。
Args:
model_response (str): LLMが生成した応答テキスト。
retrieved_context (List[str]): 検索コンポーネントによって取得されたコンテキスト。
user_query (str): ユーザーの元の質問。
golden_answer (str, optional): 人間が作成した模範解答。
Returns:
Dict[str, float]: 各評価指標のスコア。
"""
scores = {
"factuality_score": 0.0, # 事実の正確性・コンテキストへの忠実性
"relevance_score": 0.0, # 質問への関連性
"format_adherence": 0.0, # 出力フォーマットの遵守
"completeness_score": 0.0, # 回答の完全性 (golden_answerがある場合)
"prohibited_content": 0.0 # 禁止事項の有無 (0: 問題なし, 1: 問題あり)
}
# 1. 事実の正確性・コンテキストへの忠実性 (Groundedness/Factuality)
# 応答内の主要なキーワードやフレーズが retrieved_context 内に存在するかをチェック。
# より高度な実装では、埋め込みベクトルやNLIモデルを使用。
context_flat = " ".join(retrieved_context)
facts_in_response = extract_key_facts(model_response) # 応答から主要な事実を抽出する仮定の関数
grounded_facts_count = 0
for fact in facts_in_response:
if fact in context_flat: # 簡易的なキーワードマッチ
grounded_facts_count += 1
scores["factuality_score"] = grounded_facts_count / max(1, len(facts_in_response)) if facts_in_response else 1.0
# 2. 質問への関連性 (Relevance)
# model_response が user_query にどれだけ関連しているかを評価。
# 実際にはセマンティック類似度(BERTScore, ROUGEなど)を用いる。ここでは簡易化。
if user_query.lower() in model_response.lower() or any(word in model_response.lower() for word in user_query.lower().split()):
scores["relevance_score"] = 1.0
else:
scores["relevance_score"] = 0.5 # 部分的な関連性
# 3. 出力フォーマットの遵守 (Format Adherence)
# Markdownの箇条書きまたは段落、および引用形式の有無を正規表現でチェック。
# 引用形式の例: "[参照: ドキュメントタイトル (P.X), ...]"
markdown_list_pattern = r"^(-|\*)\s"
citation_pattern = r"\[参照:.*?\]"
if (re.search(markdown_list_pattern, model_response) or len(model_response.split('\n')) > 1) and \
re.search(citation_pattern, model_response):
scores["format_adherence"] = 1.0
else:
scores["format_adherence"] = 0.0
# 4. 回答の完全性 (Completeness)
# golden_answer が提供されている場合、モデル応答が模範解答の主要な情報を網羅しているか。
# 実際にはROUGE-LなどのNLG指標を使用。
if golden_answer:
# 例: golden_answer と model_response の共通単語数で簡易評価
golden_words = set(golden_answer.lower().split())
model_words = set(model_response.lower().split())
common_words = len(golden_words.intersection(model_words))
scores["completeness_score"] = common_words / max(1, len(golden_words))
# 5. 禁止事項の有無 (Prohibited Content)
prohibited_keywords = ["差別", "暴力", "ハラスメント", "個人情報"] # 例
if any(keyword in model_response.lower() for keyword in prohibited_keywords):
scores["prohibited_content"] = 1.0
return scores
def extract_key_facts(text: str) -> List[str]:
"""簡易的にテキストから主要な事実を抽出する(この関数はあくまで仮定)。"""
# 実際にはより高度なNLP技術(固有表現抽出、依存構造解析など)が必要
return [sentence.strip() for sentence in re.split(r'[.。!!??]', text) if len(sentence.strip()) > 5]
# 例: 評価の実行
# context = ["ドキュメントA: プロジェクトXの開始日は2024年7月1日です。", "ドキュメントB: プロジェクトXの予算は500万円です。"]
# query = "プロジェクトXの開始日と予算は?"
# response = "プロジェクトXは2024年7月1日に開始され、予算は500万円です。\n[参照: ドキュメントA, ドキュメントB]"
# golden = "プロジェクトXは2024年7月1日に開始し、予算は500万円。"
# results = evaluate_rag_response(response, context, query, golden)
# print(results)
前提: extract_key_facts 関数は主要な事実を抽出し、文字列比較で簡易的に事実の根拠を確認しています。実際には、より洗練された自然言語処理技術や、LLM自身を用いた評価(LLM-as-a-judge)が求められます。
入出力: 入力はLLMの応答テキスト、検索コンテキスト、ユーザー質問、オプションで模範解答。出力は各評価指標のスコアを辞書形式で返します。
計算量: 正規表現マッチングや文字列操作が主であるため、入力テキスト長に比例します(O(N))。extract_key_factsの実装に依存します。
メモリ条件: 入力テキストの長さに応じたメモリを使用します。
誤り分析と抑制手法
RAGシステムにおける主な失敗モードと、それらを抑制するための手法を以下に示します。
1. 幻覚 (Hallucination)
2. 様式崩れ (Format Deviation)
3. 脱線 (Off-topic Generation)
4. 禁止事項 (Prohibited Content)
改良
誤り分析の結果に基づき、以下のサイクルでプロンプトとシステムの改良を行います。
失敗モードの特定: 評価フェーズで検出された主な失敗モード(幻覚、様式崩れなど)を特定します。
原因分析: 各失敗モードの根本原因を分析します。これはプロンプトの指示が不明瞭なのか、コンテキストの質が悪いのか、LLM自体の限界なのかを判断します。
プロンプトの調整: 分析結果に基づき、プロンプトの指示の明確化、制約の強化、少数例の改善、またはCoTステップの見直しを行います。
RAGコンポーネントの調整: 必要に応じて、検索コンポーネントの改善(例: 検索アルゴリズムの調整、チャンキング戦略の変更、Rerankerの導入)も検討します。
テストデータの拡充: 新たに見つかった失敗モードに対応する難例やコーナーケースをテストデータセットに追加します。
再評価
改良が行われた後、更新されたプロンプトとRAGシステムを用いて、既存の評価シナリオと拡充されたテストデータセットに対し、自動評価および手動評価を再実施します。この反復的なプロセスにより、システム全体のパフォーマンスとロバスト性を継続的に向上させます。
まとめ
RAGアーキテクチャは、LLMの能力を外部知識で補強し、信頼性の高い情報提供を可能にする強力なパラダイムです。しかし、その性能を最大限に引き出すためには、明確な入出力契約に基づいたプロンプトの精緻な設計と、多角的な評価、そして継続的な改良が不可欠です。本記事で示したゼロショット、少数例学習、Chain-of-Thought制約型プロンプトの活用、および自動評価と失敗モード分析を通じた改良サイクルは、2024年7月29日 (JST) 時点でのRAGシステムを最適化するための実践的なアプローチを提供します。
graph TD
A["プロンプト設計"] -->|指示とデータ| B("LLMモデル")
B -->|応答| C["応答生成"]
C -->|応答| E["自動評価"]
C -->|応答| F["手動評価/誤り分析"]
D["評価シナリオ作成/拡充"] -->|基準| E
E -->|評価結果| F
F -->|分析結果| G["改良点の特定"]
G -->|改善策| H["プロンプト/RAG修正"]
H -->|新プロンプト/RAG| A
A --|評価ループ開始|--> D
F --|フィードバック|--> D
コメント