Claude API プロンプトキャッシュ完全ガイド 2026 — APIコスト90%削減の実践テクニック

Claude API プロンプトキャッシュ完全ガイド 2026 — APIコスト90%削減の実践テクニック


PR: 本記事にはアフィリエイトリンク(プロモーション)が含まれます。掲載するサービスは編集部が記事内容との関連性で選定しており、報酬の有無で評価を変えていません。

この記事でわかること

  • 対象読者: Claude API を使ってアプリ開発中のエンジニア、APIコストの最適化を検討中の方
  • 読了時間: 本文 約12分 + コードサンプル確認 約5分
  • 得られるもの: ① プロンプトキャッシュの仕組みと料金体系の完全理解 ② Python/TypeScript実装サンプルのコピペ可能コード ③ 実際のコスト試算と削減効果の定量化 ④ ベストプラクティスと陥りやすい落とし穴

プロンプトキャッシュとは何か

Claude APIでAIアプリを構築していると、必ずぶつかる問題があります。**「同じコンテキストを毎回送り続けるコスト」**です。

例えば、こんなシナリオを考えてみましょう:

  • 社内規程(5万トークン相当のPDF)をもとに質問に答えるチャットボット
  • 大量のコードファイルを読み込んで分析するコードレビューエージェント
  • 複数回のやり取りが続くカスタマーサポートbot

これらのケースでは、会話のたびに同じシステムプロンプトや大量のドキュメントを毎回送信することになります。100回の問い合わせがあれば、5万トークンのコンテキストを100回分 = 500万トークン分の課金が発生します。

プロンプトキャッシュはこの問題を解決します。

一度処理したコンテキストをAnthropicのサーバー側でキャッシュし、次のリクエストでは大幅に安いレートで再利用できる機能です。上記の例では、キャッシュヒット時のコストが通常の10分の1以下になります。


プロンプトキャッシュの仕組み

cache_control ブレークポイント

プロンプトキャッシュは、メッセージの特定の位置にcache_controlパラメータを追加することで有効化します。

# キャッシュ対象のコンテンツにマークを付ける
{
    "type": "text",
    "text": "長いシステムプロンプトや大量ドキュメント...",
    "cache_control": {"type": "ephemeral"}
}

このブレークポイントを境に、それ以前のコンテンツすべてがキャッシュ対象になります。1回のリクエストに最大4つのブレークポイントを設定できます。

キャッシュの有効期間(TTL)

  • デフォルトTTL: 5分間
  • 5分以内に同じキャッシュが参照されれば TTL がリセットされます
  • アクティブに使われているシステムは実質的にキャッシュが維持されます

最小キャッシュトークン数

キャッシュが有効になるには、一定量以上のトークンが必要です:

モデル最小キャッシュトークン
claude-sonnet-4-6, claude-sonnet-4-51,024トークン
claude-opus-4-71,024トークン
claude-haiku-4-52,048トークン

小さすぎるコンテキストはキャッシュされず、通常課金になります。


料金体系と削減効果

課金の3段階

プロンプトキャッシュでは、トークンが3種類の異なるレートで課金されます:

トークン種別相対コスト用途
通常入力トークン×1.0 (基準)キャッシュなしの通常リクエスト
キャッシュ書き込みトークン×1.25 (+25%)初回キャッシュ生成時
キャッシュ読み込みトークン×0.10 (-90%)キャッシュヒット時

実際のコスト試算(claude-sonnet-4-6の場合)

シナリオ: 社内FAQbot(システムプロンプト = 10,000トークン)に1日100回の問い合わせ

キャッシュなしの場合:

1日のコスト = 10,000 tokens × 100回 = 1,000,000 input tokens

キャッシュありの場合(キャッシュヒット率 = 95%と仮定):

キャッシュ書き込み(100件中5%=5回のキャッシュ再生成): 5回 × 10,000 tokens = 50,000 tokens × 1.25
キャッシュ読み込み(ヒット分): 10,000 tokens × 95件 = 950,000 tokens × 0.10

