事情本身不复杂,线索都在日志里。claude-3-5-sonnet 的 system 里加了 cache_control ephemeral,第一次请求 cache_creation_input_tokens 是 8200,五分钟后同样的 system 再发,cache_read_input_tokens 变成 0。

第一反应是「system 里拼了当天日期,每次请求前缀都不一样」,验证下来不成立。

原因不止一个,得分开看。ephemeral 缓存的默认存活时间是 5 分钟,间隔一超就失效。少数情况下是缓存断点没放在 system 的最后一块,前面变动导致整段作废。

另外,换了 API key 或组织,缓存不跨账号共享。

动手时先把 usage 里的三个缓存字段都打出来

python -c "import json;u=json.load(open('resp.json'))['usage'];print(u.get('cache_creation_input_tokens'),u.get('cache_read_input_tokens'),u.get('input_tokens'))"

去掉 system 里的日期后再跑一轮

python -c "print(open('system.txt').read()[:120])"

接着把 cache_control 挪到最后一个 system block

curl https://api.anthropic.com/v1/messages -H "x-api-key: $ANTHROPIC_KEY" -H 'anthropic-version: 2023-06-01' -H 'Content-Type: application/json' -d @req_cache.json

剩下的就是按 3 分钟间隔连发三次看命中

python -c "import time;[time.sleep(180) for _ in range(3)];print('three rounds done')"

中途换过一次思路,把范围缩到最小的那一层,反而快了。

改完先别撤,确认这几项。连续三次请求里后两次的 cache_read_input_tokens 都接近 8200,input_tokens 只剩几十,单次成本降到原来的三分之一。

缓存是按前缀逐字节比对的,system 里任何一个动态字段都会让整段失效,日期、用户名、请求 id 都不要往里放。报错文案容易误导,真正的原因往往在相邻那一层的日志里。

作者 9823189