agent-Specialization/cache_research/SUMMARY.md
JOJO 3a4ea67e26 feat(stats): token 统计新增缓存命中追踪与命中率展示
- utils/token_usage.py:usage 归一化补全缓存命中字段提取,覆盖
  prompt_tokens_details/input_tokens_details.cached_tokens(OpenAI 系)、
  顶层 prompt_cache_hit_tokens(DeepSeek)、顶层 cached_tokens(Kimi/Step)、
  cache_read_input_tokens(Anthropic 系)、cachedContentTokenCount(Gemini);
  Anthropic 语义下把缓存读/写加回总输入以统一口径,normalize 保持幂等
- 对话级统计新增 total_cached_input_tokens 与 cache_exempt_input_tokens
  (首轮/深度压缩后首轮未命中缓存的输入视为冷启动成本,豁免出命中率分母;
  压缩通过 cache_cold_start_pending 标记在下一次真实调用时判定)
- token_update 广播与 token-statistics 接口同步携带新字段
- TokenDrawer 面板新增「累积缓存输入」「缓存命中率」(前端按 缓存/(总输入-豁免) 换算)
- 深色模式下「当前上下文」数字由灰色 --accent 改为 --text-primary(白)
- 附 cache_research/ 各厂商缓存字段调研文档(代码注释引用)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 12:16:48 +08:00

8.0 KiB
Raw Blame History

LLM API 缓存命中 Token 字段调研 · 总汇总

调研时间2026-08-29 · 目的:为「验证各家 API 是否返回缓存命中 token」实验提供字段对照 详细报告:official_overseas_v2/report.md(海外官方)、official_china/README.md(国内官方)、aggregators/report.md(聚合层) ⚠️ 全部为官方文档 + 社区证据调研结论,未经实请求验证;实验时以实际返回为准。


一、缓存命中字段总对照表

官方 API

提供商 命中字段完整路径 写入字段 自动/显式 最低门槛 命中价折扣
OpenAI Chat Completions usage.prompt_tokens_details.cached_tokens ...cache_write_tokensGPT-5.6+ 自动5.6+ 可显式断点) 10245.5 及更早 2048 读 0.1×5.6+/ 0.5×(老模型)
OpenAI Responses API usage.input_tokens_details.cached_tokens ...cache_write_tokens 同上 同上 同上
Anthropic Claude usage.cache_read_input_tokens(顶层) usage.cache_creation_input_tokens(另有 cache_creation.ephemeral_5m/1h_input_tokens 细分) 显式 cache_control 按模型 512/1024/2048/4096 读 0.1×、写 1.25×5m/ 2×1h
Google Gemini usageMetadata.cachedContentTokenCountSDKcached_content_token_count 无 usage 内写入字段(显式缓存按资源 TTL 计费) 隐式自动 + 显式 cachedContents 隐式 20482.5/ 40963.x 命中 ~0.1×2.5+
xAI Grok usage.prompt_tokens_details.cached_tokensResponsesinput_tokens_details.cached_tokens 自动(建议 x-grok-conv-id/prompt_cache_key 未公布 有缓存价
Mistral usage.prompt_tokens_details.cached_tokens 半显式(建议 prompt_cache_key 64 tokens 起,恒为 64 的倍数 读 0.1×
DeepSeek usage.prompt_cache_hit_tokens(顶层!) + prompt_cache_miss_tokens 无(自动) 自动 未公布 读 ≈0.03×$0.014 vs $0.44,折扣最大)
Kimi / Moonshot usage.cached_tokens(顶层);部分官方示例为 prompt_tokens_details.cached_tokens——两处都要读 自动(可用请求参数 prompt_cache_key 提命中率) 未公布 读 0.1×~0.2×k3 为 0.1×
Qwen / DashScope usage.prompt_tokens_details.cached_tokens;显式另有 cache_creation_input_tokensAnthropic 兼容模式为 cache_read_input_tokens 显式时上报创建量 隐式自动 + 显式 cache_control 隐式 256部分模型 2000/ 显式块 1024 隐式读 0.2×;显式读 0.1×、写 1.25×
智谱 GLM usage.prompt_tokens_details.cached_tokens 自动 512 读 0.5×
豆包 / 火山方舟 usage.prompt_tokens_details.cached_tokens 创建接口响应同路径 仅显式Context API / Responses API caching 参数) 缓存输入折扣价 + 存储费
MiniMax OpenAI 模式:prompt_tokens_details.cached_tokensAnthropic 模式:cache_read_input_tokens Anthropic 模式:cache_creation_input_tokens 自动 + 显式Anthropic 模式) 512 读 0.1×~0.2×
阶跃 Step usage.cached_tokens(顶层) 自动 256 读 0.2×
百度千帆 usage.prompt_tokens_details.cached_tokens 自动 未公布 读 0.4×

聚合层 / 中转(实验时最容易踩坑的一层)