コスト比較(概算):

  • キャッシュなし: 1,000,000 tokens分の入力課金
  • キャッシュあり: 62,500 + 95,000 = 157,500 tokens相当の課金
  • 削減率: 約84%のコスト削減

「最大90%削減」と「84%削減」の違いについて: 「最大90%」はキャッシュヒット時の単体トークンコスト削減率(通常の1/10)を指します。「84%」はヒット率95%の実用シナリオでのトータル削減率です。ヒット率が高いほど実際の削減率は90%に近づきます。

実際の数字はAnthropicの公式Pricingページで確認してください。モデルや利用量によって基準単価が異なります。


Python実装 — 基本編

セットアップ

pip install anthropic>=0.40.0

システムプロンプトのキャッシュ

最も基本的なユースケースは、長いシステムプロンプトのキャッシュです。

import anthropic

client = anthropic.Anthropic()

# 長いシステムプロンプト(実際は数千〜数万トークンになることも)
LARGE_SYSTEM_PROMPT = """
あなたは株式会社Exampleの社内AIアシスタントです。
以下の社内規程に基づいて従業員の質問に答えてください。

[第1章 総則]
第1条(目的)この規程は...(以下、数万トークン分の内容)
"""

def ask_with_cache(user_question: str) -> str:
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system=[
            {
                "type": "text",
                "text": LARGE_SYSTEM_PROMPT,
                "cache_control": {"type": "ephemeral"}  # キャッシュ有効化
            }
        ],
        messages=[
            {"role": "user", "content": user_question}
        ]
    )
    
    # キャッシュの使用状況を確認
    usage = response.usage
    print(f"通常入力: {usage.input_tokens}")
    print(f"キャッシュ書き込み: {usage.cache_creation_input_tokens}")
    print(f"キャッシュ読み込み: {usage.cache_read_input_tokens}")
    
    return response.content[0].text

# 1回目: キャッシュ書き込みが発生
answer1 = ask_with_cache("有給休暇の申請方法を教えてください")

# 2回目以降(5分以内): キャッシュ読み込みでコスト大幅削減
answer2 = ask_with_cache("残業時間の上限はいくつですか?")

キャッシュ使用状況の取得

レスポンスのusageオブジェクトで、キャッシュの効果を確認できます:

def analyze_cache_usage(usage):
    total_input = (
        usage.input_tokens + 
        usage.cache_creation_input_tokens + 
        usage.cache_read_input_tokens
    )
    
    if total_input > 0:
        cache_hit_rate = usage.cache_read_input_tokens / total_input * 100
        print(f"キャッシュヒット率: {cache_hit_rate:.1f}%")
        
        # コスト節約効果の推定
        # キャッシュ読み込みは通常の10%のコストなので、90%節約
        saved_tokens = usage.cache_read_input_tokens * 0.9
        print(f"節約されたトークン換算: {saved_tokens:.0f}")

Python実装 — 応用編

ドキュメント分析のキャッシュ

大量のドキュメントを繰り返し参照する場合のパターンです。

import anthropic
from pathlib import Path

client = anthropic.Anthropic()

def create_document_analyzer(doc_content: str):
    """大量ドキュメントをキャッシュした分析関数を返す"""
    
    def analyze(question: str) -> str:
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=2048,
            messages=[
                {
                    "role": "user",
                    "content": [
                        {
                            "type": "text",
                            "text": f"以下のドキュメントを参照して質問に答えてください:\n\n{doc_content}",
                            "cache_control": {"type": "ephemeral"}  # ドキュメントをキャッシュ
                        },
                        {
                            "type": "text",
                            "text": f"\n質問: {question}"
                            # 質問部分はキャッシュしない(毎回変わるため)
                        }
                    ]
                }
            ]
        )
        return response.content[0].text
    
    return analyze

# 使用例: 大きなPDFのテキスト内容を一度ロードして繰り返し質問
large_doc = Path("company_guidelines.txt").read_text()
analyzer = create_document_analyzer(large_doc)

