ankuro.dev
← ブログ一覧に戻る
【CCA Foundations対策 / Claude API編 #14】コンテキスト最適化とprovenance管理——マルチエージェントの信頼性設計
2026-04-03#Claude#API#Python#生成AI#エージェント#Claude Certified Architect

【CCA Foundations対策 / Claude API編 #14】コンテキスト最適化とprovenance管理——マルチエージェントの信頼性設計

CCA Foundations試験ガイドおよびAnthropicの公式ドキュメントをもとに解説しています。

マルチエージェントシステムで「重要な情報が無視された」「ソースが不明な主張が含まれた」「部分的な失敗で全体が止まった」という問題が起きる。今回はこれらを設計レベルで防ぐ2つのパターン——コンテキスト最適化とprovenance管理——を整理する。

この記事でわかること:

  • lost-in-the-middle現象とprimacy effectを使った対策
  • 上流エージェントのverbose outputを削減する設計
  • 矛盾する情報源をconflict annotationで扱う方法
  • カバレッジ注釈で部分的な成功を透明に伝える
  • エラーを最低レイヤーで解決して構造化して伝播させる

lost-in-the-middle問題

大量のテキストを入力すると、Claudeは先頭と末尾の情報は確実に参照するが、中間の情報を見落とす傾向がある。これをlost-in-the-middle現象と呼ぶ。

マルチエージェントシステムでは、複数のサブエージェントの出力を集約して合成エージェントに渡す場面でこの問題が起きやすい。

Web検索エージェント: 15,000トークンの結果 ← 先頭なので参照される
文書分析エージェント: 50,000トークンの結果 ← 中間なので見落とされる
レポート生成エージェント: 10,000トークンの結果 ← 末尾なので参照される

対策①:重要情報を先頭に置く(primacy effect)

合計75,000トークンの集約入力を渡す前に、重要な発見のサマリーを冒頭に追加する

def build_synthesis_input(search_results, doc_results, report_results):
    # 重要な発見を先頭に要約(primacy effectを活用)
    key_findings_summary = extract_key_findings([
        search_results, doc_results, report_results
    ])

    synthesis_input = f"""## 主要な発見(サマリー)
{key_findings_summary}

---

## Web検索エージェントの詳細結果
{search_results}

## 文書分析エージェントの詳細結果
{doc_results}

## レポート生成エージェントの詳細結果
{report_results}
"""
    return synthesis_input

