feat(anthropic): configurable prompt-cache TTL, still 5m by default - #61
Merged
Merged
Conversation
`AnthropicClient` 自己放两个 ephemeral 断点(system 前缀 + 最后一条持久消息),
但一直只用 API 缺省的 5 分钟。那个窗口的参照物不是任务多长,而是**一轮**多长:
缓存项在最后一次使用后才开始计时,所以只要相邻两次调用的间隔超过 TTL,整条
前缀就作废并按写价重写。
ApodexHarness 2026-10-05 的 GDPval 批次量到了代价(6056 次 claude-opus-5-5
effort=max 调用):相邻调用间隔 p90 = 355s、**12.9% 超过 300s**,这些调用冷率
**35.7%**,而间隔 ≤300s 的只有 9.5%;turn>3 且写入 >30k 的冷调用 498 次、
中位 81k,合计 61.6M 写 token。
- `prompt_cache_ttl`("5m" | "1h")加到构造器,并由 `build_protocol_client`
从 cfg 透传,所以这是**profile 级**的键 —— 一个模型的缓存寿命跟着选它的
profile 走,而不是跟着部署走。
- 空值与 "5m" 都**不发** ttl 字段:5m 本就是 API 缺省,不发能让所有现有消费者
的请求逐字节不变。
- `ANTHROPIC_PROMPT_CACHE_TTL` 作为整次运行的兜底(给 profile 够不到的构造点),
client 自己声明的值优先。
- 非法值(`30m`、数字、带空格的单位)在构造期抛 `ValueError`,不回落到 5m ——
回落只会表现为"命中率下降",而命中率没人盯着,正是这个旋钮要修的那种静默。
- TTL 只决定已有断点的寿命,不会放断点:`ANTHROPIC_PROMPT_CACHE=0` 依然通杀。
- 计费不用改:`_anthropic_cache_write_tokens` 早就把
`cache_creation.ephemeral_1h_input_tokens` 算进去了。
**该按模型开,不要全局开**:1h 的写价是基础输入的 2×,5m 是 1.25×,所以只有
"一轮可能比 5 分钟还长"的慢模型才赚;快模型没有冷重写可省,只会多付写价。
文档里把这条判据和实测数字都写下来了。
网关必须真的转发该字段,所以 llm-hub 是实测过的(2026-10-05,
temp/2026-10-05_llmhub-1h-ttl-实测.md):带 `ttl:"1h"` 返回 HTTP 200,服务端把
write 记进 **1h 桶**而非 5m 桶(43215 vs 对照组 40615 进 5m),548s 后回读
**完整命中**(creation 0),而未配置的对照组已失效并整条重写。
`anthropic-beta: extended-cache-ttl-2025-04-11` **不需要**。这里两项都查了:
ttl 被网关悄悄剥掉时,5 秒回读的表现和成功完全一样,只有计费桶和跨窗口回读
能区分。
测试:新增 8 个(缺省不发字段、1h 落在两个断点上、显式 5m 不上线、env 兜底与
client 优先、非法值在构造期/env 下抛错、kill switch 仍通杀、builder 透传)。
全量 1913 passed / 2 skipped;ruff 与 pyright(改动模块)清。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
为什么
AnthropicClient自己放两个 ephemeral 断点(system 前缀 + 最后一条持久消息),但一直只用 API 缺省的 5 分钟。那个窗口的参照物不是任务多长,而是一轮多长 —— 缓存项在最后一次使用后才开始计时,所以只要相邻两次调用的间隔超过 TTL,整条前缀就作废并按写价重写。ApodexHarness 2026-10-05 的 GDPval 批次量到了代价(6056 次
claude-opus-5-5effort=max调用):改了什么
prompt_cache_ttl("5m"|"1h")加到AnthropicClient构造器,并由build_protocol_client从 cfg 透传 —— 这是 profile 级的键,一个模型的缓存寿命跟着选它的 profile 走,而不是跟着部署走。5m都不发 ttl 字段:5m 本就是 API 缺省,不发能让所有现有消费者的请求逐字节不变。ANTHROPIC_PROMPT_CACHE_TTL作为整次运行的兜底(给 profile 够不到的构造点),client 自己声明的值优先。30m、数字、带空格的单位)在构造期抛ValueError,不回落到 5m —— 回落只会表现为"命中率下降",而命中率没人盯着,正是这个旋钮要修的那种静默。ANTHROPIC_PROMPT_CACHE=0依然通杀。_anthropic_cache_write_tokens早就把cache_creation.ephemeral_1h_input_tokens算进去了。消费者注意:该按模型开,不要全局开
1h 的写价是基础输入的 2×,5m 是 1.25×。所以只有"一轮可能比 5 分钟还长"的慢模型才赚;快模型没有冷重写可省,只会多付写价。判据和上面那组实测数字都写进了
docs/provider-substrate-boundary.md。网关实测(llm-hub,2026-10-05)
网关必须真的转发该字段,所以实测过 —— 而且查了两项,因为 ttl 被悄悄剥掉时,5 秒回读的表现和成功完全一样:
{"type":"ephemeral"}(现状)+ ttl:"1h"+ ttl:"1h"+ beta 头anthropic-beta: extended-cache-ttl-2025-04-11不需要(已 GA)。测试
新增 8 个:缺省不发字段、1h 落在两个断点上、显式 5m 不上线、env 兜底与 client 优先、非法值在构造期/env 下抛错、kill switch 仍通杀、builder 透传。
全量 1913 passed / 2 skipped;ruff 全清;pyright(改动模块)0 error。按
docs/versioning.md,feature PR 不动版本号与 CHANGELOG,只加changes/61.feature.md。🤖 Generated with Claude Code