> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Gemini コンテキストキャッシュ利用ガイド

> OpenAI 互換の Chat Completions API またはネイティブ Gemini API を使用して Gemini コンテキストキャッシュ（Context Cache）を作成・再利用し、cache_control で安定したプレフィックスをキャッシュして、長いコンテンツを繰り返し送信する際の token コストを削減します。

本ガイドでは、OpenAI 互換の Chat Completions API またはネイティブ Gemini API を使用して、Gemini コンテキストキャッシュ（Context Cache）を作成・再利用する方法を説明します。

事前準備：

```bash theme={null}
export API_KEY="あなたのAPIキー"
```

<Note>本ガイドの例では `gemini-3.6-flash` を使用します。ほかのモデルが Context Cache に対応しているかどうかは、プラットフォームのモデル説明および料金ページを参照してください。</Note>

## 適したユースケース

複数のリクエストで同じ長いコンテンツを繰り返し送信する場合、次のような安定したプレフィックスをキャッシュできます。

* 非常に長い system prompt
* 固定されたナレッジベースまたは製品ドキュメント
* マルチターン会話内の変化しない履歴メッセージ
* 繰り返し使用するツール定義と説明

Context Cache は、「先頭部分は変わらず、最後の質問だけが継続的に変わる」リクエストに適しています。

## 基本的な使い方

安定したプレフィックスの最後のメッセージにある content block へ `cache_control` を追加します。

```json theme={null}
{
  "type": "text",
  "text": "これは安定したプレフィックスの最後のコンテンツです",
  "cache_control": {
    "type": "ephemeral",
    "ttl": "5m"
  }
}
```

対応している TTL：

| TTL  | 意味        |
| ---- | --------- |
| `5m` | 5 分間キャッシュ |
| `1h` | 1 時間キャッシュ |

<Note>`ttl` を省略した場合は、デフォルトで `5m` が使用されます。</Note>

## メッセージ構造

次の構造を推奨します。

```text theme={null}
system
→ 安定した長文または履歴メッセージ
→ cache_control を設定した安定プレフィックスの境界
→ 現在のユーザー質問（キャッシュしない）
```

`cache_control` を設定したメッセージと、それ以前のすべてのメッセージがキャッシュプレフィックスを構成します。その後ろには、少なくとも 1 件のリアルタイムメッセージが必要です。

## OpenAI 互換リクエストの例

```bash theme={null}
curl "https://api.apimart.ai/v1/chat/completions" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.6-flash",
    "stream": false,
    "messages": [
      {
        "role": "system",
        "content": "提供された参考資料のみに基づいて、質問に厳密に回答してください。"
      },
      {
        "role": "user",
        "content": "ここに繰り返し使用する長い参考資料を入力します……"
      },
      {
        "role": "assistant",
        "content": [
          {
            "type": "text",
            "text": "上記の参考資料を読み、内容を理解しました。",
            "cache_control": {
              "type": "ephemeral",
              "ttl": "5m"
            }
          }
        ]
      },
      {
        "role": "user",
        "content": "参考資料の重要なポイントを 3 つにまとめてください。"
      }
    ]
  }'
```

初回送信時、システムはキャッシュの作成を試み、その新しいキャッシュを使用して現在のリクエストを処理します。

<Note>キャッシュ作成用の API を別途呼び出す必要はありません。`cache_control` は「キャッシュ境界」と「キャッシュ有効期間」の両方を表します。</Note>

## ネイティブ Gemini リクエストの例

ネイティブ Gemini の `generateContent` インターフェースでも、`contents[].parts[]` への `cache_control` の追加に対応しています。

```bash theme={null}
curl "https://api.apimart.ai/v1beta/models/gemini-3.6-flash:generateContent" \
  -H "x-goog-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "systemInstruction": {
      "parts": [
        {
          "text": "提供された参考資料のみに基づいて、質問に厳密に回答してください。"
        }
      ]
    },
    "contents": [
      {
        "role": "user",
        "parts": [
          {
            "text": "ここに繰り返し使用する長い参考資料を入力します……"
          }
        ]
      },
      {
        "role": "model",
        "parts": [
          {
            "text": "上記の参考資料を読み、内容を理解しました。",
            "cache_control": {
              "type": "ephemeral",
              "ttl": "5m"
            }
          }
        ]
      },
      {
        "role": "user",
        "parts": [
          {
            "text": "参考資料の重要なポイントを 3 つにまとめてください。"
          }
        ]
      }
    ]
  }'
```

`cache_control` は、プラットフォームが Gemini リクエスト形式に追加した拡張フィールドです。プラットフォームは境界を認識すると、転送前にこのフィールドを削除し、キャッシュコンテンツ（`cachedContent`）を自動的に作成または再利用します。

ストリーミングインターフェースでは同じリクエストボディを使用し、URL だけを次のように変更します。