# 同じドキュメントに対して複数の質問 → キャッシュで効率化
answers = [
    analyzer("第3章の要点を教えてください"),
    analyzer("コンプライアンスポリシーについて説明してください"),
    analyzer("禁止事項のリストを出してください"),
]

マルチターン会話でのキャッシュ

会話履歴が長くなるチャットアプリでの最適化です。

import anthropic
from typing import List, Dict

client = anthropic.Anthropic()

class CachedChatSession:
    def __init__(self, system_prompt: str):
        self.system_prompt = system_prompt
        self.messages: List[Dict] = []
    
    def chat(self, user_message: str) -> str:
        # ユーザーメッセージを追加
        self.messages.append({
            "role": "user",
            "content": user_message
        })
        
        # 会話履歴の中間部分にキャッシュポイントを設定
        messages_with_cache = self._add_cache_breakpoints(self.messages)
        
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=1024,
            system=[
                {
                    "type": "text",
                    "text": self.system_prompt,
                    "cache_control": {"type": "ephemeral"}  # システムプロンプトをキャッシュ
                }
            ],
            messages=messages_with_cache
        )
        
        assistant_message = response.content[0].text
        self.messages.append({
            "role": "assistant",
            "content": assistant_message
        })
        
        return assistant_message
    
    def _add_cache_breakpoints(self, messages: List[Dict]) -> List[Dict]:
        """長い会話履歴の途中にキャッシュポイントを追加"""
        if len(messages) < 4:
            return messages
        
        # 最新の2メッセージを除いた古い部分をキャッシュ
        cached_messages = []
        for i, msg in enumerate(messages):
            if i == len(messages) - 3:  # 3つ前のメッセージにキャッシュポイント
                content = msg["content"]
                if isinstance(content, str):
                    cached_messages.append({
                        "role": msg["role"],
                        "content": [
                            {
                                "type": "text",
                                "text": content,
                                "cache_control": {"type": "ephemeral"}
                            }
                        ]
                    })
                else:
                    cached_messages.append(msg)
            else:
                cached_messages.append(msg)
        
        return cached_messages

# 使用例
session = CachedChatSession(
    system_prompt="あなたは熟練したPythonエンジニアです。コードの質問に答えてください。"
)

print(session.chat("Pythonのdecoratorの使い方を教えてください"))
print(session.chat("もう少し具体的な例を見せてください"))
print(session.chat("クラスデコレーターとの違いは?"))

TypeScript/Node.js実装

基本的なキャッシュ設定

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const SYSTEM_PROMPT = `
あなたはTypeScriptの専門家AIアシスタントです。
以下のガイドラインに従って回答してください:
[大量のガイドライン内容...]
`;

async function askWithCache(question: string): Promise<string> {
  const response = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    system: [
      {
        type: "text",
        text: SYSTEM_PROMPT,
        // @ts-ignore - cache_control は SDK v0.40+ でサポート
        cache_control: { type: "ephemeral" },
      },
    ],
    messages: [{ role: "user", content: question }],
  });

  // キャッシュ使用状況をログ
  const usage = response.usage as any;
  console.log({
    input_tokens: usage.input_tokens,
    cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
    cache_read_input_tokens: usage.cache_read_input_tokens ?? 0,
  });

  return response.content[0].type === "text" ? response.content[0].text : "";
}

// 実行例
(async () => {
  // 1回目: キャッシュ書き込み
  await askWithCache("async/awaitのベストプラクティスは?");

  // 2回目以降: キャッシュ読み込みで高速・低コスト
  await askWithCache("Genericsの効果的な使い方を教えてください");
})();

ツール定義のキャッシュ(Function Calling)

大量のツール定義を持つエージェントで特に効果的です。

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

// 大量のツール定義(実際は数十〜数百のAPIを持つエージェントも)
const TOOLS: Anthropic.Tool[] = [
  {
    name: "search_database",
    description: "社内データベースを検索します",
    input_schema: {
      type: "object" as const,
      properties: {
        query: { type: "string", description: "検索クエリ" },
        table: { type: "string", description: "テーブル名" },
      },
      required: ["query"],
    },
  },
  // ... 他にも多数のツール定義
];

