📌 本文重點
- 只為「新算的 token」付錢才省成本
- 穩定前綴(system、tools、長期記憶)要被快取
- cache key 必須含模型、租戶與權限版本
- 長對話可降 30–60% 推理成本
長對話裡真正燒錢的不是「整個 context 有幾個 token」,而是每一輪新算了多少 token。Claude 的 prompt caching 就是把那些每輪都重複出現的前綴(system prompt、tool schema、長期記憶等)標記成可重用的計算結果,只為「新出現的部分」付錢。實務上,你可以在 RAG、Agent、Code Assistant 裡大幅壓低長會話成本,同時讓模型保持完整上下文。
💡 關鍵: 掌握「每輪新算 token」才是控成本的核心,而不是只看整個 context 長度。
重點說明:為什麼「重用前綴」省錢
- 計費與計算模型:只對「新算過的 token」付錢
現代 LLM(包含 Claude)推理時,會對輸入序列做一次注意力與前向計算。若供應商支援 前綴快取,就能把已算過的 embedding / KV cache 重新使用,對於已快取的部分:
- 計費:只收少量或不收額外費用(依供應商設計)
-
計算:避免重跑 attention / matmul
-
快取什麼:可穩定重複的 prefix
典型可快取區塊:
- System prompt:角色、風格、平台規範
- Tool schema:function calling / tools JSON schema
- 長期記憶 / 專案上下文:repo 結構、domain handbook、RAG 長期摘要
-
長期對話前半段:幾十輪以上的歷史,只要不會被頻繁重寫
-
API 層設計:cache key + 失效策略是核心
要讓 prompt caching 在多模型、多供應商環境可維護,必須明確管理:
- cache key 組成:模型版本 + system prompt hash + tools hash + tenant + 權限版本
- 失效策略:
- 模型升級(
model_version變更) - 工具清單或 schema 改動
- tenant 權限/角色變更
- 模型升級(
💡 關鍵: 把模型 ID、租戶與權限版本都放進
cacheKey,才能避免快取錯配與資料洩漏。
實作範例:切分前綴與多層快取
1. Prompt 結構切分策略
我們先定義一個標準化的 prompt 結構:
// TypeScript / Node pseudo-code
interface PromptSegments {
system: string; // 系統指令
toolsSchema: object[]; // 工具定義 (OpenAI / Claude tools 格式)
longTermContext: string; // 長期記憶 / 專案說明 / RAG summary
shortHistory: string; // 近期對話 (例如最後 10 輪)
userInput: string; // 本輪使用者輸入
}
function buildMessages(segments: PromptSegments) {
return [
{ role: "system", content: segments.system },
{ role: "system", content: `TOOLS_SCHEMA:\n${JSON.stringify(segments.toolsSchema)}` },
{ role: "system", content: `LONG_TERM_CONTEXT:\n${segments.longTermContext}` },
{ role: "assistant", content: segments.shortHistory },
{ role: "user", content: segments.userInput },
];
}
前 3 段(system / toolsSchema / longTermContext)就是要被 prompt caching 鎖定的 prefix;shortHistory 則保留可替換的空間,避免整個對話歷史變成難以管理的快取。
2. Node 版 cache middleware(多模型、多供應商共用)
假設你有一個抽象的 LLMClient,在呼叫前注入 cache metadata:
// Node.js pseudo-code
import crypto from "crypto";
interface CacheMetadata {
cacheKey: string;
cacheTtlSec: number;
}
function buildCacheKey({
provider,
model,
system,
toolsSchema,
tenantId,
permissionsVersion,
}: {
provider: string;
model: string;
system: string;
toolsSchema: object[];
tenantId: string;
permissionsVersion: string;
}): string {
const hashInput = JSON.stringify({ system, toolsSchema, tenantId, permissionsVersion });
const hash = crypto.createHash("sha256").update(hashInput).digest("hex");
return `${provider}:${model}:${hash}`; // 核心:模型 + 前綴內容 + 租戶/權限
}
async function withPromptCache(llmClient, segments: PromptSegments, ctx) {
const cacheKey = buildCacheKey({
provider: ctx.provider, // "anthropic" / "openai" / "azure-openai" ...
model: ctx.model, // 例如 "claude-3.7-sonnet"
system: segments.system,
toolsSchema: segments.toolsSchema,
tenantId: ctx.tenantId,
permissionsVersion: ctx.permissionsVersion,
});
const messages = buildMessages(segments);
const response = await llmClient.chat({
messages,
// 自定義或供應商原生字段
metadata: {
// 自家 caching layer 用
promptCache: {
cacheKey,
segmentsCached: ["system", "toolsSchema", "longTermContext"],
ttlSec: 3600, // 長期 context 一小時有效
},
},
});
return response;
}
在多供應商環境下的重點:
cacheKey必須在抽象層就固定格式,不要依賴各家私有的 cache token。- 將「哪些 segment 可快取」「TTL 設定」寫在自家
metadata.promptCache,再由底層 adapter 映射到各家 API(例如 Anthropic 的 prompt caching 參數、OpenAI 未來的前綴重用機制等)。
3. Python 版:RAG + Agent + Code Assistant 共用策略
示範一個 Python middleware,處理三種場景:
# Python pseudo-code
import hashlib
from typing import List, Dict, Any
class PromptCacheMiddleware:
def __init__(self, backend):
self.backend = backend # Redis / in-memory / provider-native
def _hash(self, payload: Dict[str, Any]) -> str:
raw = repr(payload).encode("utf-8")
return hashlib.sha256(raw).hexdigest()
def build_cache_key(self, provider: str, model: str, tenant: str,
system: str, tools: List[Dict[str, Any]],
perm_version: str) -> str:
hash_part = self._hash({"system": system, "tools": tools,
"tenant": tenant, "perm": perm_version})
return f"{provider}:{model}:{hash_part}"
def call(self, llm_client, segments, ctx, scenario: str):
# scenario: "rag" | "agent" | "code"
cache_key = self.build_cache_key(
ctx["provider"], ctx["model"], ctx["tenant"],
segments.system, segments.tools_schema,
ctx["permissions_version"],
)
ttl = 3600 if scenario in ("rag", "code") else 600
messages = build_messages(segments)
# 自家 cache backend,也可以是 provider 的 prompt cache
cached_prefix = self.backend.get(cache_key)
if cached_prefix:
# 若使用 provider-native KV cache,可在這裡直接標記使用
pass
resp = llm_client.chat(
messages=messages,
metadata={
"prompt_cache": {
"cache_key": cache_key,
"ttl_sec": ttl,
"segments_cached": ["system", "toolsSchema", "longTermContext"],
}
},
)
self.backend.set(cache_key, "USED", ttl)
return resp
這樣你就可以在同一層 middleware 裡:
- 為 RAG 保留穩定的 long-term summary 前綴
- 為 Agent 快取工具清單與角色設定
- 為 Code Assistant 快取 repo / 專案上下文
建議與注意事項:幾個常見坑
-
工具清單動態變化 → cache 大量失效
-
問題:Agent 工具列表若每次請求都依據情境動態調整,
toolsSchemahash 會頻繁變動,導致cacheKey不可重用。 -
建議:
- 將工具分成「核心必備工具」與「情境工具」,只對核心工具段做 caching。
- 使用 mid-conversation tool changes(例如 Claude Opus 5 的 beta 功能)讓工具權限在對話中調整,而不觸發整個 prefix 重算。
-
模型升級 → 快取錯配
-
問題:模型從
claude-3.6換到claude-3.7,如果cacheKey沒把 model / version 納入,就會拿舊模型算出的 prefix 餵給新模型,出現行為差異。 -
建議:
- 必須把 model id / version 放在
cacheKey開頭(前面範例已包含)。 - 建多供應商兼容測試(對齊「同一前綴 + 不同模型」在工具呼叫、輸出格式上的差異),參考 LLM Provider Quirks 文章的思路。
- 必須把 model id / version 放在
-
多租戶 SaaS:tenant 隔離
-
問題:如果
cacheKey沒包含 tenant / 權限版本,在多租戶環境下可能把 A 客戶的長期記憶前綴給 B 客戶用,造成嚴重資料洩漏。 -
建議:
tenantId與permissionsVersion必須是cacheKey的一部分。- 權限變更(role / scope 調整)時,明確 bump
permissionsVersion,強制快取失效。 - 對於敏感工具(例如能查詢客戶資料庫的 tool),可直接標記為 不參與 prompt caching,或使用獨立 cache 空間。
-
避免 cache 污染:RAG + Agent + Code Assistant
-
污染範例:
- RAG summary 中意外混入使用者敏感資料,然後被快取成長期前綴。
- Agent 工具列表在某次測試中加入 dev-only 工具,被 cache,之後所有 production 對話都看得到。
- 建議:
- 在產生長期 context / tools schema 前,做一次 安全與敏感資料清洗。
- 長期前綴內容最好由後端控制(例如固定的 RAG summary service),而不是讓使用者直接寫入。
- 為 dev / staging / prod 分別建立獨立 cache namespace。
實際好處:你專案會得到什麼
引入 Claude Code Prompt Caching 這類前綴快取機制後,對典型專案有幾個直接好處:
- 長對話成本顯著下降:像 code assistant 或長期顧問型 Agent,一整天會話的 token 數看起來驚人,但前 70–90% 的前綴計算可以重用。實務上常見是 30–60% 推理成本下降。
- 維持完整上下文又不必瘋狂裁剪:有快取後,你可以保留更多長期記憶與工具說明,而不是每次都為了成本把 context 切到只剩最近幾輪。
- 跨供應商、多模型快速試錯:抽象層設計好
cacheKey與前綴切分後,就能在不同模型間切換時,維持一致的成本控制策略,不怕「換模型就打回重算」。
💡 關鍵: 只要一開始就設計好前綴切分與快取策略,prompt caching 就能變成穩定的基礎設施,而不是事後補救。
只要在專案一開始就規劃好 prompt 結構、cache key、失效策略,你就能把 prompt caching 當成基礎設施來使用,而不是事後補上去的微調。
🚀 你現在可以做的事
- 在現有專案裡明確切分
system、toolsSchema、longTermContext等前綴區塊- 為你的 LLM 抽象層加入統一的
cacheKey與metadata.promptCache設計- 在開發環境先測試 RAG、Agent、Code Assistant 三種場景的快取策略與失效機制










