从 20% 到 90%:LLM Prompt Cache 工程优化实践(生产环境无损优化)

3 阅读3分钟

保持业务功能完全不变,如何让自动出题系统的 Token 成本降低近一半

摘要

本文记录了一次真实生产项目的优化过程。项目是一个基于知识库(RAG)的 LLM 自动出题系统,目标是在不修改任何业务功能、接口、数据库结构和校验逻辑的前提下,仅通过 Prompt Engineering 与工程优化,提高 Prompt Cache 命中率、降低 Token 消耗。

最终实现:

  • Prompt Cache:约 10%~30% → 80%~90%(依模型实现而异)
  • Token 成本降低约 40%~60%
  • Context 构建从 N 次降为 1 次
  • 保持 API、JSON、数据库、校验逻辑完全不变

一、背景

自动出题流程:

flowchart LR
A[知识库] --> B[Chunk切分]
B --> C[构建Prompt]
C --> D[LLM生成问题]
D --> E[答案溯源校验]
E --> F[(数据库)]
F --> G[评分模型]

线上监控发现:

  • 评分阶段缓存命中率很高;
  • 出题阶段缓存命中率极低;
  • Token 成本几乎全部集中在出题阶段。

于是开始定位问题。


二、Prompt Cache 为什么失效?

Prompt Cache 的核心不是"语义相同",而是Token 前缀连续一致

错误理解:

Prompt A
Context 相同
Category 不同

很多人认为应该命中缓存。

实际上缓存比较的是:

token1
token2
...
token1024

前缀任意位置变化,都可能导致缓存无法复用。


三、第一版代码的问题

① 分类导致 Context 不一致

sample_chunks = _stable_sample(chunks, category)

不同分类得到不同 Chunk。

② 每个分类重复构建 Context

context = "\n".join(...)

CPU 与 Prompt 都重复。

③ Prompt 前部变化太多

Category
Count
DocNames
Context

④ Request Count 动态变化

request_count=max(count*3,10)

不同请求 Prompt 不一致。

⑤ Context 无上限

题量越大,Prompt 越长。


四、优化过程

Step1:共享 Context

shared_chunks = sorted_chunks[:need]
shared_context = build_context(shared_chunks)

for category in categories:
    generate_questions_by_category(
        context=shared_context
    )

收益:

  • Prompt 更稳定
  • CPU 降低
  • Cache 明显提升

Step2:固定 Chunk

取消按分类随机采样:

shared_chunks = sorted_chunks[:need]

所有分类共享同一 Context。


Step3:固定 Request Count

GLOBAL_REQUEST_COUNT = 60

Prompt 永远一致。

真正数量:

return valid[:count]

保证业务结果一致。


Step4:限制 Context

MAX_SHARED_CHUNKS = 50

content[:300]

保证 Prompt 长度稳定。


Step5:共享 DocNames

fixed_doc_names = sorted({
    c["filename"]
    for c in shared_chunks
})

避免分类之间文档列表不同。


Step6:调整 Prompt 顺序

Prefix
↓
DocNames
↓
Context
↓
Category
↓
Count

让固定内容尽量靠前。


Step7:降低随机性

temperature = 0.2

提高稳定性。


五、为什么这是"无损优化"?

整个优化过程中没有修改:

  • generate_all_questions()
  • generate_questions_by_category()
  • 输出 JSON
  • 数据库结构
  • 答案溯源
  • 去重
  • 幻觉题处理
  • 分类规则

变化的是实现,不是业务能力


六、优化前后对比

项目优化前优化后
Prompt Cache10~30%80~90%*
Token 成本100%40~60%
Context 构建N 次1 次
Prompt 稳定性
CPU 拼接多次一次

*实际效果与模型厂商 Prompt Cache 策略有关。


七、踩坑总结

❌ Prompt 相同语义 ≠ Cache 命中

❌ 随机采样会破坏 Prompt 稳定性

❌ Request Count 动态变化会影响前缀一致性

❌ Context 无限增长会增加成本

真正有效的是:

  • 固定 Context
  • 固定 Chunk
  • 固定 Request Count
  • 固定 DocNames
  • 控制 Prompt 长度
  • 保持 Prompt 前缀稳定

八、适用场景

这些优化适用于绝大多数 OpenAI Compatible API:

  • OpenAI
  • DeepSeek
  • OpenRouter
  • 阿里百炼
  • 火山引擎
  • SiliconFlow

尤其适用于:

  • RAG
  • 自动出题
  • 知识库问答
  • Agent
  • 企业内部 AI

九、结语

这次优化最大的收获不是某一个 Prompt 技巧,而是理解了 Prompt Cache 的本质。

很多时候,降低成本并不需要重写业务逻辑

通过稳定 Prompt、共享 Context、控制 Token,就可以在保持功能完全一致的前提下获得非常可观的收益。

对于生产环境来说,这类优化风险小、收益高、易于回滚,非常值得优先实施。