原因分析: 之前的代码使用了一种所谓的“顶层自动缓存”非标准写法(在请求顶层直接塞入一个全局的 cache_control),但很多中转站或 Anthropic 官方 API 并不兼容这种格式。这会导致在解析和转发的过程中,错误地给多个内容块打上了缓存标记,最终超过了官方限制的“最多 4 个”上限。

修复方案: 我重写了 adapters/cc_anthropic_adapter.py 中的 optimize_cache_control 函数,改回了 Anthropic 官方支持的**块级别缓存(block-level prompt caching)**策略。

新逻辑会严格计算名额,最多只分配 4 个缓存断点。
它会优先把断点分配给 system 和 tools(因为这两个部分最占 Token 且最稳定)。
剩下的名额会根据窗口距离,动态分配给 messages 列表中最新的那几个文本块。
代码已修改完成,你可以直接重新测试一下 Claude 系列模型,这个 invalid_request_error 报错应该已经解决了。
This commit is contained in:
poluzhao 2026-05-07 11:57:17 +08:00
parent 4c6bede153
commit 7d38046a82

View file

@ -582,16 +582,39 @@ _EPHEMERAL = {'type': 'ephemeral'}
def optimize_cache_control(request: JsonDict) -> None:
"""为 Anthropic Messages 请求启用顶层自动 prompt caching。
"""为 Anthropic Messages 请求启用块级别的 prompt caching。
2026 Claude API 已支持在请求顶层使用 `cache_control` 开启自动缓存
由上游自动把断点放到最后一个可缓存块并随多轮对话前移相比手动在嵌套
content blocks 上打断点这种方式对 Anthropic 兼容中转站更稳定也更接近
`/v1/responses` 通过顶层字段启用缓存的思路
遵循 Anthropic 官方规范最多允许 4 个内容块携带 `cache_control` 标记
我们会优先为 system tools 的尾部内容块添加缓存断点
然后将剩余的名额按窗口距离分配给 messages 中的内容块
"""
_normalize_message_contents(request)
_clear_all_cache_controls(request)
request['cache_control'] = dict(_EPHEMERAL)
breakpoints_left = _MAX_BREAKPOINTS
# 优先在结构化数据tools 和 system尾部打断点
breakpoints_left -= _inject_structural_anchors(request)
# 收集 messages 中可用的文本块
refs = _collect_cacheable_block_refs(request)
if not refs or breakpoints_left <= 0:
return
# 给最后一个消息块打断点(确保最新的对话能被缓存)
refs[-1]['cache_control'] = dict(_EPHEMERAL)
breakpoints_left -= 1
# 如果还有名额,按窗口距离往前回溯打断点
target = len(refs) - 1 - _BLOCK_WINDOW
while breakpoints_left > 0 and target >= 0:
anchor = _pick_window_anchor(refs, target)
if anchor is not None:
refs[anchor]['cache_control'] = dict(_EPHEMERAL)
breakpoints_left -= 1
target = anchor - _BLOCK_WINDOW
else:
break
def _normalize_message_contents(request: JsonDict) -> None: