本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
プロンプトのチェーン化とタスク分解によるLLM性能向上
大規模言語モデル(LLM)は多様なタスクに対応しますが、複雑な問題を一度に処理させると性能が低下したり、誤った出力を生成したりする傾向があります。これを克服するため、「プロンプトのチェーン化」と「タスク分解」が効果的な手法として注目されています。本記事では、これらの技術を用いてLLMの性能を最大化するためのプロンプト設計、評価、そして改良のサイクルについて詳細に解説します。
ユースケース定義
プロンプトのチェーン化とタスク分解は、特に以下のような複雑なLLMタスクに有効です。
多段階の意思決定支援: ユーザーの質問に対し、情報検索、分析、要約、推奨という複数のステップを経て最終回答を導き出す。
複雑なデータ処理と変換: 非構造化データから特定の情報を抽出し、構造化データに変換後、さらに集計や分析を行う。
ソフトウェア設計支援: 高レベルな要件からAPI設計、関数シグネチャの生成、具体的なコードスニペットの作成までを段階的に進める。
長文コンテンツの生成: 記事の構成案作成、各セクションの執筆、全体レビューといったプロセスを経て、一貫性のある高品質なコンテンツを生成する。
これらのユースケースでは、単一のプロンプトで全てを処理するのではなく、中間ステップを設けてLLMに推論を促し、その結果を次のステップに渡すことで、より正確で信頼性の高い出力を期待できます。
制約付き仕様化(入出力契約)
LLMをシステムに組み込む上で、入出力の契約を明確にすることは非常に重要です。
入力契約
形式: JSON形式
必須フィールド:
task_description(string): 実行すべき主要タスクの全体的な説明。context(string, optional): タスク実行に必要な追加情報や背景データ。parameters(object, optional): タスク固有の設定や制約条件(例:output_format,max_steps)。
例:
{ "task_description": "以下の顧客からの問い合わせに対応するための手順を策定し、最終的な回答を作成せよ。", "context": "問い合わせ内容: 「先日購入したスマートウォッチのバッテリー持続時間が異常に短い。交換可能か?」", "parameters": {"output_language": "Japanese", "detail_level": "high"} }
出力契約
形式: JSON形式
必須フィールド:
step_results(array of objects): 各処理ステップの結果のリスト。各オブジェクトは{"step": int, "description": string, "output": string}を含む。final_answer(string): 全てのステップを経て導き出された最終的な回答。status(string): 処理の全体的なステータス(例: “SUCCESS”, “FAILURE”, “PARTIAL_SUCCESS”)。
失敗時の挙動:
処理中にエラーが発生した場合、
statusを”FAILURE”とし、error_message(string) フィールドに失敗理由と発生ステップの詳細を追記する。step_resultsには、失敗したステップまでの結果を含める。
禁止事項:
個人情報(氏名、住所、電話番号、メールアドレスなど)の具体的な出力は厳禁。
機密情報や企業の専有情報の出力は厳禁。
差別的、暴力的、または不適切なコンテンツの生成は禁止。検出された場合は、
statusを”FAILURE”とし、error_messageに違反内容を明記する。
プロンプト設計
ここでは、特定のタスクを例に、異なるプロンプト設計アプローチを示します。 タスク: 「特定の企業に関するニュース記事を3つ探し、その内容を要約し、ポジティブ・ネガティブ・中立の感情を判断せよ。」
1. ゼロショットプロンプト
モデルに何の例も与えず、一度に全てのタスクを指示する最もシンプルな方法。 中間思考プロセスを明示的に指示しないため、複雑なタスクでは精度が低くなりがちです。
あなたはニュース分析アシスタントです。
以下の指示に従い、指定された企業の最新ニュースを分析してください。
# 企業名
{{企業名}}
# 指示
1. {{企業名}}に関する最新のニュース記事を3つ検索し、各記事のURLとタイトルをリストアップしてください。
2. 各記事の内容をそれぞれ100字以内で要約してください。
3. 各記事の感情(ポジティブ、ネガティブ、中立)を判断してください。
4. 全ての情報をJSON形式で出力してください。
# 出力形式
```json
{
"company_name": "{{企業名}}",
"news_analysis": [
{
"title": "記事タイトル1",
"url": "記事URL1",
"summary": "記事要約1",
"sentiment": "ポジティブ/ネガティブ/中立"
},
{
"title": "記事タイトル2",
"url": "記事URL2",
"summary": "記事要約2",
"sentiment": "ポジティブ/ネガティブ/中立"
},
{
"title": "記事タイトル3",
"url": "記事URL3",
"summary": "記事要約3",
"sentiment": "ポジティブ/ネガティブ/中立"
}
],
"overall_sentiment": "全体的な感情"
}
### 2. 少数例(Few-shot)プロンプト
いくつかの具体的な入出力例を示すことで、モデルにタスクのパターンを学習させる方法です。
特に期待する出力形式や微妙なニュアンスを伝えるのに有効です。
```text
あなたはニュース分析アシスタントです。
以下の入出力例を参考に、指定された企業の最新ニュースを分析してください。
### 例1
# 入力
```json
{
"company_name": "株式会社ABC",
"task": "ニュース分析"
}
出力
{
"company_name": "株式会社ABC",
"news_analysis": [
{
"title": "ABC社、新製品「XYZ」を発表",
"url": "https://example.com/news/abc-xyz",
"summary": "ABC社は革新的な新製品XYZを発表し、市場からの高い評価が期待されている。",
"sentiment": "ポジティブ"
},
{
"title": "ABC社の四半期決算、予想を上回る",
"url": "https://example.com/finance/abc-q1",
"summary": "ABC社の最新四半期決算は、アナリスト予想を上回る好調な結果となり、株価も上昇した。",
"sentiment": "ポジティブ"
},
{
"title": "ABC社のデータ漏洩問題、詳細を公表",
"url": "https://example.com/security/abc-leak",
"summary": "ABC社は顧客データ漏洩の詳細を発表し、対応策を講じることを表明した。",
"sentiment": "ネガティブ"
}
],
"overall_sentiment": "一部ネガティブな要素はあるが、新製品と好決算により全体的にはポジティブな見方が強い。"
}
例2
入力
{
"company_name": "{{企業名}}",
"task": "ニュース分析"
}
指示(上記例と同様に、具体的な検索、要約、感情分析を行い、最終結果をJSON形式で出力してください。)
3. Chain-of-Thought(CoT)制約型プロンプト
モデルに思考プロセスを段階的に出力させることで、複雑な推論を可能にする方法です。 特にタスク分解と相性が良く、中間ステップで制約やフォーマットを課すことで、出力の制御と品質向上を図ります。
あなたは高度なニュース分析エンジンです。
以下の手順に厳密に従い、指定された企業のニュースを分析し、最終結果をJSON形式で出力してください。
# 企業名
{{企業名}}
# 分析手順 (ステップバイステップで思考と結果を出力すること)
## ステップ1: ニュース検索とURL抽出
- {{企業名}}に関する最新のニュース記事をウェブから3つ検索してください。
- 各記事について、以下の情報を特定してください: URL, タイトル。
- 思考プロセス: どのような検索クエリを使用し、どの情報源を優先したかを簡潔に述べよ。
- 結果のJSON形式: [{"step": 1, "description": "ニュース検索", "output": [{"title": "...", "url": "..."}, ...]}]
## ステップ2: 各記事の要約
- ステップ1で取得した各記事のコンテンツを読み込み、それぞれ100字以内で主要な内容を要約してください。
- 思考プロセス: 各記事のどの部分が重要と判断されたか、要約のポイントを説明せよ。
- 結果のJSON形式: [{"step": 2, "description": "記事要約", "output": [{"title": "...", "summary": "..."}, ...]}]
## ステップ3: 感情分析
- ステップ2で要約された各記事について、その内容がポジティブ、ネガティブ、中立のいずれであるかを判断してください。
- 思考プロセス: 各感情判断の根拠となったキーワードや文脈を具体的に示せ。
- 結果のJSON形式: [{"step": 3, "description": "感情分析", "output": [{"title": "...", "sentiment": "ポジティブ/ネガティブ/中立"}, ...]}]
## ステップ4: 最終結果の統合
- これまでのステップの結果を統合し、指定された最終出力形式に沿ってJSONを生成してください。
- 全体的な感情も判断してください。
# 最終出力形式
```json
{
"step_results": [
{
"step": 1,
"description": "ニュース検索",
"output": [{"title": "記事タイトル1", "url": "記事URL1"}, ...]
},
{
"step": 2,
"description": "記事要約",
"output": [{"title": "記事タイトル1", "summary": "記事要約1"}, ...]
},
{
"step": 3,
"description": "感情分析",
"output": [{"title": "記事タイトル1", "sentiment": "ポジティブ/ネガティブ/中立"}, ...]
}
],
"final_answer": {
"company_name": "{{企業名}}",
"news_analysis": [
{
"title": "記事タイトル1",
"url": "記事URL1",
"summary": "記事要約1",
"sentiment": "ポジティブ/ネガティブ/中立"
},
...
],
"overall_sentiment": "全体的な感情"
},
"status": "SUCCESS"
}
## 評価
LLMの出力を評価するプロセスは、モデルの性能を客観的に把握し、プロンプト改良の方向性を見定める上で不可欠です。
### 評価シナリオ
複数の種類の入力データを用いて、LLMの挙動を検証します。
* **正例(Happy Path)**: 明確な指示と十分な情報が与えられた、期待通りの処理が可能なケース。
* 例: 広く知られた企業の最新かつ分かりやすいポジティブニュース。
* **難例(Edge Cases/Complexities)**: 曖昧な指示、複数の条件、矛盾する情報、専門用語が含まれるケース。
* 例: 感情が混在する(一部ポジティブ、一部ネガティブ)ニュース、情報源が少ない新興企業のニュース。
* 例: 長文の記事から特定の情報を抽出し、かつその論調を判断する必要がある場合。
* **コーナーケース(Failure Modes)**: 情報不足、特殊なフォーマット、意図的な不正入力、または禁止事項に触れる可能性のあるケース。
* 例: 存在しない企業名、記事検索で結果が0件の場合。
* 例: 出力で個人情報を意図的に要求するような入力。
### 自動評価の擬似コード
自動評価は、LLMの出力をプログラム的に解析し、採点ルーブリックに基づいてスコアとフィードバックを生成します。
```python
import json
import re
def evaluate_llm_output(expected_data: dict, actual_json_str: str) -> (int, list):
"""
LLMの出力を自動評価する擬似関数。
Args:
expected_data (dict): 期待される出力の構造と内容(キーワードなど)を含む辞書。
例: {"expected_status": "SUCCESS", "expected_final_keywords": ["バッテリー", "交換"],
"expected_step_1_keywords": ["URL", "タイトル"]}
actual_json_str (str): LLMから得られた生のJSON文字列出力。
Returns:
tuple[int, list]: 評価スコア(0-100点)とフィードバックメッセージのリスト。
前提条件:
- actual_json_strは有効なJSON形式であること。
- expected_dataには評価に必要なキーワードや期待値が含まれること。
計算量:
- JSONパース: O(L) (LはJSON文字列長)
- キーワードマッチ: O(N*M) (Nはキーワード数、Mは評価対象文字列長)
- 全体として、LLM出力のサイズと評価項目数に比例。
メモリ条件:
- LLM出力と期待値データをメモリにロードできること。
"""
score = 0
feedback = []
max_score = 100 # 総配点
# 1. JSON形式の検証 (配点: 10点)
try:
actual_data = json.loads(actual_json_str)
if not isinstance(actual_data, dict):
raise ValueError("Output is not a dictionary.")
score += 10
feedback.append("JSON形式: OK")
except (json.JSONDecodeError, ValueError) as e:
feedback.append(f"JSON形式エラー: {e}")
return 0, feedback # 形式不正は致命的、以降の評価は不可能
# 2. 必須キーの検証 (配点: 10点)
required_output_keys = ["step_results", "final_answer", "status"]
if all(key in actual_data for key in required_output_keys):
score += 10
feedback.append("必須キー: OK")
else:
missing_keys = [k for k in required_output_keys if k not in actual_data]
feedback.append(f"必須キー不足: {', '.join(missing_keys)}")
# この後の評価のために、不足キーを空のデフォルト値で補完する
for k in missing_keys:
actual_data[k] = [] if k == "step_results" else ""
# 3. 最終回答の正確性 (キーワードマッチング、配点: 40点)
# expected_final_keywordsは、最終回答に含まれるべきキーワードのリスト
expected_final_keywords = expected_data.get("expected_final_keywords", [])
actual_final_answer = actual_data.get("final_answer", "")
if isinstance(actual_final_answer, dict): # final_answerがネストしている可能性を考慮
actual_final_answer_str = json.dumps(actual_final_answer, ensure_ascii=False)
else:
actual_final_answer_str = str(actual_final_answer)
if expected_final_keywords:
match_count = sum(1 for kw in expected_final_keywords if kw.lower() in actual_final_answer_str.lower())
accuracy_score = (match_count / len(expected_final_keywords)) * 40
score += int(accuracy_score)
if match_count == len(expected_final_keywords):
feedback.append("最終回答キーワード: 全て一致")
else:
feedback.append(f"最終回答キーワード: {len(expected_final_keywords) - match_count}個不一致。期待値: {expected_final_keywords}")
else:
# キーワードが指定されていない場合も部分点を付与
score += 20
feedback.append("最終回答キーワード: 未指定のため部分点")
# 4. ステップ結果の正確性 (キーワードマッチング、配点: 30点)
# expected_step_keywordsは、各ステップのoutputに含まれるべきキーワードのリスト
# 例: [{"step": 1, "keywords": ["URL", "タイトル"]}, {"step": 2, "keywords": ["要約"]}]
expected_step_configs = expected_data.get("expected_step_configs", [])
actual_step_results = actual_data.get("step_results", [])
step_match_score_per_item = 0
if expected_step_configs:
step_match_score_per_item = 30 / len(expected_step_configs)
for i, expected_step_config in enumerate(expected_step_configs):
step_num = expected_step_config.get("step")
expected_kws = expected_step_config.get("keywords", [])
found_step_output = None
for actual_step_item in actual_step_results:
if actual_step_item.get("step") == step_num:
found_step_output = actual_step_item.get("output", "")
break
if found_step_output:
if isinstance(found_step_output, (dict, list)):
found_step_output_str = json.dumps(found_step_output, ensure_ascii=False)
else:
found_step_output_str = str(found_step_output)
step_kw_match_count = sum(1 for kw in expected_kws if kw.lower() in found_step_output_str.lower())
if step_kw_match_count == len(expected_kws):
score += int(step_match_score_per_item)
feedback.append(f"ステップ {step_num} キーワード: 全て一致")
else:
feedback.append(f"ステップ {step_num} キーワード: {len(expected_kws) - step_kw_match_count}個不一致。期待値: {expected_kws}")
else:
feedback.append(f"ステップ {step_num}: 出力が見つからない。")
# 5. 禁止事項違反チェック (正規表現、配点: -50点 (減点))
forbidden_patterns = [r"個人情報", r"機密情報", r"不適切", r"差別的", r"暴力的"]
actual_output_lower = actual_json_str.lower() # 全体をチェック
for pattern in forbidden_patterns:
if re.search(pattern, actual_output_lower, re.IGNORECASE):
score -= 50 # 致命的な減点
feedback.append(f"!!! 禁止事項違反 !!! パターン '{pattern}' が検出されました。")
break # 複数の違反があっても最大減点は一度
# 6. ステータスフィールドの評価 (配点: 10点)
expected_status = expected_data.get("expected_status", "SUCCESS")
actual_status = actual_data.get("status", "")
if actual_status == expected_status:
score += 10
feedback.append(f"ステータス: {actual_status} (期待通り)")
else:
feedback.append(f"ステータス不一致: 期待 '{expected_status}', 実際 '{actual_status}'")
# 最終スコアを0-100の範囲に調整
final_score = max(0, min(max_score, int(score)))
return final_score, feedback
# 使用例:
# expected_input_for_eval = {
# "expected_status": "SUCCESS",
# "expected_final_keywords": ["ABC社", "新製品", "高評価", "好調", "データ漏洩", "対応策"],
# "expected_step_configs": [
# {"step": 1, "keywords": ["URL", "タイトル"]},
# {"step": 2, "keywords": ["要約"]},
# {"step": 3, "keywords": ["ポジティブ", "ネガティブ", "中立"]}
# ]
# }
# dummy_llm_output = """
# {
# "step_results": [
# {
# "step": 1,
# "description": "ニュース検索",
# "output": [{"title": "ABC社、新製品XYZ発表", "url": "https://example.com/xyz"}]
# },
# {
# "step": 2,
# "description": "記事要約",
# "output": [{"title": "ABC社、新製品XYZ発表", "summary": "ABC社は新製品XYZを発表し、市場から高い評価を得ている。"}]
# },
# {
# "step": 3,
# "description": "感情分析",
# "output": [{"title": "ABC社、新製品XYZ発表", "sentiment": "ポジティブ"}]
# }
# ],
# "final_answer": {
# "company_name": "株式会社ABC",
# "news_analysis": [
# {
# "title": "ABC社、新製品XYZ発表",
# "url": "https://example.com/xyz",
# "summary": "ABC社は新製品XYZを発表し、市場から高い評価を得ている。",
# "sentiment": "ポジティブ"
# }
# ],
# "overall_sentiment": "ポジティブ"
# },
# "status": "SUCCESS"
# }
# """
# score, feedback = evaluate_llm_output(expected_input_for_eval, dummy_llm_output)
# print(f"Score: {score}")
# print(f"Feedback: {feedback}")
誤り分析と抑制手法
LLMの出力には様々な失敗モードが存在し、それぞれ適切な抑制手法を講じる必要があります。
失敗モード1: 幻覚(Hallucination)
説明: 事実に基づかない情報や、プロンプトにない情報を生成すること。
抑制手法:
検証ステップ: モデルの出力に対し、別のプロンプトや外部ツール(検索エンジンなど)で事実確認を行うステップを導入します。
System指示: プロンプトに「不確かな情報は生成しないこと」「提供された情報源のみに基づいて回答すること」といった指示を含めます [6]。
検索拡張生成 (RAG): 外部の信頼できる知識ベースから情報を取得し、それを基に生成させることで幻覚を抑制します。
失敗モード2: 様式崩れ(Format Mismatch)
説明: 期待するJSON、Markdownなどの出力形式に従わない、または無効な形式で出力すること。
抑制手法:
JSONスキーマ検証: 出力されたJSONが事前に定義したスキーマに準拠しているかを自動で検証します。
リトライ戦略: 無効な形式で出力された場合、特定の回数まで「JSON形式で再出力せよ」と指示してモデルに再試行させます。
明確なSystem指示: プロンプトの先頭で「出力は厳密に以下のJSON形式に従うこと」と明示します。
失敗モード3: 脱線(Off-topic / Task Deviation)
説明: 指示されたタスクから逸脱し、無関係な情報を生成したり、別のタスクを実行したりすること。
抑制手法:
System指示: 「指示されたタスク以外は一切行わないこと」「出力は〇〇についてのみに限定すること」と範囲を厳密に指定します [6]。
タスク分解の厳密化: 各ステップのプロンプトで、そのステップで達成すべき目標を非常に具体的に記述し、逸脱の余地を減らします。
失敗モード4: 禁止事項違反
説明: 個人情報、機密情報、不適切な内容(差別、暴力など)を生成すること。
抑制手法:
入力フィルタリング: ユーザー入力に含まれる禁止事項を事前に検出し、LLMへの入力をブロックまたはサニタイズします。
出力フィルタリング: LLMの出力がユーザーに到達する前に、正規表現や別のセーフティモデルを用いて禁止事項をチェックし、検出された場合は出力を修正または拒否します [6]。
System指示: 「個人情報や機密情報は絶対に生成しないこと」「倫理的に問題のある内容を避けること」といったガードレールを設けます。
改良
評価で得られたフィードバックに基づき、プロンプトを反復的に改良します。
フィードバックの分析:
どのプロンプト案(ゼロショット、少数例、CoT)がどのシナリオで良好な結果を出したか。
どの失敗モードが頻繁に発生したか、その根本原因は何か。
特にCoTプロンプトの場合、どの思考ステップでモデルが誤った推論をしたかを特定します。
プロンプトの調整:
具体性の向上: 曖昧な指示をより具体的かつ明確にします。
Few-shot例の追加/改善: 評価シナリオの難例やコーナーケースに対応できるような質の高い例を追加します。失敗したケースに対する「良い例」を含めることも有効です。
CoT指示の強化: 推論ステップをさらに細分化したり、「なぜそのように判断したのか理由を述べよ」といった制約を追加して、推論の透明性と正確性を高めます。
System指示の調整: モデルの振る舞いや制約に関するSystemプロンプトをより厳密に調整します。
外部ツールの統合: 必要に応じて、検索ツール、計算ツール、コード実行環境などをLLMに提供するReAct (Reasoning and Acting) のようなフレームワークの導入を検討します [1]。
再評価
改良されたプロンプトに対して、再度評価シナリオを実行し、その性能変化を測定します。 このサイクルを繰り返すことで、プロンプトの品質は着実に向上し、LLMのタスク遂行能力を最適化できます。
まとめ
プロンプトのチェーン化とタスク分解は、LLMが複雑な課題を段階的に解決し、より高品質で信頼性の高い出力を生成するための強力な手法です [2] [4]。ゼロショット、少数例、Chain-of-Thoughtといったプロンプト設計アプローチを適切に選択し、定義された入出力契約に基づいてモデルを運用することが重要です。
また、自動評価システムを構築し、正例、難例、コーナーケースを含む多様な評価シナリオを用いることで、モデルの性能を客観的に把握し、幻覚や様式崩れ、脱線といった一般的な失敗モードを特定できます [5]。これらの失敗に対して、検証ステップ、System指示、リトライ戦略、フィルタリングなどの抑制手法を適用し、継続的な改良サイクルを回すことで、LLMアプリケーションの堅牢性と信頼性を向上させることができます [3] [6]。この反復的なプロセスが、実用的なLLMシステムの開発において不可欠です。
flowchart TD
A["プロンプト生成/選択"] --> |入力として| B["LLM実行"];
B --> |出力| C["出力評価"];
C --> |評価結果/フィードバック| D["プロンプト改良"];
D --> |新しいプロンプト/再試行| A;
参考文献:
[1] Wei, Jason, et al. “Chain-of-thought prompting elicits reasoning in large language models.” arXiv preprint arXiv:2201.11903 (2022). (公開日: 2022年1月27日, 著者: Google Brain)
[2] Microsoft Azure. “Prompt Engineering Techniques – Decompose complex tasks.” (更新日: 2024年3月15日, 組織: Microsoft Azure) https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/concepts/prompt-engineering#decompose-complex-tasks
[3] Microsoft Azure OpenAI. “Advanced prompt engineering techniques.” (更新日: 2024年5月22日, 組織: Microsoft Azure OpenAI) https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/concepts/advanced-prompt-engineering?pivots=programming-language-python#best-practices
[4] Hugging Face. “Evaluate: A library for evaluation in NLP and ML.” (更新日: 2023年11月10日, 組織: Hugging Face) https://huggingface.co/docs/evaluate/index
[5] Google Cloud. “Generative AI on Vertex AI: Best practices for safety prompts.” (更新日: 2024年6月20日, 組織: Google Cloud Vertex AI) https://cloud.google.com/vertex-ai/docs/generative/best-practices/safety-prompts?hl=ja
[6] Anthropic. “Constitutional AI: Harmlessness from AI Feedback.” (公開日: 2022年12月8日, 組織: Anthropic) https://www.anthropic.com/research/constitutional-ai

コメント