# DeepSeek 上下文缓存优化设计方案 > 目标:提高 DeepSeek API 的 prompt 缓存命中率,从而降低 M4 翻译与 M7 日报的输入成本。 > 依据:DeepSeek「上下文硬盘缓存」机制([官方文档](https://api-docs.deepseek.com/zh-cn/guides/kv_cache))。 > 状态:**设计稿**,尚未改动生产代码;文中「改动点」需确认后实施。 ## 一、机制要点(DeepSeek 上下文硬盘缓存) 1. **默认开启、零代码开启**:所有请求自动参与缓存,无需在请求里额外传参。 2. **「缓存前缀单元」完整匹配**才命中:每次请求的输入结束位 / 输出结束位、以及系统检测到的公共前缀,都会落盘为**缓存前缀单元**。后续请求必须**完整匹配**某个缓存前缀单元,才会计入「缓存命中」。 - `A+B` 之后再来 `A+B+C` → 命中 `A+B`(沿用例一看)。 - `A+B` 之后再来 `A+C` → **不命中**,但系统会把公共前缀 `A` 落盘,下一轮 `A+D` 可命中 `A`。 3. **价格差异极大**(`deepseek-v4-flash`): | 输入 | 空闲 | 高峰 | |------|------|------| | 缓存命中 | 0.05 元/百万 | 0.10 元/百万 | | 缓存未命中 | 1.5 元/百万 | 3.0 元/百万 | - 命中比未命中便宜 **30 倍**。提升命中率是单位成本上最强的手段。 4. **可观测**:响应 `usage` 里的 `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` 直接给出命中情况,可据此验证优化前后。 ## 二、当前实现的问题 ### 场景一:M4 翻译(`llm/extractor.py`) 当前 `user` message 结构(`prompts/translation_and_extraction.md`): ``` 请处理以下英文财经新闻: **标题**: {title} ← 每篇不同 **来源**: {source_name} ← 每篇不同 **发布时间**: {publish_time} ← 每篇不同 **正文**: {content} ← 每篇不同 ``` - `system` prompt 完全固定 → 可作为一个稳定前缀,但只有几百 token,占单篇输入比例很小。 - `user` prompt 里「标题/来源/发布时间」都在正文之前且每篇不同 → 导致**每篇文章的 user 前缀都错位**,正文 `{content}` 无法形成稳定公共前缀,命中率大幅下降。 - 实测单篇平均输入 ≈1667 token,估算其中 75%+ 是正文 `{content}`;若能让正文前缀稳定,可显著命中。 ### 场景二:M7 日报 AI 摘要(`scheduler/reporter.py`) - `system` prompt 固定("你是国际财经日报撰写助手…"),user prompt 是当日事件列表,每批内容不同 → 只有极短的 system 前缀可命中,命中率低但总量小(每天仅几千 token),非成本重点。 ## 三、优化方案 ### 方案 A(推荐,改动小、收益大):稳定 M4 user prompt 前缀 把「每篇变化但重复出现的结构性开头」从正文前挪到结构里,让**正文 `{content}` 作为相对固定的前缀占位**,或至少让「固定指令段」全部前置。 调整 `prompts/translation_and_extraction.md` 的 user 段,把固定指令前置,变化字段后置: ``` 请处理以下英文财经新闻,输出 JSON(标题译文、正文全文译文、事件列表): [MODEL_INSTRUCTION_BLOCK] ← 完全固定的指令/示例(移到最前) 【待处理新闻】 正文: {content} ← 长固定位置内容 【元信息】 标题: {title} 来源: {source_name} 发布时间: {publish_time} ``` > 说明:把很长的 `{content}` 提前、固定,让后续连续请求以同一篇或相似内容为前缀;配合条件:同一批 82 篇连续翻译时,尽量让公共 system + 固定指令 + 常见正文前缀命中缓存。 ### 方案 B(最直接):「正文最早、短字段最后」重排 即便不改语义,仅调整 user 段字段顺序也能提升命中:把 `{content}`(变量庞大但相对可复用的重复新闻源)尽量前置且稳定,`{title}`/`{source_name}`/`{publish_time}`(每篇必变)后置。这样 system + 固定指令 + 正文前缀可形成缓存前缀单元,后续请求命中。 ### 方案 C:并发下最大化公共前缀(很少改动) - M4 并发 `concurrency=3` 同时发 3 个不同文章 → 冷启动多,前缀难以公共。可在首篇(或任务开头)先发 1 篇建立缓存前缀,再并发,后续命中率高。收益存在但不稳定,不强制。 ### 方案 D(工程化验证):在 events 文件记录缓存命中 利用 `usage.prompt_cache_hit_tokens / prompt_cache_miss_tokens`,把这两项写入 `data/events/{date}/{hash}.json`(现有字段 `prompt_tokens` 旁),用于量化验证方案 A/B 落地后的命中率提升。属观测增强,不影响功能。 ### 方案 E(日报场景,次要) M7 摘要量小,可不动;若要优化,保持 system prompt 稳定即可(当前已稳定)。可复用「截断重试时复用同一前缀」。 ## 四、建议实施顺序 1. 先落地**可观测性(方案 D)**:给 events 记录缓存命中 token(小改动,零风险),跑 1 次对比基线。 2. 再实施**方案 A / B**(重排 M4 user prompt),对比命中率与单篇输入成本。 3. 酌情试点**方案 C**(并发预热)。 ## 五、验收标准 - 单篇输入 token 中 `prompt_cache_hit_tokens` 占比显著上升(目标:日常连续翻译批 >50%)。 - 单篇平均输入成本下降(命中 0.05 vs 未命中 1.5 元/百万)。 - 翻译质量、输出格式不受影响(缓存只作用于输入前缀,不影响输出随机性)。 ## 六、参考 - [DeepSeek 上下文硬盘缓存](https://api-docs.deepseek.com/zh-cn/guides/kv_cache) - [DeepSeek 模型与价格](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/) - 相关代码:`prompts/translation_and_extraction.md`、`llm/extractor.py`、`scheduler/reporter.py` - 配套成本分析:[llm-cost-analysis.md](llm-cost-analysis.md)