async function runAgentWithCache(userMessage: string) {
  // ツール定義の最後の要素にキャッシュポイントを設定
  const toolsWithCache = TOOLS.map((tool, index) => {
    if (index === TOOLS.length - 1) {
      return {
        ...tool,
        cache_control: { type: "ephemeral" as const },
      };
    }
    return tool;
  });

  const response = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 4096,
    tools: toolsWithCache as Anthropic.Tool[],
    messages: [{ role: "user", content: userMessage }],
  });

  return response;
}

実用シナリオと適用判断基準

シナリオ1: RAGシステムの最適化

検索拡張生成(RAG)では、取得したドキュメントが大きい場合にキャッシュが効果的です。

def rag_with_cache(query: str, retrieved_docs: list[str]) -> str:
    # 取得したドキュメントをキャッシュ対象にまとめる
    context = "\n\n---\n\n".join(retrieved_docs)
    
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": f"参照ドキュメント:\n{context}",
                        "cache_control": {"type": "ephemeral"}  # ドキュメントをキャッシュ
                    },
                    {
                        "type": "text",
                        "text": f"\n質問: {query}"
                    }
                ]
            }
        ]
    )
    return response.content[0].text

シナリオ2: コードベース分析エージェント

大量のコードファイルを繰り返し参照するエージェントです。

def analyze_codebase(codebase_content: str, task: str) -> str:
    """コードベース全体をキャッシュしてタスクを実行"""
    
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=4096,
        system=[
            {
                "type": "text",
                "text": "あなたはシニアソフトウェアエンジニアです。",
                "cache_control": {"type": "ephemeral"}
            }
        ],
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": f"以下のコードベースを分析してください:\n\n{codebase_content}",
                        "cache_control": {"type": "ephemeral"}  # コードをキャッシュ
                    },
                    {
                        "type": "text",
                        "text": f"\nタスク: {task}"
                    }
                ]
            }
        ]
    )
    return response.content[0].text

# 同じコードに対して複数のタスク実行
analyze_codebase(code, "バグを見つけてください")
analyze_codebase(code, "テストを追加してください")
analyze_codebase(code, "パフォーマンスを改善してください")

キャッシュを使うべき状況・使わない状況

キャッシュが有効な状況

状況キャッシュ効果理由
長いシステムプロンプト(2,000+ tokens)★★★ 非常に高毎回の読み込みコストが激減
大量ドキュメントの繰り返し参照★★★ 非常に高1回のキャッシュで複数クエリに対応
大量ツール定義(関数呼び出し)★★★ 非常に高ツール定義は変わらないため効果絶大
長い会話履歴★★ 高古い部分をキャッシュして節約
Few-shotプロンプト★★ 高固定例示部分をキャッシュ可能

キャッシュが効果薄い状況

状況理由
短いシステムプロンプト(< 1,024 tokens)最小トークン数未満でキャッシュ不可
頻度の低いAPIコール(5分以上間隔)TTL切れで再キャッシュが必要
毎回異なるコンテキストキャッシュヒットが発生しない

ベストプラクティス

1. キャッシュポイントの配置戦略

# ✅ 良い例: 固定コンテンツの末尾にキャッシュポイント
messages=[
    {
        "role": "user",
        "content": [
            {"type": "text", "text": "大量の固定ドキュメント...", "cache_control": {"type": "ephemeral"}},
            {"type": "text", "text": "毎回変わる質問"}  # これはキャッシュしない
        ]
    }
]

# ❌ 悪い例: 変動するコンテンツをキャッシュ
# キャッシュポイント以前に動的コンテンツが含まれると毎回ミスヒット

2. キャッシュウォームアップ

本番リクエストの前にキャッシュを事前生成できます:

def warm_up_cache(system_prompt: str) -> None:
    """プリウォームリクエストでキャッシュを生成"""
    client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1,  # 最小限の出力
        system=[
            {
                "type": "text",
                "text": system_prompt,
                "cache_control": {"type": "ephemeral"}
            }
        ],
        messages=[{"role": "user", "content": "準備完了"}]
    )
    print("キャッシュウォームアップ完了")

