論文概要(Abstract)
CoRAG(Chain-of-Retrieval Augmented Generation)は、モデルが反復的・動的に検索クエリを生成しながら情報を取得するフレームワークです(Wang et al., 2025)。最終回答を生成する前に、モデルは過去の検索結果と中間回答に基づいて複数回の検索を行います。Rejection Samplingを用いた学習により、人手アノテーションなしで検索チェーンを自動合成し、推論時にはBeam Searchで最良の検索パスを探索します。PopQAで73.8%(先行SOTA比+13.6%)、2WikiMultihopQAで85.6%を達成しています。
この記事は Zenn記事: LangGraph×Claude Sonnet 4.6で実装する階層的Agentic RAG検索パイプライン の深掘りです。
情報源
- arXiv ID: 2502.11443
- URL: https://arxiv.org/abs/2502.11443
- 著者: Liang Wang, Hao Sun, Shuming Ma, Furu Wei
- 発表年: 2025(NeurIPS 2025採択)
- 分野: cs.CL, cs.AI, cs.IR
背景と動機(Background & Motivation)
従来のRAGシステムは「1回検索→1回生成」の単一パスで動作します。しかし、マルチホップ質問(例:「Transformerを提案した論文の第一著者が所属していた企業の最新のAIサービスは?」)では、複数の情報を段階的に取得する必要があります。
既存の反復RAG手法(IRCoT、Self-RAG、Adaptive-RAG)は、固定的な検索パターンや手動設計の検索戦略に依存しており、多様なタスクへの汎化が困難でした。CoRAGは「いつ・何を検索するか」をモデル自身が学習するアプローチで、この根本的な制約を打破します。
Zenn記事の階層的検索パイプラインがA-RAG論文の3層ツール(keyword / semantic / chunk_read)をエージェントに公開するアプローチであるのに対し、CoRAGは検索クエリ自体の生成を学習するアプローチです。両者は相補的であり、CoRAGの検索チェーン学習をLangGraphの階層的パイプラインに組み込むことで、さらなる精度向上が期待できます。
主要な貢献(Key Contributions)
- 貢献1: Chain-of-Retrieval Training — Rejection Samplingを用いて、正解に到達する検索チェーンのみを学習データとして選別。人手アノテーション不要
- 貢献2: Dynamic Retrieval Synthesis — LLMを使って中間検索クエリを自動合成。手動の検索チェーン設計が不要
- 貢献3: Test-Time Scaling — 推論時のBeam Search幅を増やすほど性能が向上するスケーリング特性を発見
技術的詳細(Technical Details)
問題定式化
質問$q$とドキュメントコーパス$\mathcal{D}$が与えられたとき、CoRAGは以下の検索チェーンを生成します。
\[r_1, e_1, r_2, e_2, \ldots, r_n, e_n, a\]ここで、
- $r_i$: $i$番目の検索クエリ
- $e_i$: $r_i$によって取得されたパッセージ
- $a$: 最終回答
各検索クエリ$r_i$は、質問$q$と過去の検索結果$(e_1, \ldots, e_{i-1})$に条件付けられて生成されます。
\[r_i \sim p_\theta(r | q, e_1, \ldots, e_{i-1})\]Rejection Samplingによる学習
CoRAGの学習は3ステップで行われます。
Step 1: 候補チェーンの合成
LLMを使って、質問$q$に対する候補検索チェーンを複数生成します。
\[\{(r_1^{(j)}, e_1^{(j)}, \ldots, r_n^{(j)}, e_n^{(j)}, a^{(j)})\}_{j=1}^{J}\]ここで$J$は候補数です。
Step 2: Rejection(棄却)
最終回答$a^{(j)}$が正解と一致しないチェーンを棄却します。
\[\text{Accept}(j) = \mathbb{1}[\text{EM}(a^{(j)}, a^*) > 0]\]ここでEMはExact Match、$a^*$は正解です。
Step 3: Fine-tuning
受理されたチェーンでモデルをファインチューニングします。
\[\mathcal{L}(\theta) = -\sum_{j \in \text{Accepted}} \log p_\theta(r_1^{(j)}, e_1^{(j)}, \ldots, a^{(j)} | q)\]この学習方式の重要なポイントは、中間検索クエリの正解ラベルが不要なことです。正解が得られる検索チェーンであれば、どのような中間クエリでも学習データとして使用されます。
推論時のBeam Search
推論時には、Beam Search幅$B$を増やすほど、より良い検索チェーンが見つかり性能が向上します。
各ステップで$B$本のビームを展開し、新たな検索クエリを生成します。各クエリでパッセージを取得し、中間回答の品質でビームをスコアリング・剪定します。
\[\text{Score}(b) = \text{Quality}(a_{\text{intermediate}}^{(b)}, q)\]このTest-Time Scaling特性は、推論時の計算資源を増やすことで性能を向上できることを意味します。これはOpenAIのo1モデルの思考トークンスケーリングと類似した概念です。
アルゴリズム
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from dataclasses import dataclass
@dataclass
class RetrievalChain:
"""検索チェーンの1ステップ"""
query: str
passage: str
score: float
def corag_inference(
question: str,
model,
retriever,
beam_width: int = 4,
max_steps: int = 5,
) -> str:
"""CoRAGの推論アルゴリズム(Beam Search)
Args:
question: 入力質問
model: 検索クエリ・回答生成モデル
retriever: ドキュメント検索器
beam_width: ビーム幅(大きいほど精度向上、コスト増)
max_steps: 最大検索ステップ数
Returns:
最終回答文字列
"""
# 初期ビーム: 質問のみ
beams = [{"context": [], "score": 0.0}]
for step in range(max_steps):
candidates = []
for beam in beams:
# 検索クエリを生成
query = model.generate_query(
question=question,
context=beam["context"],
)
# パッセージを取得
passages = retriever.retrieve(query, top_k=3)
for passage in passages:
new_context = beam["context"] + [
{"query": query, "passage": passage}
]
# 中間回答の品質をスコアリング
intermediate_answer = model.generate_answer(
question=question,
context=new_context,
)
score = model.evaluate_quality(
question, intermediate_answer
)
candidates.append({
"context": new_context,
"score": score,
})
# 上位B本のビームを選択
candidates.sort(key=lambda x: x["score"], reverse=True)
beams = candidates[:beam_width]
# 終了判定: 最良ビームのスコアが閾値以上
if beams[0]["score"] > 0.9:
break
# 最良ビームから最終回答を生成
best_beam = beams[0]
final_answer = model.generate_answer(
question=question,
context=best_beam["context"],
)
return final_answer
実装のポイント(Implementation)
LangGraphとの統合
CoRAGの検索チェーンは、LangGraphのStateGraphで自然に表現できます。Zenn記事のsearch_historyがCoRAGの検索コンテキスト$(e_1, \ldots, e_{i-1})$に対応し、iteration_countがCoRAGの最大ステップ数に対応します。
Beam Searchの実用的な設定
- Beam幅4: PopQAで73.8%(最良)。幅1(Greedy)と比較して約3%向上
- 最大ステップ5: ほとんどのクエリは3-4ステップで回答可能
- 計算コスト: Beam幅$B$に比例してLLM呼び出し回数が増加
ファインチューニングの必要性
CoRAGの最大の制約は、タスク固有のファインチューニングが必要なことです。ゼロショットでは性能が大幅に低下します。Zenn記事のようにClaude Sonnet 4.6をプロンプトベースで使う場合は、CoRAGの検索チェーン戦略をシステムプロンプトに記述し、ファインチューニングなしで近い効果を狙う方法が現実的です。
Production Deployment Guide
AWS実装パターン(コスト最適化重視)
CoRAGの反復検索パイプラインをAWSで本番運用する際の構成を示します。
トラフィック量別の推奨構成:
| 規模 | 月間リクエスト | 推奨構成 | 月額コスト | 主要サービス |
|---|---|---|---|---|
| Small | ~3,000 (100/日) | Serverless | $80-200 | Lambda + Bedrock + DynamoDB |
| Medium | ~30,000 (1,000/日) | Hybrid | $400-1,000 | Lambda + ECS Fargate + ElastiCache |
| Large | 300,000+ (10,000/日) | Container | $3,000-6,000 | EKS + Karpenter + EC2 Spot |
Small構成の詳細 (月額$80-200):
- Lambda: 1GB RAM, 60秒タイムアウト(Beam Search対応) ($30/月)
- Bedrock: Claude 3.5 Haiku, Prompt Caching有効 ($100/月、Beam幅4で4倍のAPI呼び出し)
- DynamoDB: 検索チェーンキャッシュ、On-Demand ($15/月)
- S3: 検索インデックス格納 ($5/月)
コスト削減テクニック:
- Beam幅を2に減らすとコスト半減(精度は約1%低下)
- 検索チェーンキャッシュ(DynamoDB TTL)で同一クエリの再計算を防止
- Prompt Caching有効化でシステムプロンプト部分のコストを30-90%削減
- Bedrock Batch APIで非リアルタイム処理を50%削減
コスト試算の注意事項:
- 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値です
- CoRAGのBeam Search幅に比例してBedrock API呼び出し回数が増加するため、Beam幅の選択がコストに直結します
- 最新料金は AWS料金計算ツール で確認してください
Terraformインフラコード
Small構成 (Serverless): Lambda + Bedrock + DynamoDB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# --- IAMロール(最小権限) ---
resource "aws_iam_role" "corag_lambda" {
name = "corag-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy" "bedrock_invoke" {
role = aws_iam_role.corag_lambda.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"]
Resource = "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-haiku*"
}]
})
}
# --- Lambda関数(Beam Search対応) ---
resource "aws_lambda_function" "corag_handler" {
filename = "corag_lambda.zip"
function_name = "corag-beam-search-handler"
role = aws_iam_role.corag_lambda.arn
handler = "index.handler"
runtime = "python3.12"
timeout = 120 # Beam Search: 最大5ステップ × Beam幅4
memory_size = 1024
environment {
variables = {
BEDROCK_MODEL_ID = "anthropic.claude-3-5-haiku-20241022-v1:0"
DYNAMODB_TABLE = aws_dynamodb_table.chain_cache.name
BEAM_WIDTH = "4"
MAX_RETRIEVAL_STEPS = "5"
}
}
}
# --- DynamoDB(検索チェーンキャッシュ) ---
resource "aws_dynamodb_table" "chain_cache" {
name = "corag-chain-cache"
billing_mode = "PAY_PER_REQUEST"
hash_key = "query_hash"
attribute {
name = "query_hash"
type = "S"
}
ttl {
attribute_name = "expire_at"
enabled = true
}
}
# --- CloudWatchアラーム(コスト監視) ---
resource "aws_cloudwatch_metric_alarm" "lambda_duration" {
alarm_name = "corag-duration-spike"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "Duration"
namespace = "AWS/Lambda"
period = 300
statistic = "Average"
threshold = 60000 # 平均60秒超過でアラート
alarm_description = "CoRAG Beam Search実行時間異常"
dimensions = {
FunctionName = aws_lambda_function.corag_handler.function_name
}
}
運用・監視設定
CloudWatch Logs Insights クエリ:
1
2
3
4
5
6
7
-- Beam Search効率分析: ステップ数とBeam幅の分布
fields @timestamp, beam_width, retrieval_steps, total_tokens
| stats avg(retrieval_steps) as avg_steps,
avg(total_tokens) as avg_tokens,
pct(duration_ms, 95) as p95_latency
by bin(1h)
| filter avg_tokens > 50000 -- トークン使用量異常検知
コスト最適化チェックリスト
- Beam幅の最適化: 精度とコストのトレードオフを検証済み
- 検索チェーンキャッシュ: DynamoDB TTL設定済み
- Prompt Caching: システムプロンプト固定部分のキャッシュ有効化
- Bedrock Batch API: 非リアルタイム処理に50%割引適用
- Lambda メモリ最適化: CloudWatch Insights分析済み
- コスト異常検知: AWS Budgets + CloudWatch アラーム設定済み
実験結果(Results)
| データセット | Standard RAG | Self-RAG | IRCoT | CoRAG | 改善率 |
|---|---|---|---|---|---|
| PopQA | 52.3% | 54.2% | 57.6% | 73.8% | +21.5% |
| 2WikiMultihop | 71.4% | 73.8% | 77.2% | 85.6% | +14.2% |
| MuSiQue | 38.6% | 40.1% | 43.5% | 55.2% | +16.6% |
| HotpotQA | - | - | - | 改善 | - |
| FEVER | - | - | - | 改善 | - |
分析ポイント:
- マルチホップQA(2WikiMultihop、MuSiQue)での改善が特に顕著。これは反復的な検索チェーンがマルチホップ推論に本質的に適していることを示唆
- Rejection Samplingの除去で約8%の性能低下。動的クエリ合成の除去で約5%低下。Beam Searchの除去(Greedy)で約3%低下
- Test-Time Scaling: Beam幅を増やすほど性能が線形に向上
実運用への応用(Practical Applications)
CoRAGの検索チェーン学習は、Zenn記事の階層的パイプラインと組み合わせることで威力を発揮します。具体的には以下の統合が考えられます。
- 検索クエリ生成の改善: Claude Sonnet 4.6のシステムプロンプトに、CoRAGの検索チェーン戦略(過去の検索結果に基づく次のクエリ生成)を記述
- Beam Searchの近似: LangGraphの条件分岐で、複数の検索パスを並列実行し、最良の結果を選択
- 検索チェーンキャッシュ: 同一クエリパターンの検索チェーンをDynamoDBにキャッシュし、2回目以降のレイテンシを削減
関連研究(Related Work)
- IRCoT (Trivedi et al., 2022): 検索とChain-of-Thought推論のインターリービング。CoRAGの直接的な先行研究で、CoRAGはIRCoTの固定パターンを動的チェーンに発展
- Self-RAG (Asai et al., 2023): 検索の必要性を自己判断する手法。CoRAGはSelf-RAGの「いつ検索するか」の判断を検索チェーン全体の最適化に拡張
- Adaptive-RAG (Jeong et al., 2024): クエリ複雑度に応じた検索戦略の切り替え。CoRAGのRejection Samplingは、Adaptive-RAGのルーティングを学習ベースに置き換え
- A-RAG (Du et al., 2026): 3層検索ツールによる階層的検索。CoRAGとA-RAGは相補的で、A-RAGの検索ツール上でCoRAGの検索チェーン学習を適用する拡張が考えられる
まとめと今後の展望
CoRAGは、検索チェーンの自動合成とTest-Time Scalingという2つの革新的アイデアにより、知識集約型QAの精度を大幅に向上させました。主な制約はファインチューニングの必要性ですが、Zenn記事のようにプロンプトベースで検索戦略を指示するアプローチと組み合わせることで、実用的なハイブリッドシステムが構築可能です。
参考文献
- arXiv: https://arxiv.org/abs/2502.11443
- Related Zenn article: https://zenn.dev/0h_n0/articles/a4cd3a7f1cf4ce
- Trivedi et al. (2022): Interleaving Retrieval with Chain-of-Thought Reasoning
- Asai et al. (2023): Self-RAG: Learning to Retrieve, Generate, and Critique
- Jeong et al. (2024): Adaptive-RAG