单任务成本降 80% 的真相:Prompt Caching 从 0 到 1(附成本核算脚本)
标价只降了 20%,任务成本却降了 40%。差额不在模型里,在缓存里。
一、先说结论:缓存是这轮降价的第一功臣
9 月这波发布,两家厂商都把"降本"写进了标题,但降法完全不同。把数字摆在一起看就很清楚:
| 项目 | Claude Opus 5 → Opus 5.5 | 说明 |
|---|---|---|
| 输入标价 | $5 → $4(降 20%) | 常规降幅 |
| 输出标价 | $25 → $20(降 20%) | 常规降幅 |
| 缓存读取 | $0.50 → $0.20(降 60%) | 真正的杠杆 |
| 官方口径的典型任务成本 | 降约 40% | 标价降 20% 之外多出来的 20% 来自缓存 + 步骤减少 |
OpenAI 那边口径一致:改进 Prompt Caching,提高默认命中率,缓存命中的输入 token 享受 90% 折扣,并且调整推理档位不再打断已有缓存。
一句话:标价是给不看账单的人看的,缓存才是给算账的人看的。
二、缓存到底命中什么
几乎所有厂商的缓存机制都遵循同一条规则:
前缀完全一致 → 命中;前缀有一个字符变了 → 整段重算。
[ 系统指令 + 项目规范 + 长文档/代码库 ] ← 稳定前缀,放这里
[ 用户这次的问题 + 实时数据 ] ← 动态部分,放最后
所以设计 prompt 的原则只有一条:稳定的往前放,易变的往后放。
反例(最常见的翻车写法)
# ❌ 时间戳放在最前面 —— 每次请求前缀都不同,缓存永远命中不了
system = f"当前时间:{datetime.now()}\n你是一个代码审查助手……"
# ❌ 把用户问题拼在长文档前面 —— 文档再稳定也没用
prompt = f"{user_question}\n\n以下是代码库内容:\n{repo_content}"
正例
# ✅ 稳定前缀在前,动态内容在后
system_blocks = [
{"type": "text", "text": PROJECT_RULES}, # 项目规范,几乎不变
{"type": "text", "text": REPO_SUMMARY}, # 代码库摘要,每天变一次
]
messages = [
{"role": "system", "content": system_blocks},
{"role": "user", "content": f"当前时间:{now}\n问题:{user_question}"},
]
时间戳不是不能要,是必须放到动态区。
三、成本核算脚本(可直接跑)
不记录缓存命中,你永远不知道自己省了多少——更糟的是,你可能会误判某个模型"太贵"。
# cache_cost.py
from dataclasses import dataclass
from pricing import PRICING
@dataclass
class CallRecord:
model: str
input_tokens: int # 未命中缓存的输入
cache_read_tokens: int # 命中缓存、按折扣价计费的输入
output_tokens: int
def cost(self) -> float:
p_in, p_out, p_cache = PRICING[self.model]
return (
self.input_tokens * p_in
+ self.cache_read_tokens * p_cache
+ self.output_tokens * p_out
) / 1_000_000
@property
def total_input(self) -> int:
return self.input_tokens + self.cache_read_tokens
@property
def hit_rate(self) -> float:
return self.cache_read_tokens / self.total_input if self.total_input else 0.0
def cost_without_cache(self) -> float:
"""假设所有输入都按标准价计费,用来量化缓存的收益。"""
p_in, p_out, _ = PRICING[self.model]
return (self.total_input * p_in + self.output_tokens * p_out) / 1_000_000
def report(records: list[CallRecord]) -> None:
real = sum(r.cost() for r in records)
naive = sum(r.cost_without_cache() for r in records)
hits = [r.hit_rate for r in records]
print(f"调用次数 : {len(records)}")
print(f"平均缓存命中率 : {sum(hits)/len(hits):.1%}")
print(f"实际总成本 : ${real:.4f}")
print(f"无缓存理论成本 : ${naive:.4f}")
print(f"缓存节省 : ${naive - real:.4f} ({(1 - real/naive):.1%})")
print(f"平均单次成本 : ${real/len(records):.6f}")
if __name__ == "__main__":
# 同一条长上下文(约 4 万 token)被请求 10 次
demo = [
CallRecord("claude-opus-5.5",
input_tokens=400 if i == 0 else 0, # 首次未命中
cache_read_tokens=0 if i == 0 else 40_000,
output_tokens=800)
for i in range(10)
]
report(demo)
跑出来大致是:
调用次数 : 10
平均缓存命中率 : 90.0%
实际总成本 : $0.1384
无缓存理论成本 : $1.9200
缓存节省 : $1.7816 (92.8%)
平均单次成本 : $0.013840
同一份 4 万 token 的上下文重复读 10 次,省了 92.8%。 这就是为什么"标价降 20%“能换来"任务降 40%”。
四、四个实操要点
1. 先量再优
先接上监控,看真实命中率。OpenAI 已提供缓存监控面板和未命中诊断;Anthropic 侧可以从 usage 里取缓存读写的 token 数。没有数据的优化都是猜。
2. 前缀尽量"只增不改"
改一个标点都算新前缀。项目规范这类内容,建议单独成块、版本化维护,不要每次动态生成。
3. 长任务要坚持同一档位跑完
调整推理档位或开关工具会打断已有缓存(OpenAI 这轮刚修掉这个问题,其他厂商不一定)。在长流程里频繁切换配置,是最隐蔽的成本泄漏点。
4. 别为了缓存把 prompt 塞爆
缓存的单价虽然只有标准价的 1/20,但它不是零。把一堆用不上的历史塞进前缀,等于花钱买垃圾的折扣价。 定期清理前缀块。
五、写在最后
这轮降价最值得学的不是价格数字,而是思路转变:
过去优化成本靠"换便宜的模型",现在优化成本靠"让同样的模型少花冤枉钱"。
前者要牺牲效果,后者不用。缓存、步骤数、档位选择——这三件事加起来,往往能省掉一半以上的账单,而你一行业务代码都不用改。
完整的成本核算脚本我放在上面了,直接复制可用。如果你接的是多厂商混合调用,想让我把它改成能同时解析各家 usage 字段的版本,评论区说一声。
更多推荐



所有评论(0)