服务 缓存字段行为 关键坑
OpenRouter 规范化为 usage.prompt_tokens_details.cached_tokens + 扩展 cache_write_tokens / cache_discount / cost ⚠️ 它另有「响应缓存」X-OpenRouter-Cache-Status: HIT——命中时 usage 全为 0,与 prompt 缓存是两回事;个别上游(如 DeepSeek缓存不过网关
opencode Zen / Go Zen 价格表单列 Cached Read/Write必然解析了上游缓存字段「opencode go」= $10/月订阅服务,非 Go 语言版 ⚠️ opencode 客户端流式解析有 bug#33997tokens_cache_read 恒 0——别看客户端展示值抓原始 SSE
one-api / new-api / one-hub 意图透传 cached_tokens,但流式渠道多个已证实 bug字段清零/计费错误/负 token ⚠️ 客户端收到的 usage ≠ 网关账单;非流式作基线对照
国内中转站packycode、灵眸AI 等) 口碑「官转」站透传 Anthropic 原生 cache_creation/read_input_tokens 并按 5m cache write 计费;逆向接口站无缓存 社区验收标准=响应 usage 里有没有这两个字段
LiteLLM / Portkey / CF AI Gateway LiteLLM 双格式并存但 Anthropic 透传路径有 bugPortkey 明确规范化CF 未文档化(推测透传) LiteLLM /v1/messages 路径不映射 cached_tokens#27763
订阅制Copilot/Cursor/Windsurf/Augment 无公开 per-request usage APICursor/Augment 面板展示 cache read/write数据来自上游响应 无法从响应侧做本实验,跳过

二、实验用统一读取器Python 伪代码)

def extract_cache_hit(usage: dict, body: dict | None = None) -> dict:
    """按优先级从各家 usage 中提取缓存命中 token 数。"""
    u = usage or {}
    details = u.get("prompt_tokens_details") or {}
    in_details = u.get("input_tokens_details") or {}
    candidates = [
        ("prompt_cache_hit_tokens", u.get("prompt_cache_hit_tokens")),          # DeepSeek顶层
        ("cached_tokens@top",       u.get("cached_tokens")),                    # Kimi / Step / 部分 DashScope顶层
        ("prompt_tokens_details",   details.get("cached_tokens")),              # OpenAI Chat / Qwen / GLM / MiniMax / 千帆 / xAI / Mistral / OpenRouter
        ("input_tokens_details",    in_details.get("cached_tokens")),           # OpenAI/xAI Responses API
        ("cache_read_input_tokens", u.get("cache_read_input_tokens")),          # Anthropic / Bedrock / MiniMax-Anthropic / 中转站
    ]
    hit = next(((k, v) for k, v in candidates if v), (None, 0))
    # Gemini 走完全独立的 usageMetadatacamelCase从响应体而非 usage 取
    gemini = ((body or {}).get("usageMetadata") or {}).get("cachedContentTokenCount")
    return {"hit_tokens": hit[1] or gemini or 0, "field": hit[0] or ("usageMetadata" if gemini else None)}

三、实验设计要点(三份报告的共同结论)

  1. 两轮法:第 1 轮建缓存(命中=0 或走写入字段),第 2 轮同前缀不同后缀(命中>0。两轮间隔必须在缓存 TTL 内Anthropic/Qwen 显式 = 5 分钟)。
  2. 前缀 ≥2048 tokens避开各家阈值差异256~4096 不等)。
  3. 流式必须 stream_options: {"include_usage": true},否则 OpenAI 系协议流式响应没有 usage chunkKimi 流式末 chunk 带 usageAnthropic 看 message_start 事件。
  4. 语义差异OpenAI 系 prompt_tokens 包含缓存部分Anthropic input_tokens 不含缓存部分cache_read 另算)。对账时别混。
  5. 区分两种「缓存」网关级响应缓存result cache命中时 usage 可能归零)≠ prompt 前缀缓存KV cache本实验目标
  6. 聚合层要抓三个视图:客户端响应 usage、网关账单/消费日志、可直连时的上游原生 usage——三者可能互不一致new-api #6144 教训)。
  7. 首轮 cache_read=0 是预期行为,不是字段丢失;写入字段(cache_creation_input_tokens / cache_write_tokens>0 反而证明缓存机制在运作。

四、详细报告索引

报告 路径 覆盖
海外官方 official_overseas_v2/report.md OpenAI / Anthropic / Gemini / xAI / Mistral / Bedrock / Azure
国内官方 official_china/README.md + usage_fields_reference.md DeepSeek / Kimi / Qwen / GLM / 豆包 / MiniMax / Step / 千帆
聚合层 aggregators/report.md OpenRouter / opencode Zen·Go / one-api·new-api·one-hub / 中转站 / Copilot·Cursor·Windsurf·Augment / LiteLLM·Portkey·CF