标价只降了 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 字段的版本,评论区说一声。

Logo

一站式 AI 云服务平台

更多推荐