すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Model Studio:コンテキストキャッシュ

最終更新日:Dec 20, 2025

大規模言語モデルを呼び出す際、マルチターン対話や同じドキュメントに対して複数の質問をする場合など、異なる推論リクエストで入力が重複することがあります。コンテキストキャッシュ機能は、これらのリクエストの共通プレフィックスを保存することで、冗長な計算を削減します。これにより、応答品質に影響を与えることなく、応答速度を向上させ、コストを削減します。

さまざまなシナリオに対応するため、コンテキストキャッシュには 2 つのモードが用意されています。利便性、確実性、コストの要件に応じてモードを選択できます。

  • 暗黙的キャッシュ:この自動モードは追加の構成が不要で、無効にすることはできません。利便性を優先する一般的なシナリオに適しています。システムはリクエスト内容の共通プレフィックス自動的に検出してキャッシュしますが、キャッシュヒット率は保証されません。キャッシュされた部分は、入力トークンの標準価格の 20% で課金されます。

  • 明示的キャッシュ:このキャッシュモードは手動で有効にする必要があります。特定のコンテンツに対して手動でキャッシュを作成し、5 分以内のヒットを保証できます。キャッシュの作成に使用されたトークンは、標準入力トークン価格の 125% で課金されます。ただし、その後のヒットは標準価格のわずか 10% で課金されます。

項目

暗黙的キャッシュ

明示的キャッシュ

応答品質への影響

影響なし

影響なし

ヒット確率

保証されません。具体的なヒット確率はシステムが決定します。

ヒットを保証

キャッシュ作成に使用されるトークン

標準入力トークン価格の 100%

標準入力トークン価格の 125%

キャッシュされた入力トークン

標準入力トークン価格の 20%

標準入力トークン価格の 10%

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

256

1,024

キャッシュの有効期間

保証されません。システムは長期間使用されていないキャッシュデータを定期的にパージします。

5 分 (ヒット時にリセット)

説明

暗黙的キャッシュと明示的キャッシュは相互排他的です。1 つのリクエストでは 1 つのモードしか使用できません。

暗黙的キャッシュ

モデルの可用性

シンガポールリージョン

北京リージョン

説明

スナップショットモデルと最新モデルは現在サポートされていません。

仕組み

暗黙的キャッシュをサポートするモデルにリクエストを送信すると、この機能は自動的に動作します。

  1. 検索:システムがリクエストを受信した後、プレフィックスマッチングを使用して、messages 配列内のコンテンツの共通プレフィックスがキャッシュに存在するかどうかを判断します。

  2. 決定

    • キャッシュヒットが発生した場合、システムはキャッシュされた結果を生成に使用します。

    • キャッシュミスが発生した場合、システムはリクエストを通常どおり処理し、将来のリクエストのために現在のプロンプトのプレフィックスをキャッシュに保存します。

システムは、長期間使用されていないキャッシュデータを定期的にパージします。キャッシュヒット率は 100% 保証されません。同一のリクエストコンテキストであってもキャッシュミスが発生する可能性があります。実際のヒット確率はシステムが決定します。
説明

256 トークン未満のコンテンツはキャッシュされません。

キャッシュヒット率の向上

暗黙的キャッシュヒットは、システムが異なるリクエストで共通のプレフィックスを共有していると判断した場合に発生します。ヒット確率を高めるには、繰り返し出現するコンテンツをプロンプトの先頭に、可変のコンテンツを末尾に配置します。

  • テキストモデル:システムが「ABCD」をキャッシュしたと仮定します。「ABE」というリクエストは「AB」部分にヒットする可能性がありますが、「BCD」というリクエストはヒットしません。

  • 視覚理解モデル:

    • 同じ画像またはビデオに関する複数の質問の場合:画像またはビデオをテキストの前に配置すると、ヒット確率が高まります。

    • 異なる画像またはビデオに関する同じ質問の場合:テキストを画像またはビデオの前に配置すると、ヒット確率が高まります。

課金

暗黙的キャッシュモードを有効にするための追加料金はありません。

リクエストがキャッシュにヒットした場合、一致した入力トークンは cached_tokens として、標準の input_token 価格の 20% で課金されます。一致しなかった入力トークンは、標準の input_token 価格で課金されます。出力トークンは標準価格で課金されます。

例:リクエストに 10,000 の入力トークンが含まれ、そのうち 5,000 がキャッシュにヒットした場合、コストは次のように計算されます。

  • 不一致トークン (5,000):標準価格の 100% で課金されます。

  • 一致トークン (5,000):標準価格の 20% で課金されます。

