1024 token 缓存触发条件
缓存不生效的常见原因
按 Anthropic Prompt Caching Docs(访问于 2026-05-19),触发缓存有两条硬规则:
- 最小块:被 cache_control 标记的累积 token ≥ 1024(Sonnet / Opus / Haiku 4.5 起统一)
- 字节级一致:同一段 block 文本一字、一空格、一换行符不变
Solopreneur 常见踩坑(实测见过的真实案例):
| 踩坑模式 | 现象 |
|---|---|
| system 含「今天是 2026-05-19」 | 每天换文本,cache 每天失效 |
| system 含用户名 / 租户名 | 每个用户独立 cache 块,无法共享 |
| 用 f-string 拼 system,变量在中间 | 每次插值不同,字节哈希变 |
| system 只有 600 token | 不到 1024 条件,cache_control 被忽略 |
| 多 block 但 cache_control 只放在第一个 | 后续 block 不缓存 |
工程化方案:三段分层
┌─────────────────────────────────────┐
│ 第 1 层:tools 定义(2-5K token) │ ← cache_control
├─────────────────────────────────────┤
│ 第 2 层:品牌 system prompt(2-4K) │ ← cache_control
├─────────────────────────────────────┤
│ 第 3 层:用户上下文 / 文档(可变) │ ← 不加 cache_control
└─────────────────────────────────────┘
↓
messages: [{role: user, content: 今日问题}]
按 Anthropic Cookbook(访问于 2026-05-19)推荐写法,cache_control 一次请求最多 4 个,通常用 2-3 个分层。
代码示例:三层缓存
import anthropic
client = anthropic.Anthropic()
TOOLS = [...] # tool 定义,3-5K token
BRAND_SYSTEM = """你是 [品牌名] 的 AI 助手,回复语气友好专业。
产品功能 1:...
产品功能 2:...
常见问答:Q1...A1...
常用术语:...
回复格式:...
""" # 2-4K token,绝对不变
# 调用
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1500,
tools=TOOLS, # 自动可缓存(SDK 内部支持)
system=[
{
"type": "text",
"text": BRAND_SYSTEM,
"cache_control": {"type": "ephemeral"},
}
],
messages=[
{
"role": "user",
"content": f"当前用户:{username}\n租户:{tenant}\n时间:{now}\n\n问题:{q}",
}
],
)
注意:把 username / tenant / now 全部塞 user 消息,绝不混进 system。
1024 token 怎么数
| 文本类型 | 1024 token 大致字数 |
|---|---|
| 中文长文(全汉字) | 1500-1800 字 |
| 中英文混合(技术文档) | 1200-1500 字 |
| 代码 + 注释 | 800-1200 字符 |
| JSON 结构 | 600-1000 字符 |
按 Anthropic Token Counting API(访问于 2026-05-19),可以预先 POST 文本到 /v1/messages/count_tokens 拿精确 token 数。Solopreneur 部署前在 CI 里跑一次校验,避免改 system 后跌破 1024 条件而不自知。
版本指纹:防止悄无声息失效
PROMPT_VERSION = "brand-v3-2026-04-08"
# 在 system 末尾加版本注释(不影响语义)
BRAND_SYSTEM = f"""你是 [品牌名] 的 AI 助手...
<!-- prompt-version: {PROMPT_VERSION} -->
"""
部署管线里加一个测试:
- staging 启动后发一条测试请求
- 看响应
usage.cache_creation_input_tokens是否非零 - 再发第二条相同 system 的请求
- 看响应
usage.cache_read_input_tokens是否非零 - 都符合预期才推 production
按 2026 年 Anthropic Pricing(访问于 2026-05-19),Sonnet 4.6 cache_write 单价 $3.75/MTok、cache_read $0.30/MTok,如果 cache 失效一天,5K prompt × 10K 请求多花 $112.5。版本指纹这一步省下的钱比花的时间多 100 倍。
命中率监控与周度复盘
每条请求记日志:
log = {
"timestamp": ...,
"prompt_version": PROMPT_VERSION,
"input_tokens": usage.input_tokens,
"cache_creation_tokens": usage.cache_creation_input_tokens,
"cache_read_tokens": usage.cache_read_input_tokens,
"user_id": ...,
"path": "/chat",
}
周度 SQL:
SELECT
prompt_version,
path,
SUM(cache_read_tokens) * 1.0 /
(SUM(cache_read_tokens) + SUM(input_tokens)) AS hit_rate
FROM api_log
WHERE created_at > now() - interval '7 days'
GROUP BY 1, 2;
健康参考线:
| 场景 | 健康命中率 |
|---|---|
| 客服 SaaS(共享 brand prompt) | 80-95% |
| 文档对话(每用户独立文档) | 30-50% |
| 多轮 Agent(同一 session) | 90%+ |
| RAG 检索类(每次 chunk 不同) | 看 system 是否稳定,40-70% |
一字不改的部署纪律
按 Anthropic Cookbook(访问于 2026-05-19)和社区实测,稳定 cache 的关键是部署纪律:
- system prompt 写死成常量,不要拼接动态值
- 不要 trim() 不要二次格式化(空格变了 cache 也变)
- 不要在 system 里 print 调试信息
- 改 prompt 走 PR,升版本号
- staging 跑命中率测试再 merge
很多团队踩的坑是「我只改了一个错别字」——错别字也是 cache miss。
tool definitions 也要 cache
跑 Agent 场景的 Solopreneur 特别注意:tools 数组通常 2-5K token,每次多轮调用全部重发。
# tool 定义 cache
response = client.messages.create(
model="claude-sonnet-4-6",
tools=[
{"name": "search_db", "description": "...", "input_schema": {...}},
# 10-30 个 tool
],
system=BRAND_SYSTEM,
messages=conversation_history,
)
按 Anthropic 文档,tools 是 cacheable block。Agent 在一个 session 内来回调 10-20 次,tool 定义全部 cache_read 单价(0.1x base),省钱效果显著。
中转方透传性测试
签约中转方前,跑这个脚本:
# 同一 system 发 2 次,看第二次的 cache_read
resp1 = client.messages.create(..., system=[{"text": LONG_PROMPT, "cache_control": {"type": "ephemeral"}}])
print(resp1.usage.cache_creation_input_tokens) # 应非零
print(resp1.usage.cache_read_input_tokens) # 应零(首次)
time.sleep(2)
resp2 = client.messages.create(..., system=[{"text": LONG_PROMPT, "cache_control": {"type": "ephemeral"}}])
print(resp2.usage.cache_creation_input_tokens) # 应零
print(resp2.usage.cache_read_input_tokens) # 应非零(命中)
如果中转方第二次响应里 cache_read 还是零,要么协议没透传,要么内部走了不同 endpoint。这种中转方一律不签。Solopreneur 用一条独立开发者用得起的 Claude 4.7 / GPT-5.5 中转跑日常,签约前用上面 6 行代码验证,半小时就能判清。