Files
writing-agent/docs/briefs/prompt-cache-audit-2026-10-07.md
T
chenmoranzhiandCursor 7185f25dec 补上费用核对与缓存审计材料,方便后续按结论继续压创作成本。
交接说明自包含,不依赖聊天记录也能接着做优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 23:49:51 +08:00

7.0 KiB
Raw Blame History

提示缓存核对:记账是否可信、首填为何仍≈0

状态:核对结论。承接 docs/briefs/token-cost-report-2026-10-06.md §1.5 / §4.P2。
记录:2026-10-07。
疑问:若 OpenRouter 上 DeepSeek 缓存能挂住,是否说明「首填几乎不吃缓存」是本地记账错了,或粘滞/TTL 才是主因?

1. 结论(先看这个)

  1. 缓存记账可信。 我们只是把 OpenRouter 的 usage.prompt_tokens_details.cached_tokens(及 DeepSeek 的 prompt_cache_hit_tokens)原样写入 ~/.writing-agent/stats;不是自估、不是漏字段。
  2. 「首填≈0、retry 高」是上游真值,不是漏记。 同轮对照里,绝大多数是 fill 几乎无缓存、fill-retry 立刻上到八成上下。若解析坏了,两边应同假。
  3. DeepSeek 探测成功,不能推翻 Gemini 首填账。 花销报告窗口主模型是 google/gemini-3.*-flash;DeepSeek 只证明「同 baseUrl + 同解析链路」在另一模型上能读到非零缓存。
  4. 粘滞/session_id 不是当前主账缺口。 同会话内 fill→retry 已能吃到缓存,说明粘到了同一上游且前缀可复用;贵在 每一轮第一枪大前缀仍全价,以及 重试常态。
  5. 优化优先级不变: 仍以 P0(fill 去工具循环、消灭 retry)为主;P2(跨 select/fill 稳定前缀)对 Gemini 有意义,但不是「先修记账」或「再抠 TTL」。

2. 记账链路(我们记了什么)

层 行为
OpenRouter 响应 prompt_tokens / prompt_tokens_details.cached_tokens / 可选 cache_write_tokens
parseUsage(src/llm/client.ts) 读 cached_tokens;DeepSeek 另认 prompt_cache_hit_tokens
stats jsonl cachedTokens / cacheWriteTokens 等落盘
报告命中率 cached / prompt(按请求平均或合计)

单元测试覆盖 OpenAI details、DeepSeek hit/miss、cache write。探测脚本与生产走同一 complete → parseUsage。

原始对照(2026-10-07,Gemini 探测): 直接 fetch OpenRouter,两次固定大前缀请求的 usage 均为:

"prompt_tokens_details": {
  "cached_tokens": 0,
  "cache_write_tokens": 0
}

落盘若记成 0,与上游一致。


3. 生产窗口再切一刀(缓存行为)

范围:~/.writing-agent/stats 中 2026-10-03、10-04、10-06、10-07 的 dictate_agent(与花销报告同一量级窗口,略含 10-07)。

step 请求 有缓存占比 均命中率 中位命中
select 79 1.3% ~0.8% 0%
fill 79 1.3% ~0.5% 0%
fill-retry 83 84.3% ~71.9% ~85%
reply 30 0% 0% 0%

同轮 fill + 至少一次 fill-retry 的配对约 45:

模式 对数
首填≈0 且 retry 命中 >40% 34
两者都≈0 11
首填命中 >20% 0

样本:同轮 fill cached=0、随后 fill-retry prompt≈90k / cached≈82k(命中约 91%)。这只能解释为:上游在 retry 上报了高缓存,我们照记。

模型侧:窗口几乎全是 google/gemini-3.7-flash / gemini-3.8-flash;与花销报告一致。


4. 探测:DeepSeek vs Gemini(同 OpenRouter)

核对方式:固定大前缀连打 OpenRouter,读 usage.prompt_tokens_details.cached_tokens(脚本已删,结论仍有效)。

4.1 deepseek-v4-pro

调用 prompt cached 命中
A#1 10602 7650 72.2%
A#2~A#4 10602 9743 91.9%
等 60s 后 B#1~B#2 10602 9743 91.9%

→ 隐式缓存可用;短间隔爬升;约 1 分钟间隔未掉光。说明「同 baseUrl + 粘滞 + 记账」这条链对 DeepSeek 成立。

4.2 google/gemini-3.8-flash

调用 prompt cached 命中
A#1~A#3(间隔 5s) ~12.7k~15k 0 0%

原始 usage 同上,cached_tokens=0 且 cache_write_tokens=0。

→ 不是我们没记上,是这次探测形态下上游就报 0。与生产里「仅当 fill 刚跑过、retry 几乎同前缀」才高频命中,并不矛盾:retry 复用的是刚发过的整包 fill 前缀;探测的「新会话 + 纯文本 system」未复现那种命中。

OpenRouter 文档对 Gemini 同时提到隐式缓存与 cache_control 显式断点;本仓库未打 cache_control。生产 retry 仍能命中,说明至少部分 Gemini 路由在「极近、极同前缀」下会报 cached_tokens>0;不能因此假设跨 select→fill、或跨轮首填也会自动吃到折扣。


5. 对花销报告结论的修正与保留

命题 判定
首填缓存几乎失效 保留(记账复核后仍成立)
贵主要因为 fill 大前缀 ×(1+retry) 保留
怀疑「其实有缓存但没记」 否定
怀疑「DeepSeek 能缓存 ⇒ 当前账单不该贵」 否定(账单模型是 Gemini;且 DeepSeek≠本窗口主路径)
怀疑「再抠 TTL / session 就能救首填」 弱化:同会话 retry 已证明粘滞可用;缺口在 select≠fill 前缀 与 每轮第一枪,不是「完全没粘住」
P2 共享稳定前缀 仍值得做(对 Gemini 跨步命中),但收益次于 P0 去循环
换 DeepSeek 指望隐式缓存 可作实验,质量/工具/价格需另验;不能单靠本次探测定案

相对花销直觉(未缓存=1、缓存≈0.25):retry 已打折,未打折的是几乎每次的首填大包。消灭 retry 或把首填变小,都比「怀疑仪表」更贴账单。


6. 建议(本核对后的落地顺序)

与 token-cost-report-2026-10-06.md §5 对齐,本文件只钉缓存相关验收:

步 动作 验收
A 不改记账;需要时用原始 usage 或 /generation 抽查 抽查与 jsonl cachedTokens 一致
B P0:fill 改为一次 structured,去掉写工具循环 每轮调用 p50→2;fill-retry 占比 <10%(主收益)
C P2:select/fill 共用不变字节前缀,易变块置尾 同会话第 2 轮起 fill 首次 cached/prompt 明显上升(Gemini 目标仍可订 ≥40%,以实测为准)
D 若坚持 Gemini 显式缓存 单独评估 OpenRouter cache_control;先小流量探测,再决定是否进主路径
E 可选:用 DeepSeek 等隐式缓存友好模型做同书对照周 比的是 有效单价 × 质量,不是单看 hit%

明确不做: 为「修缓存」去动 parseUsage 估命中;用 provider.order 换 session_id;把探测脚本的 DeepSeek 命中率写进创作账单当成已省钱。


7. 一句话答复原疑问

缓存记录是正确的。DeepSeek 能挂缓存,只说明链路和仪表没坏;当前创作窗口里 Gemini 首填几乎不打折是上游如实返回,retry 高命中反而证明记对了。钱仍主要花在「每轮第一枪大前缀 + 填写真的在重试」,优先去 Agent 循环,而不是先怀疑统计或死磕 TTL。