合計入力コストは、キャッシュなしモードのコストの 60% に相当します:(50% × 100%) + (50% × 20%) = 60%。

image.png

キャッシュされたトークン数は、応答cached_tokens 属性から取得できます。

OpenAI 互換のバッチメソッドは、キャッシュ割引の対象外です。

キャッシュヒットの例

テキスト生成モデル

OpenAI 互換

OpenAI 互換のメソッドを使用し、暗黙的キャッシュがトリガーされると、次のような応答を受け取ります。キャッシュされたトークン数は usage.prompt_tokens_details.cached_tokens で確認できます。この値は usage.prompt_tokens の一部です。

{
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "I am a large-scale language model developed by Alibaba Cloud. My name is Qwen."
            },
            "finish_reason": "stop",
            "index": 0,
            "logprobs": null
        }
    ],
    "object": "chat.completion",
    "usage": {
        "prompt_tokens": 3019,
        "completion_tokens": 104,
        "total_tokens": 3123,
        "prompt_tokens_details": {
            "cached_tokens": 2048
        }
    },
    "created": 1735120033,
    "system_fingerprint": null,
    "model": "qwen-plus",
    "id": "chatcmpl-6ada9ed2-7f33-9de2-8bb0-78bd4035025a"
}

DashScope

DashScope Python SDK または HTTP リクエストを使用し、暗黙的キャッシュがトリガーされると、次のような応答を受け取ります。キャッシュされたトークン数は usage.prompt_tokens_details.cached_tokens で確認できます。この値は usage.input_tokens の一部です。

{
    "status_code": 200,
    "request_id": "f3acaa33-e248-97bb-96d5-cbeed34699e1",
    "code": "",
    "message": "",
    "output": {
        "text": null,
        "finish_reason": null,
        "choices": [
            {
                "finish_reason": "stop",
                "message": {
                    "role": "assistant",
                    "content": "I am a large-scale language model from Alibaba Cloud. My name is Qwen. I can generate various types of text, such as articles, stories, and poems, and can adapt and expand based on different scenarios and needs. Additionally, I can answer various questions and provide help and solutions. If you have any questions or need assistance, feel free to let me know, and I will do my best to provide support. Please note that repeatedly sending the same content may not yield more detailed responses. It is recommended that you provide more specific information or vary your questions so I can better understand your needs."
                }
            }
        ]
    },
    "usage": {
        "input_tokens": 3019,
        "output_tokens": 101,
        "prompt_tokens_details": {
            "cached_tokens": 2048
        },
        "total_tokens": 3120
    }
}

視覚理解モデル

OpenAI 互換

OpenAI 互換のメソッドを使用し、暗黙的キャッシュがトリガーされると、次のような応答を受け取ります。キャッシュされたトークン数は usage.prompt_tokens_details.cached_tokens で確認できます。この値は usage.prompt_tokens の一部です。

{
  "id": "chatcmpl-3f3bf7d0-b168-9637-a245-dd0f946c700f",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": "This image shows a heartwarming scene of a woman and a dog interacting on a beach. The woman is wearing a plaid shirt and sitting on the sand, smiling as she interacts with the dog. The dog is a large, light-colored breed wearing a colorful collar, with its front paw raised as if to shake hands or give a high-five to the woman. The background is a vast ocean and sky, with sunlight shining from the right side of the frame, adding a warm and peaceful atmosphere to the entire scene.",
        "refusal": null,
        "role": "assistant",
        "audio": null,
        "function_call": null,
        "tool_calls": null
      }
    }
  ],
  "created": 1744956927,
  "model": "qwen-vl-max",
  "object": "chat.completion",
  "service_tier": null,
  "system_fingerprint": null,
  "usage": {
    "completion_tokens": 93,
    "prompt_tokens": 1316,
    "total_tokens": 1409,
    "completion_tokens_details": null,
    "prompt_tokens_details": {
      "audio_tokens": null,
      "cached_tokens": 1152
    }
  }
}

DashScope

DashScope Python SDK または HTTP リクエストを使用し、暗黙的キャッシュがトリガーされると、キャッシュされたトークン数は合計入力トークン (usage.input_tokens) に含まれます。特定のフィールドはリージョンとモデルによって異なります。

  • 北京リージョン:

    • qwen-vl-max および qwen-vl-plus の場合、値は usage.prompt_tokens_details.cached_tokens で確認できます。

    • qwen3-vl-plus および qwen3-vl-flash の場合、値は usage.cached_tokens で確認できます。

  • シンガポールリージョン:すべてのモデルで、値は usage.cached_tokens で確認できます。

