登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

Anthropic Prompt Caching 如何判断命中:cache_control 边界与成本核对

来源:17golang原创

时间:2026-08-28 05:49:57 176浏览 收藏

线上客服机器人把同一份产品手册反复发给 Claude,输入 token 很快就成了主要成本。Prompt Caching 能复用这段稳定前缀,但只写上 cache_control 并不等于每次都命中:断点放在会变化的用户问题上,结果仍可能是一次次缓存写入。真正可靠的做法是把断点放在“最后一个稳定区块”,再读取响应里的 usage 字段验收。

判断一次请求是否命中,先看 cache_read_input_tokens 是否大于 0;如果只有 cache_creation_input_tokens,这次是写缓存,不是读缓存。

要点速览
  • tools → system → messages 是缓存前缀的形成顺序,断点前的内容共同决定缓存键。
  • 会变化的时间戳、用户问题不要放在缓存断点里,断点应落在稳定前缀的最后一个区块。
  • 默认 TTL 是 5 分钟;需要更长复用窗口时可使用 1 小时 TTL,但写入价格更高。
  • 验收时同时记录 cache_creation_input_tokenscache_read_input_tokens 和普通 input_tokens,不要只凭延迟猜命中。

先分清“写入缓存”和“读取缓存”

Anthropic 的缓存命中是前缀级别的。系统会把 toolssystemmessages 按顺序拼成可检查的前缀,在指定的 cache_control 断点处尝试复用。第一次请求通常没有可读的旧条目,它会产生缓存写入;下一次请求只有在前缀匹配时才会出现缓存读取。

因此,业务日志里最好把三类 token 分开。cache_creation_input_tokens 表示本次写入了多少输入 token,cache_read_input_tokens 表示从已有缓存读取了多少输入 token,而 input_tokens 仍包含没有从缓存读取的输入部分。

把 cache_control 放在稳定前缀末尾

下面的示例把产品手册放在 system 内容里,把每次变化的客户问题放在 user 消息里。断点落在产品手册末尾,后面的问题不会改变缓存前缀。示例中的 cache_controlcache_read_input_tokenscache_creation_input_tokens 都是需要在真实响应中核对的字段。

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="YOUR_CLAUDE_MODEL",
    max_tokens=512,
    system=[
        {
            "type": "text",
            "text": "产品手册:退款条件、发货范围、售后联系方式……",
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[
        {"role": "user", "content": "客户问题:订单已经发出,还能修改收货地址吗?"}
    ]
)

print(response.usage.model_dump())

这里有一个容易忽略的边界:如果把当前时间、请求编号或客户问题也放进同一个断点之前,前缀就会随请求变化。缓存系统不会替你从变化内容后面“猜”出稳定部分;它只会在已写入的断点附近寻找可复用条目。

Anthropic Prompt Caching 中稳定前缀经过 cache_control 形成可复用缓存断点

为什么同一份手册仍可能没有命中

缓存不是任意短文本的免费标记。当前文档为不同 Claude 模型定义了最小可缓存 token 数,短于对应门槛时,请求可以正常返回,但两个缓存 usage 字段都可能为 0。工程上应先确认稳定前缀足够长,再观察第二次请求是否出现 cache_read_input_tokens

用 usage 字段做一次可重复验收

不要用单次响应的耗时判断命中,因为网络、排队和输出长度都会影响延迟。可以为每次请求保留下面这组最小记录:

字段验收含义排查方向
cache_creation_input_tokens本次写入缓存的输入量首次请求或断点前缀发生变化
cache_read_input_tokens本次从缓存读取的输入量大于 0 才能确认发生读取
input_tokens未由缓存读取的输入量观察动态问题和未缓存后缀
usage = response.usage
audit = {
    "input_tokens": usage.input_tokens,
    "cache_creation_input_tokens": usage.cache_creation_input_tokens,
    "cache_read_input_tokens": usage.cache_read_input_tokens,
}

if audit["cache_read_input_tokens"] > 0:
    print("缓存命中", audit)
elif audit["cache_creation_input_tokens"] > 0:
    print("缓存写入", audit)
else:
    print("未发生缓存读写,检查前缀长度与断点", audit)

把这段判断放进请求指标或结构化日志后,缓存效果就能在压测和线上请求中复核。特别是灰度发布时,不要只看平均 token 成本,要按缓存读、写、未缓存三类拆开。

Anthropic Messages API 使用 cache_creation_input_tokens 和 cache_read_input_tokens 核对缓存读写

TTL、20 个区块回看与成本边界

默认缓存生命周期是 5 分钟,缓存条目在被使用时会刷新;生命周期从写入或读取请求开始计算,流式输出时间也会占用这段窗口。如果业务的两次请求间隔明显超过 5 分钟,可以显式指定 {"type": "ephemeral", "ttl": "1h"},但要把更高的缓存写入价格纳入预算。

显式断点最多有 4 个。每个断点向前查找时最多回看 20 个区块,而且只能找到过去真正写入过的断点。如果对话不断增长,旧断点离当前位置超过这个窗口,即使内容没有变也可能读不到;这时要提前在稳定位置设置另一个断点。

成本核对时记住三个比例:5 分钟缓存写入按基础输入价格的 1.25 倍计,1 小时写入按 2 倍计,缓存读取按基础输入价格的 0.1 倍计。具体美元单价会随模型和平台变化,预算表应以 Anthropic 当前 pricing 页面为准。

常见故障:为什么断点写对了还没有读

  • 断点前内容变了:工具定义、system 指令或稳定手册有一处改动,都可能使后续缓存失效。先对比实际发送的请求,不要只对比业务输入。
  • 缓存前缀太短:未达到当前模型的最小 token 门槛时,两个缓存字段可能同时为 0。
  • 只发了一次请求:第一次请求只能证明写入是否发生,不能证明后续读取。
  • 并发得太早:首个请求的缓存条目要等响应开始后才可用;需要验证读取时,先让写入请求开始,再发送下一次请求。

相关问题

cache_control 放在 user 问题上可以吗?

语法上可以,但如果 user 问题每次不同,就会让断点前缀不断变化。只有问题本身是稳定复用内容时才适合放在那里。

cache_read_input_tokens 为 0 就一定是配置错误吗?

不一定。还要检查请求是否首次发送、稳定前缀是否达到模型门槛、TTL 是否已过期,以及断点前的 tools、system、messages 是否发生变化。

多个 cache_control 会不会直接增加费用?

断点本身不单独计费,费用取决于实际发生的缓存写入、缓存读取和普通输入。多个断点的价值是控制不同稳定区块的复用边界。

应该用缓存命中替代业务侧缓存吗?

不应该。Prompt Caching 解决的是重复输入前缀的处理成本与延迟,业务答案的正确性、权限和失效策略仍应由应用自己的缓存层负责。

发布前检查清单

  1. 把静态工具定义、system 指令和产品手册放在动态问题之前。
  2. cache_control 放在最后一个稳定区块,而不是时间戳或用户问题之后。
  3. 连续发起至少两次可比请求,分别保存三个 usage 字段。
  4. cache_read_input_tokens > 0 确认读取,用写入量和 TTL 解释成本变化。
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>