```bash theme={null}
curl -N "https://api.apimart.ai/v1beta/models/gemini-3.6-flash:streamGenerateContent?alt=sse" \
  -H "x-goog-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [
      {
        "role": "user",
        "parts": [
          {
            "text": "ここに繰り返し使用する長い参考資料を入力します……",
            "cache_control": {
              "type": "ephemeral",
              "ttl": "5m"
            }
          }
        ]
      },
      {
        "role": "user",
        "parts": [
          {
            "text": "参考資料の重要なポイントを 3 つにまとめてください。"
          }
        ]
      }
    ]
  }'
```

再利用時は `systemInstruction`、境界より前の `contents`、TTL、tools を変更せず、境界より後ろのリアルタイムコンテンツだけを変更します。

## 作成と再利用の流れ

`cache_control` を含むリクエストを初めて送信する場合：

```text theme={null}
安定したプレフィックスを識別
→ Context Cache を作成
→ 現在のリクエストで新しいキャッシュを参照
→ モデルの結果を返す
```

同じ安定したプレフィックスを再度送信する場合：

```text theme={null}
同じ安定したプレフィックスを識別
→ 有効期限内の Context Cache を再利用
→ 現在のリアルタイムコンテンツだけを送信
→ モデルの結果を返す
```

そのため、初回リクエストでもキャッシュヒットした token が多く返される場合があります。これは正常な動作であり、事前に「ウォームアップリクエスト」を送信する必要はありません。

## キャッシュの再利用

再度リクエストする際は、次の内容を変更しないでください。

* モデル
* `cache_control` より前のすべてのメッセージ
* `cache_control.ttl`
* ツール定義（tools を使用する場合）
* ネイティブ Gemini リクエストの `systemInstruction`

境界より後ろのリアルタイム質問だけを変更します。

```json theme={null}
{
  "role": "user",
  "content": "参考資料ではどのようなリスクが挙げられていますか？"
}
```

安定したプレフィックスが一致し、キャッシュの有効期限が切れていなければ、システムは既存のキャッシュを再利用します。

次の変更を行うと、別のキャッシュが生成されます。

* 安定したプレフィックス内のテキストまたはメッセージ順序の変更
* モデルの変更
* `5m` から `1h` への変更
* tools またはツールパラメータ定義の変更
* 異なる API ユーザーまたはチャネルの使用

## Python の例

```python theme={null}
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.apimart.ai/v1",
)

stable_messages = [
    {
        "role": "system",
        "content": "提供された参考資料のみに基づいて、質問に厳密に回答してください。",
    },
    {
        "role": "user",
        "content": "ここに繰り返し使用する長い参考資料を入力します……",
    },
    {
        "role": "assistant",
        "content": [
            {
                "type": "text",
                "text": "上記の参考資料を読み、内容を理解しました。",
                "cache_control": {
                    "type": "ephemeral",
                    "ttl": "5m",
                },
            }
        ],
    },
]

response = client.chat.completions.create(
    model="gemini-3.6-flash",
    messages=[
        *stable_messages,
        {
            "role": "user",
            "content": "参考資料の重要なポイントを 3 つにまとめてください。",
        },
    ],
)

print(response.choices[0].message.content)
print(response.usage)
```

以降のリクエストでは同じ `stable_messages` を再利用し、最後の user メッセージだけを置き換えます。

## キャッシュヒットの確認

### OpenAI 互換レスポンス

レスポンス内の次の値を確認します。

```json theme={null}
{
  "usage": {
    "prompt_tokens": 14430,
    "prompt_tokens_details": {
      "cached_tokens": 14420,
      "cache_write_tokens": 0
    }
  }
}
```

フィールドの説明：

| フィールド                | 意味                                   |
| -------------------- | ------------------------------------ |
| `prompt_tokens`      | このリクエストの全入力 token                    |
| `cached_tokens`      | このリクエストでキャッシュから読み取った入力 token         |
| `cache_write_tokens` | キャッシュへ書き込んだ token。`0` が返されるのは正常な動作です |

システムはキャッシュを作成してから同じモデル呼び出し内で参照できるため、初回リクエストでも `cached_tokens` が大きな値になる場合があります。

### ネイティブ Gemini レスポンス

レスポンス内の `usageMetadata.cachedContentTokenCount` を確認します。

```json theme={null}
{
  "usageMetadata": {
    "promptTokenCount": 13926,
    "cachedContentTokenCount": 13916,
    "totalTokenCount": 13954
  }
}
```

フィールドの説明：

| フィールド                     | 意味                           |
| ------------------------- | ---------------------------- |
| `promptTokenCount`        | このリクエストの全入力 token            |
| `cachedContentTokenCount` | このリクエストでキャッシュから読み取った入力 token |
| `totalTokenCount`         | このリクエストの入力と出力の token 合計      |