現在 usage.cached_tokens を使用しているモデルは、将来的に usage.prompt_tokens_details.cached_tokens を使用するようにアップグレードされる予定です。
{
  "status_code": 200,
  "request_id": "06a8f3bb-d871-9db4-857d-2c6eeac819bc",
  "code": "",
  "message": "",
  "output": {
    "text": null,
    "finish_reason": null,
    "choices": [
      {
        "finish_reason": "stop",
        "message": {
          "role": "assistant",
          "content": [
            {
              "text": "This image shows a heartwarming scene of a woman and a dog interacting on a beach. The woman is wearing a plaid shirt and sitting on the sand, smiling as she interacts with the dog. The dog is a large breed wearing a colorful collar, with its front paw raised as if to shake hands or give a high-five to the woman. The background is a vast ocean and sky, with sunlight shining from the right side of the frame, adding a warm and peaceful atmosphere to the entire scene."
            }
          ]
        }
      }
    ]
  },
  "usage": {
    "input_tokens": 1292,
    "output_tokens": 87,
    "input_tokens_details": {
      "text_tokens": 43,
      "image_tokens": 1249
    },
    "total_tokens": 1379,
    "output_tokens_details": {
      "text_tokens": 87
    },
    "image_tokens": 1249,
    "cached_tokens": 1152
  }
}

利用シーン