先頭にサマリーを置くことで、合成エージェントが全体像を把握した上で詳細を読める。セクション見出し(##)を使うことで、中間のコンテンツもナビゲートしやすくなる。

対策②:上流エージェントのverbose outputを削減する

85,000トークンのWeb検索結果・70,000トークンの文書分析結果を丸ごと渡すのが問題の根本。上流エージェントが返すデータ量をソースで減らすのが最も効果的な解決策。

# NG:ページの全コンテンツをそのまま返す
def search_agent_output(results):
    return {
        "raw_pages": [page.full_content for page in results],  # 85,000トークン
    }

# OK:主要な事実・引用・関連スコアを構造化して返す
def search_agent_output(results):
    return {
        "findings": [
            {
                "claim": finding.key_claim,      # 主張
                "evidence": finding.excerpt,      # 証拠(抜粋)
                "source": finding.url,            # ソース
                "relevance_score": finding.score, # 関連スコア
                "date": finding.published_date,   # 公開日
            }
            for finding in results.top_findings
        ]
    }  # → 数千トークンに削減

要約エージェントを中間に追加するのではなく、サブエージェント自身が構造化されたデータを返す設計にする。要約エージェントを追加すると、そのエージェントも全コンテンツを処理する必要があり、問題を別の場所に移すだけになる。


provenance管理:情報の出所を追跡する

合成エージェントが複数ソースの情報を組み合わせるとき、「この主張はどこから来たのか」を追跡できる設計が信頼性の要になる。

構造化された出力形式

各発見に出所情報を付ける。

finding_schema = {
    "claim": str,           # 主張の内容
    "evidence": str,        # 根拠となる引用
    "source": str,          # 出典(URL・文書名)
    "published_date": str,  # 情報の鮮度
    "confidence": float,    # 信頼スコア(0.0〜1.0)
}

合成エージェントはこの構造化データを受け取り、最終レポートでも出典を保持したまま回答を生成する。


conflict annotation:矛盾を解決せずに伝える

信頼できる2つのソースが矛盾する数値を示している場合——文書分析エージェントはどうすべきか。

やってはいけないこと

  • どちらかを選んで処理を続ける(ヒューリスティックで選択)
  • 矛盾に気づかずそのまま渡す

正しい設計:両方の値を出典付きでフラグを立て、調整の判断をコーディネーターに委ねる。

def handle_conflicting_sources(source_a, source_b, metric):
    return {
        "type": "conflict",
        "metric": metric,
        "values": [
            {
                "value": source_a.value,
                "source": source_a.name,
                "date": source_a.date,
            },
            {
                "value": source_b.value,
                "source": source_b.name,
                "date": source_b.date,
            },
        ],
        "conflict_note": (
            f"{source_a.name}は{source_a.value}、"
            f"{source_b.name}は{source_b.value}と相反するデータを示している。"
            "どちらの値を採用するかはコーディネーターが判断する。"
        ),
    }

文書分析エージェントは分析タスクを完了させつつ、矛盾点を構造化して上位に伝える。コーディネーターはより広いコンテキスト(調査全体の目的・依頼者の優先事項)をもとに判断できる。


coverage annotation:部分的な成功を透明に伝える

サブエージェントがタイムアウト・接続エラーで一部のデータを取得できなかった場合、合成エージェントはどう対応すべきか。

やってはいけないこと

  • 取得できたデータだけで何も言わず合成を完了する
  • 部分的な成功を「失敗」として全体を止める

正しい設計:取得できた部分で処理を続け、不足している部分を注釈で明示する。

def synthesize_with_coverage(available_data, missing_sources):
    synthesis = generate_synthesis(available_data)

    coverage_annotations = {
        "well_supported_topics": [
            topic for topic in synthesis.topics
            if topic.source_count >= 2
        ],
        "coverage_gaps": [
            {
                "topic": source.expected_topic,
                "missing_source": source.name,
                "reason": source.failure_reason,  # "timeout" / "no_results" / "access_denied"
            }
            for source in missing_sources
        ],
    }

    return {
        "synthesis": synthesis.content,
        "coverage": coverage_annotations,
    }

この設計により、レポートの利用者は「どの発見が十分に裏付けられているか」「どの領域の情報が不足しているか」を把握できる。不完全なデータでも価値ある情報を届けつつ、その限界を透明に伝える。

📋 試験ガイドより
公式試験ガイドのIn-Scope Topicsに「Information provenance: Claim-source mappings, temporal data handling, conflict annotation, coverage gap reporting」および「Context window optimization: Trimming verbose tool outputs, structured fact extraction, position-aware input ordering」が明記されている。provenance管理は出典の追跡だけでなく、矛盾・ギャップ・不確実性を下流に正確に伝える設計全体を指す。


エラー伝播:最低レイヤーで解決して構造化して伝える

マルチエージェントシステムのエラー処理には一つの原則がある。

解決できる最も低いレイヤーで処理し、解決できないものだけを上位に伝える。

サブエージェントはエラーを3種類に分類して対応する。

エラー種別 対応
一時的な障害(タイムアウト・ネットワーク) サブエージェントが内部でリトライ
構造的な失敗(破損ファイル・アクセス拒否) 構造化エラーをコーディネーターに返す
有効な空の結果(「0件」) 成功として返す(タイムアウトと区別する)

タイムアウトと空の結果は別物:「0件の結果」はクエリが成功して情報がないことを示す。「タイムアウト」はアクセス自体が失敗したことを示す。コーディネーターは前者を受け入れ、後者だけリトライを判断できる。

def handle_search_failure(error, query, partial_results):
    return {
        "is_error": True,
        "error_category": classify_error(error),  # "transient" / "structural" / "access"
        "attempted_query": query,
        "partial_results": partial_results,        # 取得できた分
        "alternative_approaches": suggest_alternatives(query, error),
        "is_retryable": error.category == "transient",
    }

構造化エラーにはコーディネーターが意思決定に必要な情報をすべて含める:エラーの種類・試みたクエリ・部分的な結果・代替アプローチ。


よくある誤解まとめ

誤解 実際
コンテキストウィンドウが大きければlost-in-the-middleは起きない コンテキストサイズの問題ではなく注意の質の問題。サマリーを先頭に置く設計が必要
中間要約エージェントを追加すれば解決する 要約エージェント自体が全コンテンツを処理する必要がある。上流でverbose outputを削減するほうが根本解決
矛盾するデータはどちらか選んで処理を続けるべき 両方を出典付きでフラグを立てコーディネーターに判断を委ねる
部分的な失敗は全体の失敗として扱う 完了した部分でcoverage注釈付きで合成する。利用者に透明に伝えつつ価値を届ける
タイムアウトと0件の結果は同じエラーとして扱う 前者はアクセス失敗(リトライ対象)、後者は有効な結果(リトライ不要)。区別して返す

設計の判断基準

場面 やりがちな選択 正しい選択 判断の根拠
複数サブエージェントの出力を合成エージェントに渡す 全出力を結合してそのまま渡す 主要な発見のサマリーを先頭に追加し、セクション見出しで整理してから渡す primacy effectを活用し、lost-in-the-middleを防ぐ
サブエージェントの出力が大きすぎる 中間要約エージェントを追加する サブエージェント自身が構造化データ(主張・証拠・ソース・スコア)を返す設計にする 要約エージェントも全コンテンツを処理する必要があり、問題を移動させるだけ
信頼できるソースが矛盾する数値を示している ヒューリスティックで一方を選んで処理を続ける 両方を出典付きでconflict annotationとして返し、コーディネーターに判断を委ねる 文書分析エージェントは分析が役割。判断はコンテキストを持つコーディネーターが行う
サブエージェントが一部のデータを取得できなかった エラーとして全体を止める 取得できた部分でcoverage注釈付きで合成を完了する 完了した作業の価値を捨てずに、不足を透明に伝えられる

まとめ

  • lost-in-the-middleはコンテキスト先頭に重要情報のサマリーを置き、セクション見出しで中間をナビゲートすることで対策する
  • verbose outputの根本解決は中間要約エージェントではなく、上流エージェントが構造化データを返す設計にすること
  • 矛盾するソースはどちらかを選ばずにconflict annotationとして上位に委ねる
  • 部分的な失敗はcoverage注釈付きで合成を完了し、不足を透明に伝える
  • エラーは最低レイヤーで解決し、解決できないものだけを構造化して上位に伝播させる

#13:エスカレーションと反復改善#15:セッション管理とconfidence scoring