`streamGenerateContent` は、SSE レスポンスフレーム内に同じ `usageMetadata` を返します。クライアントはこのフィールドを含むレスポンスフレームを読み取り、最初のテキスト部分だけを確認しないようにしてください。

## 利用上の推奨事項

<Tip>
  1. 本当に安定していて、複数回再利用する長いコンテンツだけをキャッシュしてください。
  2. リクエストごとに変わる質問は、`cache_control` の境界より後ろに配置してください。
  3. 安定したプレフィックスにタイムスタンプ、ランダム ID、動的なユーザー情報を含めないでください。
  4. 短時間に繰り返し呼び出す予定の場合は `5m` を使用してください。
  5. より長い再利用期間が必要な場合は `1h` を使用してください。
  6. プレフィックスが短すぎる場合、モデルが未対応の場合、またはキャッシュが一時的に利用できない場合、リクエストは自動的に通常モードで実行されることがあります。
  7. ネイティブ Gemini のキャッシュ境界は必ず `contents[].parts[]` 内に配置し、`systemInstruction` には配置しないでください。
</Tip>

## よくある質問

<AccordionGroup>
  <Accordion title="ネイティブ Gemini リクエスト形式でキャッシュを自動作成できますか？">
    はい。`generateContent` と `streamGenerateContent` は同じ `cache_control` 構造を使用します。境界は必ず `contents[].parts[]` 内に配置し、境界を設定した content の後ろに少なくとも 1 件のリアルタイム content を残す必要があります。

    リクエストでネイティブの `cachedContent` リソース名を明示的に指定している場合、プラットフォームはユーザーが指定したリソースを優先し、キャッシュの自動作成を行いません。
  </Accordion>

  <Accordion title="キャッシュがヒットしないのはなぜですか？">
    一般的な原因は次のとおりです。

    * 安定したプレフィックスが前回のリクエストと完全には一致していない
    * TTL の有効期限が切れている
    * モデルまたは tools を変更した
    * キャッシュコンテンツがモデルで必要な最小 token 数に達していない
    * `cache_control` を最後のメッセージに配置し、リアルタイム質問を残していない
  </Accordion>

  <Accordion title="cache_control を最後のメッセージに配置できますか？">
    推奨しません。通常、最後のメッセージは現在のリアルタイム質問であり、キャッシュすべきではありません。境界より後ろにリアルタイムメッセージがない場合、リクエストは通常モードで実行されます。
  </Accordion>

  <Accordion title="ほかの TTL を設定できますか？">
    できません。現在対応しているのは `5m` と `1h` のみです。ほかの値を指定すると HTTP 400 が返されます。
  </Accordion>

  <Accordion title="複数のキャッシュ境界を設定できますか？">
    できます。ただし、すべての境界で同じ TTL を使用する必要があり、システムは最後の境界を使用します。通常は、構造を分かりやすくするため、リクエストごとに境界を 1 つだけ設定することを推奨します。
  </Accordion>

  <Accordion title="キャッシュが利用できないとリクエストは失敗しますか？">
    通常は失敗しません。キャッシュを作成または再利用する条件を満たしていない場合、システムは自動的に通常のリクエストを使用します。ただし、無効な TTL や異なる TTL の混在など、パラメータに誤りがある場合を除きます。
  </Accordion>

  <Accordion title="モデルで Context Cache が有効になっていない場合はどうなりますか？">
    リクエストは自動的に通常モードで実行され、明示的なキャッシュは作成されず、キャッシュストレージ料金も発生しません。通常の入力、出力、および存在する可能性のある暗黙的キャッシュについては、そのモデルの従来のルールに従って引き続き課金されます。
  </Accordion>

  <Accordion title="cache_write_tokens が 0 なのはなぜですか？">
    これは Gemini コンテキストキャッシュの正常な動作です。キャッシュ作成コストは個別のキャッシュストレージ料金として記録され、OpenAI/Claude 形式の `cache_write_tokens` でキャッシュ作成量を表すことはありません。
  </Accordion>

  <Accordion title="cached_tokens が 0 より大きければ、必ず明示的なキャッシュが作成されたということですか？">
    必ずしもそうではありません。システム自体の暗黙的キャッシュがヒットする場合もあります。通常のユーザーはキャッシュから読み取った token を見て、今回のリクエストでキャッシュ読み取りが適用されたかどうかを判断できます。明示的なキャッシュ作成料金を確認するには、プラットフォームの使用量ログにある Context Cache storage の記録を参照してください。
  </Accordion>

  <Accordion title="キャッシュ料金はどのように計算されますか？">
    キャッシュ作成時に、一度だけキャッシュストレージ料金が発生する場合があります。キャッシュ使用時は、ヒットした token がキャッシュ読み取り価格で課金されます。具体的な価格は、プラットフォームに表示されるモデル料金を参照してください。
  </Accordion>
</AccordionGroup>