リクエストに同じプレフィックスがある場合、コンテキストキャッシュは推論速度を向上させ、推論コストを削減し、最初のパケットのレイテンシを短縮できます。以下は典型的な適用シナリオです。

  1. 長文に基づく Q&A

    小説、教科書、法的文書などの固定された長文に対して複数のリクエストを送信する必要があるシナリオに適しています。

    最初のリクエストのメッセージ配列

    messages = [{"role": "system","content": "あなたは生徒の読解を手助けできる国語の先生です。"},
              {"role": "user","content": "<記事の内容> この文章で著者はどのような感情や考えを表現していますか?"}]

    後続のリクエストで使用するメッセージの配列

    messages = [{"role": "system","content": "あなたは生徒の読解を手助けできる国語の先生です。"},
              {"role": "user","content": "<記事の内容> この文章の第 3 段落を分析してください。"}]

    質問は異なりますが、すべて同じ記事に基づいています。同一のシステムプロンプトと記事の内容が、大きく反復的なプレフィックスを形成するため、高いキャッシュヒット確率が得られます。

  2. コード自動補完

    コード自動補完のシナリオでは、モデルはコンテキスト内の既存のコードに基づいてコードを補完します。ユーザーがコードを書き進めるにつれて、コードのプレフィックスは変更されません。コンテキストキャッシュは既存のコードをキャッシュして、補完速度を向上させることができます。

  3. マルチターン対話

    マルチターン対話を実現するには、各ターンの会話履歴を messages 配列に追加します。したがって、各新しいリクエストには前のターンがプレフィックスとして含まれるため、キャッシュヒットの確率が高くなります。

    最初のターンのメッセージ配列

    messages=[{"role": "system","content": "あなたは役立つアシスタントです。"},
              {"role": "user","content": "あなたは誰ですか?"}]

    2ターン目のメッセージ配列

    messages=[{"role": "system","content": "You are a helpful assistant."},
              {"role": "user","content": "Who are you?"},
              {"role": "assistant","content": "I am Qwen, developed by Alibaba Cloud."},
              {"role": "user","content": "What can you do?"}]

    会話のターン数が増えるにつれて、キャッシングは推論速度とコストの面でより有利になります。

  4. ロールプレイングまたは few-shot 学習

    ロールプレイングまたは few-shot 学習のシナリオでは、通常、モデルの出力形式をガイドするためにプロンプトに大量の情報を含める必要があります。これにより、異なるリクエスト間で大きく反復的なプレフィックスが生じます。

    たとえば、モデルにマーケティングの専門家として振る舞わせるために、システムプロンプトには大量のテキストが含まれます。以下は 2 つのリクエストのメッセージ例です。

    system_prompt = """あなたは経験豊富なマーケティングの専門家です。以下の形式で、さまざまな製品に対する詳細なマーケティング提案を提供してください。
    
    1. ターゲットオーディエンス:xxx
    
    2. 主なセールスポイント:xxx
    
    3. マーケティングチャネル:xxx
    ...
    12. 長期的な開発戦略:xxx
    
    提案が具体的で、実行可能で、製品の特徴に非常に関連していることを確認してください。"""
    
    # 最初のリクエストのユーザーメッセージは、スマートウォッチについて尋ねます
    messages_1=[
      {"role": "system", "content": system_prompt},
      {"role": "user", "content": "新しく発売されたスマートウォッチのマーケティング提案を提供してください。"}
    ]
    
    # 2 番目のリクエストのユーザーメッセージは、ラップトップについて尋ねます。system_prompt が同じであるため、キャッシュヒットの確率が高くなります。
    messages_2=[
      {"role": "system", "content": system_prompt},
      {"role": "user", "content": "新しく発売されたラップトップのマーケティング提案を提供してください。"}
    ]

    コンテキストキャッシュを使用すると、ユーザーが尋ねる製品の種類を頻繁に変更しても (スマートウォッチからラップトップなど)、キャッシュがトリガーされた後、システムは迅速に応答できます。

  5. ビデオ理解

    ビデオ理解のシナリオでは、同じビデオについて複数の質問をする場合、ビデオをテキストの前に配置するとキャッシュヒット率が向上します。異なるビデオについて同じ質問をする場合、テキストをビデオの前に配置するとキャッシュヒット率が向上します。以下は、同じビデオに関する 2 つのリクエストのメッセージ例です。

    # 最初のリクエストのユーザーメッセージは、このビデオの内容について尋ねます
    messages1 = [
        {"role":"system","content":[{"text": "あなたは役立つアシスタントです。"}]},
        {"role": "user",
            "content": [
                {"video": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/20250328/eepdcq/phase_change_480p.mov"},
                {"text": "このビデオの内容は何ですか?"}
            ]
        }
    ]
    
    # 2 番目のリクエストのユーザーメッセージは、ビデオのタイムスタンプについて尋ねます。質問は同じビデオに基づいているため、ビデオをテキストの前に配置するとキャッシュヒットの確率が高くなります。
    messages2 = [
        {"role":"system","content":[{"text": "あなたは役立つアシスタントです。"}]},
        {"role": "user",
            "content": [
                {"video": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/20250328/eepdcq/phase_change_480p.mov"},
                {"text": "ビデオ内の一連のイベントを説明してください。開始時刻 (start_time)、終了時刻 (end_time)、イベント (event) を JSON 形式で出力してください。```json``` コードセグメントは含めないでください。"}
            ]
        }
    ]

明示的キャッシュ

暗黙的キャッシュと比較して、明示的キャッシュは手動でキャッシュを作成する必要があり、それに対応するオーバーヘッドが発生します。ただし、より高いキャッシュヒット率と低いアクセスレイテンシを提供します。

使用方法

メッセージに "cache_control": {"type": "ephemeral"} マーカーを追加できます。システムは、各 content ブロックの位置から最大 20 個の cache_control マーカーまで遡ってキャッシュヒットを試みます。

1 つのリクエストでサポートされるキャッシュマーカーは最大 4 つです。
  • キャッシュミス

    システムは、messages 配列の先頭から cache_control マーカーまでのコンテンツを使用して新しいキャッシュブロックを作成します。このキャッシュブロックは 5 分間有効です。

    キャッシュの作成は、モデルが応答した後に発生します。作成リクエストが完了した後、キャッシュヒットを試みることができます。
    キャッシュブロックの最小コンテンツ長は 1,024 トークンです。
  • キャッシュヒット

    最も長く一致するプレフィックスがヒットとして選択され、対応するキャッシュブロックの有効期間が 5 分にリセットされます。

以下に例を示します。

  1. 最初のリクエストを開始:1,024 トークンを超えるテキスト A を含むシステムメッセージを送信し、キャッシュマーカーを追加します。

    [{"role": "system", "content": [{"type": "text", "text": A, "cache_control": {"type": "ephemeral"}}]}] 

    システムは最初のキャッシュブロックを作成します。これをキャッシュブロック A と呼びます。

  2. 2 番目のリクエストを開始:次の構造でリクエストを送信できます。

    [
        {"role": "system", "content": A},
        <other messages>
        {"role": "user","content": [{"type": "text", "text": B, "cache_control": {"type": "ephemeral"}}]}
    ]
    • 「other messages」が 20 個以下の場合、キャッシュブロック A がヒットし、その有効期間が 5 分にリセットされます。同時に、システムは A、other messages、および B のコンテンツに基づいて新しいキャッシュブロックを作成します。

    • 「other messages」が 20 個を超える場合、キャッシュブロック A はヒットしません。システムは引き続き完全なコンテキスト (A + other messages + B) に基づいて新しいキャッシュブロックを作成します。

モデルの可用性

Qwen Max:qwen3-max

Qwen Plus:qwen-plus

Qwen Flash:qwen-flash

Qwen-Coder:qwen3-coder-plus、qwen3-coder-flash

上記のモデルは、中国本土リージョンと国際リージョンの両方で明示的キャッシュ機能をサポートしています。
スナップショットモデルと最新モデルは現在サポートされていません。

はじめに

以下の例は、OpenAI 互換 API と DashScope プロトコルにおけるキャッシュブロックの作成とヒットのメカニズムを示しています。

OpenAI 互換

from openai import OpenAI
import os

client = OpenAI(
    # 環境変数を設定していない場合は、次の行を api_key="sk-xxx" に置き換えてください
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # 以下は北京リージョンの base_url です。シンガポールリージョンのモデルを使用する場合は、base_url を https://dashscope-intl.aliyuncs.com/compatible-mode/v1 に置き換えてください
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# シミュレートされたコードリポジトリのコンテンツ。キャッシュ用の最小プロンプト長は 1,024 トークンです。
long_text_content = "<Your Code Here>" * 400

# リクエストを行う関数
def get_completion(user_input):
    messages = [
        {
            "role": "system",
            "content": [
                {
                    "type": "text",
                    "text": long_text_content,
                    # ここに cache_control マーカーを配置して、messages 配列の先頭から現在のコンテンツ位置までのキャッシュブロックを作成します。
                    "cache_control": {"type": "ephemeral"},
                }
            ],
        },
        # 質問内容は毎回異なります
        {
            "role": "user",
            "content": user_input,
        },
    ]
    completion = client.chat.completions.create(
        # 明示的キャッシュをサポートするモデルを選択します
        model="qwen3-coder-plus",
        messages=messages,
    )
    return completion

# 最初のリクエスト
first_completion = get_completion("このコードの内容は何ですか?")
print(f"最初のリクエストで作成されたキャッシュトークン: {first_completion.usage.prompt_tokens_details.cache_creation_input_tokens}")
print(f"最初のリクエストでヒットしたキャッシュトークン: {first_completion.usage.prompt_tokens_details.cached_tokens}")
print("=" * 20)
# 2 番目のリクエスト、コード内容は同じで、質問のみ変更
second_completion = get_completion("このコードはどのように最適化できますか?")
print(f"2 番目のリクエストで作成されたキャッシュトークン: {second_completion.usage.prompt_tokens_details.cache_creation_input_tokens}")
print(f"2 番目のリクエストでヒットしたキャッシュトークン: {second_completion.usage.prompt_tokens_details.cached_tokens}")

DashScope

import os
from dashscope import Generation
# シンガポールリージョンのモデルを使用する場合は、次の行のコメントを解除してください。
# dashscope.base_http_api_url = "https://dashscope-intl.aliyuncs.com/api/v1"

# シミュレートされたコードリポジトリのコンテンツ。キャッシュ用の最小プロンプト長は 1,024 トークンです。
long_text_content = "<Your Code Here>" * 400

# リクエストを行う関数
def get_completion(user_input):
    messages = [
        {
            "role": "system",
            "content": [
                {
                    "type": "text",
                    "text": long_text_content,
                    # ここに cache_control マーカーを配置して、messages 配列の先頭から現在のコンテンツ位置までのキャッシュブロックを作成します。
                    "cache_control": {"type": "ephemeral"},
                }
            ],
        },
        # 質問内容は毎回異なります
        {
            "role": "user",
            "content": user_input,
        },
    ]
    response = Generation.call(
        # 環境変数が設定されていない場合は、この行を Model Studio API キーに置き換えてください: api_key = "sk-xxx",
        api_key=os.getenv("DASHSCOPE_API_KEY"), 
        model="qwen3-coder-plus",
        messages=messages,
        result_format="message"
    )
    return response

# 最初のリクエスト
first_completion = get_completion("このコードの内容は何ですか?")
print(f"最初のリクエストで作成されたキャッシュトークン: {first_completion.usage.prompt_tokens_details['cache_creation_input_tokens']}")
print(f"最初のリクエストでヒットしたキャッシュトークン: {first_completion.usage.prompt_tokens_details['cached_tokens']}")
print("=" * 20)
# 2 番目のリクエスト、コード内容は同じで、質問のみ変更
second_completion = get_completion("このコードはどのように最適化できますか?")
print(f"2 番目のリクエストで作成されたキャッシュトークン: {second_completion.usage.prompt_tokens_details['cache_creation_input_tokens']}")
print(f"2 番目のリクエストでヒットしたキャッシュトークン: {second_completion.usage.prompt_tokens_details['cached_tokens']}")
// 最小 Java SDK バージョンは 2.21.6 です
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.MessageContentText;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;

import java.util.Arrays;
import java.util.Collections;

public class Main {
    private static final String MODEL = "qwen3-coder-plus";
    // シミュレートされたコードリポジトリのコンテンツ (1,024 トークンを超えるように 400 回繰り返す)
    private static final String LONG_TEXT_CONTENT = generateLongText(400);
    private static String generateLongText(int repeatCount) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < repeatCount; i++) {
            sb.append("<Your Code Here>");
        }
        return sb.toString();
    }
    private static GenerationResult getCompletion(String userQuestion)
            throws NoApiKeyException, ApiException, InputRequiredException {
        // シンガポールリージョンのモデルを使用する場合は、https://dashscope.aliyuncs.com/api/v1 を https://dashscope-intl.aliyuncs.com/api/v1 に変更してください
        Generation gen = new Generation("http", "https://dashscope.aliyuncs.com/api/v1");

        // キャッシュ制御付きのシステムメッセージを構築
        MessageContentText systemContent = MessageContentText.builder()
                .type("text")
                .text(LONG_TEXT_CONTENT)
                .cacheControl(MessageContentText.CacheControl.builder()
                        .type("ephemeral") // キャッシュタイプを設定
                        .build())
                .build();

        Message systemMsg = Message.builder()
                .role(Role.SYSTEM.getValue())
                .contents(Collections.singletonList(systemContent))
                .build();
        Message userMsg = Message.builder()
                .role(Role.USER.getValue())
                .content(userQuestion)
                .build();

        // リクエストパラメーターを構築
        GenerationParam param = GenerationParam.builder()
                .model(MODEL)
                .messages(Arrays.asList(systemMsg, userMsg))
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .build();
        return gen.call(param);
    }

    private static void printCacheInfo(GenerationResult result, String requestLabel) {
        System.out.printf("%s で作成されたキャッシュトークン: %d%n", requestLabel, result.getUsage().getPromptTokensDetails().getCacheCreationInputTokens());
        System.out.printf("%s でヒットしたキャッシュトークン: %d%n", requestLabel, result.getUsage().getPromptTokensDetails().getCachedTokens());
    }

    public static void main(String[] args) {
        try {
            // 最初のリクエスト
            GenerationResult firstResult = getCompletion("このコードの内容は何ですか?");
            printCacheInfo(firstResult, "最初のリクエスト");
            System.out.println(new String(new char[20]).replace('\0', '='));            // 2 番目のリクエスト
            GenerationResult secondResult = getCompletion("このコードはどのように最適化できますか?");
            printCacheInfo(secondResult, "2 番目のリクエスト");
        } catch (NoApiKeyException | ApiException | InputRequiredException e) {
            System.err.println("API 呼び出しに失敗しました: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

シミュレートされたコードリポジトリは、cache_control マーカーを追加することで明示的キャッシュを有効にします。このコードリポジトリをクエリする後続のリクエストでは、システムは再計算なしでこのキャッシュブロックを再利用できます。これにより、応答が高速化され、コストが削減されます。

最初のリクエストで作成されたキャッシュトークン: 1605
最初のリクエストでヒットしたキャッシュトークン: 0
====================
2 番目のリクエストで作成されたキャッシュトークン: 0
2 番目のリクエストでヒットしたキャッシュトークン: 1605

複数のキャッシュマーカーによる詳細な制御

複雑なシナリオでは、プロンプトはしばしば再利用頻度が異なる複数の部分で構成されます。複数のキャッシュマーカーを使用して、詳細な制御を行うことができます。

たとえば、インテリジェントなカスタマーサービスエージェントのプロンプトには、通常、以下が含まれます。

  • システムペルソナ:非常に安定しており、めったに変更されません。

  • 外部ナレッジ:半安定的。このコンテンツはナレッジベースまたはツールクエリから取得され、連続した会話中には変更されない場合があります。

  • 会話履歴:動的に増加します。

  • 現在の質問:毎回異なります。

プロンプト全体が単一のユニットとしてキャッシュされると、外部ナレッジの更新などのわずかな変更でもキャッシュミスが発生する可能性があります。

リクエストに最大 4 つのキャッシュマーカーを設定して、プロンプトの異なる部分に対して個別のキャッシュブロックを作成できます。これにより、ヒット率が向上し、詳細な制御が可能になります。

課金

明示的キャッシュは、入力トークンの課金方法にのみ影響します。ルールは次のとおりです。

  • キャッシュ作成:新しく作成されたキャッシュコンテンツは、標準入力価格の 125% で課金されます。新しいリクエストのキャッシュコンテンツに既存のキャッシュがプレフィックスとして含まれている場合、新しい部分のみが課金されます。これは、新しいキャッシュトークン数から既存のキャッシュトークン数を引いて計算されます。

    たとえば、1,200 トークンの既存のキャッシュ A があり、新しいリクエストで 1,500 トークンのコンテンツ AB をキャッシュする必要がある場合、最初の 1,200 トークンはキャッシュヒットとして課金され (標準価格の 10%)、新しい 300 トークンはキャッシュ作成のために課金されます (標準価格の 125%)。

    キャッシュ作成に使用されたトークン数は、cache_creation_input_tokens パラメーターで確認できます。
  • キャッシュヒット:標準入力価格の 10% で課金されます。

    キャッシュされたトークン数は、cached_tokens パラメーターで確認できます。
  • その他のトークン:ヒットせず、キャッシュが作成されなかったトークンは、標準価格で課金されます。

キャッシュ可能なコンテンツ

messages 配列内の次のメッセージタイプのみがキャッシュマーカーの追加をサポートしています。

  • システムメッセージ

  • ユーザーメッセージ

  • アシスタントメッセージ

  • ツールメッセージ (ツール実行後の結果)

リクエストに tools パラメーターが含まれている場合、messages にキャッシュマーカーを追加すると、ツールの説明情報もキャッシュされます。

システムメッセージを例にとると、content フィールドを配列形式に変更し、cache_control フィールドを追加できます。

{
  "role": "system",
  "content": [
    {
      "type": "text",
      "text": "<Specified Prompt>",
      "cache_control": {
        "type": "ephemeral"
      }
    }
  ]
}

この構造は、messages 配列内の他のメッセージタイプにも適用されます。

制限事項

  • 最小プロンプト長は 1,024 トークンです。

  • キャッシュは後方プレフィックスマッチング戦略を使用します。システムは最後の 20 個のコンテンツブロックを自動的にチェックします。一致させるコンテンツが cache_control マーカーを含むメッセージから 20 個以上のコンテンツブロックで区切られている場合、キャッシュヒットは発生しません。

  • typeephemeral に設定することのみがサポートされています。これにより、5 分間の有効期間が提供されます。

  • 1 つのリクエストに最大 4 つのキャッシュマーカーを追加できます。

    キャッシュマーカーの数が 4 を超える場合、最後の 4 つのみが有効になります。

使用例

長文に対する異なる質問

from openai import OpenAI
import os

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # 以下は国際 (シンガポール) の base_url です。中国本土 (北京) の base_url は https://dashscope.aliyuncs.com/compatible-mode/v1 です
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

# シミュレートされたコードリポジトリのコンテンツ
long_text_content = "<Your Code Here>" * 400

# リクエストを行う関数
def get_completion(user_input):
    messages = [
        {
            "role": "system",
            "content": [
                {
                    "type": "text",
                    "text": long_text_content,
                    # ここに cache_control マーカーを配置して、プロンプトの先頭からこのコンテンツの末尾 (シミュレートされたコードリポジトリのコンテンツ) までのキャッシュを作成します。
                    "cache_control": {"type": "ephemeral"},
                }
            ],
        },
        {
            "role": "user",
            "content": user_input,
        },
    ]
    completion = client.chat.completions.create(
        # 明示的キャッシュをサポートするモデルを選択します
        model="qwen3-coder-plus",
        messages=messages,
    )
    return completion

# 最初のリクエスト
first_completion = get_completion("このコードの内容は何ですか?")
created_cache_tokens = first_completion.usage.prompt_tokens_details.cache_creation_input_tokens
print(f"最初のリクエストで作成されたキャッシュトークン: {created_cache_tokens}")
hit_cached_tokens = first_completion.usage.prompt_tokens_details.cached_tokens
print(f"最初のリクエストでヒットしたキャッシュトークン: {hit_cached_tokens}")
print(f"最初のリクエストでヒットも作成もされなかったトークン: {first_completion.usage.prompt_tokens-created_cache_tokens-hit_cached_tokens}")
print("=" * 20)
# 2 番目のリクエスト、コード内容は同じで、質問のみ変更
second_completion = get_completion("このコードの最適化の可能性はありますか?")
created_cache_tokens = second_completion.usage.prompt_tokens_details.cache_creation_input_tokens
print(f"2 番目のリクエストで作成されたキャッシュトークン: {created_cache_tokens}")
hit_cached_tokens = second_completion.usage.prompt_tokens_details.cached_tokens
print(f"2 番目のリクエストでヒットしたキャッシュトークン: {hit_cached_tokens}")
print(f"2 番目のリクエストでヒットも作成もされなかったトークン: {second_completion.usage.prompt_tokens-created_cache_tokens-hit_cached_tokens}")

この例では、コードリポジトリのコンテンツをプレフィックスとしてキャッシュします。後続のリクエストでは、このリポジトリについて異なる質問をします。

最初のリクエストで作成されたキャッシュトークン: 1605
最初のリクエストでヒットしたキャッシュトークン: 0
最初のリクエストでヒットも作成もされなかったトークン: 13
====================
2 番目のリクエストで作成されたキャッシュトークン: 0
2 番目のリクエストでヒットしたキャッシュトークン: 1605
2 番目のリクエストでヒットも作成もされなかったトークン: 15
モデルのパフォーマンスを確保するため、システムは少数の内部トークンを追加します。これらのトークンは標準の入力価格で課金されます。詳細については、「よくある質問」をご参照ください。

連続的なマルチターン対話

典型的なマルチターンチャットシナリオでは、各リクエストの messages 配列の最後のコンテンツにキャッシュマーカーを追加できます。2 ターン目から、各リクエストは前のターンで作成されたキャッシュブロックにヒットしてリフレッシュし、新しいキャッシュブロックも作成します。

from openai import OpenAI
import os
  
client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # 以下は国際 (シンガポール) の base_url です。中国本土 (北京) の base_url は https://dashscope.aliyuncs.com/compatible-mode/v1 です
    base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

system_prompt = "あなたは機知に富んだ人物です。" * 400
messages = [{"role": "system", "content": system_prompt}]

def get_completion(messages):
    completion = client.chat.completions.create(
        model="qwen3-coder-plus",
        messages=messages,
    )
    return completion

while True:
    user_input = input("入力してください:")
    messages.append({"role": "user", "content": [{"type": "text", "text": user_input, "cache_control": {"type": "ephemeral"}}]})
    completion = get_completion(messages)
    print(f"[AI の応答] {completion.choices[0].message.content}")
    messages.append(completion.choices[0].message)
    created_cache_tokens = completion.usage.prompt_tokens_details.cache_creation_input_tokens
    hit_cached_tokens = completion.usage.prompt_tokens_details.cached_tokens
    uncached_tokens = completion.usage.prompt_tokens - created_cache_tokens - hit_cached_tokens
    print(f"[キャッシュ情報] 作成されたキャッシュトークン: {created_cache_tokens}")
    print(f"[キャッシュ情報] ヒットしたキャッシュトークン: {hit_cached_tokens}")
    print(f"[キャッシュ情報] ヒットも作成もされなかったトークン: {uncached_tokens}")

上記のコードを実行し、質問を入力してモデルと対話できます。各質問は、前のターンで作成されたキャッシュブロックにヒットします。

よくある質問

Q:暗黙的キャッシュを無効にするにはどうすればよいですか?

A:できません。暗黙的キャッシュは、応答品質に影響を与えないため、適用可能なすべてのリクエストで有効になっています。キャッシュヒットが発生すると、コストが削減され、応答速度が向上します。

Q:明示的キャッシュを作成したのに、なぜキャッシュミスが発生したのですか?

A:考えられる理由は次のとおりです。

  • 5 分以内にキャッシュがヒットしませんでした。システムは有効期間が切れた後、キャッシュブロックをパージします。

  • 最後の content が既存のキャッシュブロックから 20 個以上の content ブロックで区切られている場合、キャッシュヒットは発生しません。新しいキャッシュブロックを作成することをお勧めします。

Q:明示的キャッシュにヒットすると、その有効期間はリセットされますか?

A:はい、リセットされます。各ヒットにより、キャッシュブロックの有効期間が 5 分にリセットされます。

Q:明示的キャッシュは異なるアカウント間で共有されますか?

A:いいえ、共有されません。暗黙的キャッシュと明示的キャッシュの両方のデータは、アカウントレベルで分離されています。

Q:同じアカウントが異なるモデルを使用する場合、それらの明示的キャッシュは共有されますか?

A:いいえ、共有されません。キャッシュデータはモデル間で分離されています。

Q:なぜ usage.input_tokenscache_creation_input_tokenscached_tokens の合計と等しくないのですか?

A:モデルのパフォーマンスを確保するため、バックエンドサービスはユーザーが提供したプロンプトの後に少数のトークン (通常は 10 未満) を追加します。これらのトークンは cache_control マーカーの後に配置されるため、キャッシュの作成または読み取り操作にはカウントされませんが、合計の input_tokens には含まれます。