3. コスト監視の実装

import json
from dataclasses import dataclass, asdict

@dataclass
class CacheStats:
    total_requests: int = 0
    total_input_tokens: int = 0
    cache_write_tokens: int = 0
    cache_read_tokens: int = 0
    
    @property
    def cache_hit_rate(self) -> float:
        total = self.total_input_tokens + self.cache_write_tokens + self.cache_read_tokens
        return (self.cache_read_tokens / total * 100) if total > 0 else 0
    
    @property
    def estimated_savings_ratio(self) -> float:
        """キャッシュなしと比較した節約率の推定"""
        if self.cache_read_tokens == 0:
            return 0
        normal_cost = self.cache_read_tokens  # キャッシュなしなら全部通常課金
        actual_cost = self.cache_read_tokens * 0.1  # キャッシュ読み込みは10%
        saved = normal_cost - actual_cost
        return saved / normal_cost * 100

stats = CacheStats()

def track_usage(usage):
    stats.total_requests += 1
    stats.total_input_tokens += usage.input_tokens
    stats.cache_write_tokens += getattr(usage, 'cache_creation_input_tokens', 0)
    stats.cache_read_tokens += getattr(usage, 'cache_read_input_tokens', 0)
    
    print(f"ヒット率: {stats.cache_hit_rate:.1f}% | 節約率推定: {stats.estimated_savings_ratio:.1f}%")

AIアプリ開発をさらに深く学ぶ

プロンプトキャッシュを含むClaude API活用を体系的に学びたい方には、ColosoのAI/Python開発コースがおすすめです。LLMアプリケーション開発からプロダクト化まで、実践的なカリキュラムで学べます。

Coloso — AI/Python開発コースを見る (PR)

AIエンジニアとして活躍するためのPython・LLM開発スキルを体系的に習得。Claude APIを使ったAIアプリ開発もカバーしています。


エンジニア転職でAI専門スキルを活かす

AIアプリ開発の実務経験は、現在の転職市場で非常に価値が高いスキルです。Claude APIやプロンプトエンジニアリングの知識を持つエンジニアへの需要は急増しています。

TechGo — AIエンジニア転職のプロに相談する (PR)

AIスキルを活かした転職・フリーランス活動をサポート。現役エンジニアによる無料キャリア相談を実施中。


よくある質問

Q: キャッシュはどのモデルに対応していますか?

A: 2026年5月時点で、claude-sonnet-4-6、claude-sonnet-4-5、claude-opus-4-7、claude-haiku-4-5 などの主要モデルが対応しています。最新の対応状況はAnthropic公式ドキュメントで確認してください。

Q: キャッシュが効いているか確認する方法は?

A: レスポンスのusageオブジェクトのcache_read_input_tokensが0より大きい場合、キャッシュヒットしています。

Q: 複数ユーザーでキャッシュは共有されますか?

A: キャッシュはAPIキー単位で管理されます。同じAPIキーを使うリクエストであれば、複数ユーザーからのリクエストでも同じキャッシュを活用できます。

Q: ストリーミングレスポンスでも使えますか?

A: はい、stream=Trueと組み合わせて使用できます。ストリーミングでもusage情報は最後のチャンクで取得できます。


まとめ

Claude APIのプロンプトキャッシュは、繰り返し処理を含むAIアプリケーションで最大90%のコスト削減をもたらす強力な機能です。

実装のポイントまとめ:

  1. cache_control: {"type": "ephemeral"} をキャッシュしたいコンテンツの末尾に配置
  2. 最小1,024トークン以上のコンテンツでないとキャッシュが有効にならない
  3. TTL は5分間 — アクティブに使われているシステムは自動維持される
  4. usage.cache_read_input_tokens でキャッシュ効果を監視する
  5. システムプロンプト・大量ドキュメント・ツール定義 に特に効果的

プロンプトキャッシュを活用することで、AIアプリの運用コストを大幅に削減しながら、レスポンス速度も改善できます。ぜひプロジェクトに取り入れてみてください。


関連記事: