📝

【Azure】プロンプトキャッシュについて学ぶ

に公開

執筆日

2026/1/7

学ぶこと

プロンプトキャッシュについて一から学んでいきます。
初心者向けでわかりやすく書いていこうかなと。

使うリソース

  • Microsoft Foundry
    ※Azureでは、Azure OpenAIモデルGPT-4o以降でサポートされている。

プロンプトキャッシュとは?

同じようなプロンプト(入力テキスト)の 先頭部分(prefix)を繰り返し使う際に、その処理結果を一時的に記憶(キャッシュ)して再利用する仕組みです。
これにより、同じ先頭部分を何度も計算し直す必要がなくなり、

  • レイテンシ(応答速度)が大幅に改善
  • 入力トークンコスト(APIの料金)が削減
    といったパフォーマンス向上・コスト削減が図れます

https://platform.openai.com/docs/guides/prompt-caching

どうやって有効にするの?

次の2つを満たす必要がある※Azure OpenAI/OpenAIの場合。

  • 長さが1,024トークン以上
  • プロンプトの先頭の1,024トークンが同一

https://learn.microsoft.com/ja-jp/azure/ai-foundry/openai/how-to/prompt-caching?view=foundry-classic#getting-started

もう少し深堀

ハッシュは通常、最初の 256 個のトークンを使用しますが、正確な長さはモデルによって異なります。

項目 役割
256トークン キャッシュを探すためのキー(索引)
1,024トークン キャッシュが有効かどうかの本体条件
  1. 最初の約256トークンで「どのキャッシュ候補を見るか」を決める
  2. その後先頭1,024トークンが完全一致しているかを厳密に確認
  3. 一致していればプロンプトキャッシュ!

キャッシュされるもの

キャッシュ対象 説明 キャッシュを有効にする条件・注意点
メッセージ system / developer / user / assistantを含む完全なメッセージ配列 内容・順序・文言・改行・空白まで完全一致が必要
画像 ユーザーメッセージに含まれる画像(URL / base64) 画像データだけでなく、解像度・形式などの 詳細パラメータも同一であること
ツールの使用 メッセージ配列 + ツール定義(functions / tools) ツール定義の内容・順序が変わると別キャッシュ扱い
構造化出力 JSON Schema などの構造化出力スキーマ スキーマはsystem メッセージの先頭に追加され、同一であればキャッシュ対象

TTL(有効期間)

非アクティブ後5-10分以内クリア、最終使用から1時間以内削除される。

検証

main.py
from openai import AzureOpenAI

endpoint = <Endpoint>
key  = <Key>
model_name = <"モデル名">
api_version = "2024-12-01-preview"

client = AzureOpenAI(
    api_version=api_version,
    azure_endpoint=endpoint,
    api_key=key,
)

text = """
あなたは親切で正確なアシスタントです。
- 回答は日本語
- 不明なことは推測しない
- 箇条書きで簡潔に
"""

long_prefix = text + ("\n【固定コンテキスト】この段落は毎回同一です。" * 1000)

BASE_MESSAGES = [
    {"role": "system", "content": long_prefix},
]
def chat_call(user_text: str, prompt_cache_key: str | None = None):
    messages = BASE_MESSAGES + [{"role": "user", "content": user_text}]

    extra_body = {"prompt_cache_key": prompt_cache_key} if prompt_cache_key else None
    resp = client.chat.completions.create(
        model=model_name,   
        messages=messages,
        extra_body=extra_body,
    )
    usage = resp.usage.model_dump() if hasattr(resp.usage, "model_dump") else resp.usage
    cached_tokens = (usage.get("prompt_tokens_details") or {}).get("cached_tokens")

    return resp, cached_tokens, usage

# 1回目
resp1, cached1, usage1 = chat_call(
    "プロンプトキャッシュの仕組みを100字で説明して。",
    prompt_cache_key="demo-cache"
)
print("1st cached_tokens:", cached1)
print("1st usage:", usage1)

# 2回目
resp2, cached2, usage2 = chat_call(
    "同じ前提で、メリットを3つ箇条書きで。",
    prompt_cache_key="demo-cache"
)
print("2nd cached_tokens:", cached2)
print("2nd usage:", usage2)

キャッシュされている!2回目はすぐにresponseが来た。

1st cached_tokens: 0
1st usage: {'completion_tokens': 2333, 'prompt_tokens': 17070, 'total_tokens': 19403, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 2240, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}
2nd cached_tokens: 16256
2nd usage: {'completion_tokens': 455, 'prompt_tokens': 17072, 'total_tokens': 17527, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 384, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 16256}}
ヘッドウォータース

Discussion