LLM 上下文越来越长(200K → 1M → 2M token),看起来”塞进所有文档让模型自己看”是最简单的方案。但成本和延迟让”naive 长上下文”很难做生产。理解 prefix cache、RAG、压缩三种核心降本手段,能让 LLM 应用既能用又能负担。
长上下文的真实成本
Claude Sonnet 4:
输入 $3 / 100 万 token
输出 $15 / 100 万 token
场景:
100 万 token 文档作为上下文
1000 token 用户问题
500 token 答案
单次成本:
输入:1,001,000 tokens × $3/1M = $3.003
输出:500 tokens × $15/1M = $0.0075
总计:$3.01
每天 100 用户 × 10 次 = 1000 次请求
每天成本:$3,010
每月成本:$93,000
$93,000/ 月 只是上下文成本——还没算服务器、监控、工程师工资。
降本后:
- prefix cache(命中 90%):$13,000
- RAG(只发 5K token):$465
- 智能优化组合:$200-500
三大降本手段
| 手段 | 适用场景 | 节省比例 | 复杂度 |
|---|---|---|---|
| Prompt cache | 同 prefix 频繁请求 | 60-90% | 低 |
| RAG | 大文档库 + 检索式查询 | 95%+ | 高 |
| 上下文压缩 | 自然语言可压缩 | 50-90% | 中 |
1. Prompt Cache:最低成本优化
机制:
第一次请求:
prompt = [系统提示 + 文档] + [新问题]
服务端:hash 前缀 → 计算 → 缓存中间状态
计费:完整 token
第二次请求:
prompt = [系统提示 + 文档] + [另一个新问题]
服务端:hash 前缀 → 命中缓存 → 直接复用
计费:前缀部分按缓存价(10%)+ 新问题按完整价
Anthropic Prompt Caching:
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "You are an assistant.",
},
{
"type": "text",
"text": large_document, # 大文档
"cache_control": {"type": "ephemeral"} # 标记缓存
}
],
messages=[
{"role": "user", "content": user_question}
]
)
计费:
- 写入缓存:$3.75 / 1M token(比标准贵 25%)
- 读取缓存:$0.30 / 1M token(便宜 90%)
- TTL 5 分钟(默认)/ 1 小时(可选)
OpenAI Prompt Caching(2024 起自动):
- 自动开启,无需手动标记
- 命中:50% 折扣
- TTL 几分钟
关键要求:
- prefix 必须完全一致——多一个空格不命中
- 必须在 prompt 前部
- 缓存有 TTL
适用场景:
✅ 同一文档 → 多个用户问问题
每个用户的请求 prefix 一样
✅ 系统提示长 → 多次调用
prefix = 系统提示
✅ Few-shot 示例 → 不同输入
prefix = 示例集
❌ 每次 prompt 都不同
❌ 高度个性化的 prompt
❌ 长尾、低频请求
2. RAG:大文档的标准方案
架构:
1. 准备阶段:
大文档 → 切 chunk → 向量化(embedding) → 存入向量数据库
2. 查询阶段:
用户问题 → 向量化 → 相似度检索 top-k chunk
↓
prompt = [系统提示] + [检索的 chunk] + [用户问题]
↓
LLM 生成答案
chunk 设计的核心变量:
| 变量 | 推荐值 | 原因 |
|---|---|---|
| chunk 大小 | 500-1000 token | 平衡上下文和精度 |
| 重叠(overlap) | 10-20% | 防止信息切断 |
| 切分策略 | 句子 / 段落边界 | 保留语义完整 |
| 检索 top-k | 5-20 | 平衡召回和成本 |
| embedding 模型 | OpenAI ada-002 / Cohere v3 | 当前主流 |
chunk 大小的取舍:
chunk 太小(< 200 token)
→ 一个 chunk 信息不够,无法独立回答问题
→ 需要更多 chunk → 增加成本
chunk 太大(> 2000 token)
→ 一个 chunk 多个主题,向量化时主题被"平均"
→ 检索精度下降
→ top-k 命中相关性差
500-1000 token 甜点:
→ 包含完整段落 / 小节
→ 向量化主题清晰
→ 检索精度高
重叠的作用:
不重叠:
chunk1: token 1-500(结尾在句子中间)
chunk2: token 501-1000(开头是另一句的中间)
→ 切割处的信息被破坏
10% 重叠:
chunk1: token 1-500
chunk2: token 451-950(重复 50 token)
→ 关键信息至少在一个 chunk 完整出现
结构化文档的天然边界:
API 文档:
每个 endpoint = 1 chunk
书籍:
每个章节 = 1 chunk(如果 < 2000 token)
长章节按 section 切
代码:
每个函数 = 1 chunk
类按方法切
中文 chunk 的特殊性:
中文 token 比英文密度高:
英文 1 词 ≈ 1.3 token
中文 1 字 ≈ 2-3 token
500 token chunk:
英文:约 380 词
中文:约 200 字
中文断句:
按 "。""!""?" 切(不要在逗号切)
考虑中英混排(用 \\u4e00-\\u9fff 检测)
3. 上下文压缩
几种策略:
冗余去除
原 prompt(500 token):
你是一个有耐心的客服,请用友善的语气回答用户的问题。
请确保回答准确。请不要编造信息。如果不确定请说明。
请保持专业。请使用中文回答。
...
压缩后(80 token):
客服 / 中文 / 准确 / 不编造 / 不确定时说明
摘要替代
原长文档(10K token):
[详细的产品说明书 ...]
压缩后(500 token):
产品 X 是 ...,主要功能 ...,价格 ...,规格 ...
可以用一个小 LLM(GPT-4o-mini / Claude Haiku)先生成摘要,节省主 LLM 的成本。
结构化重写
原(自然语言,200 token):
用户名是张三,他今年 30 岁,住在北京朝阳区,
是一名软件工程师,月薪 3 万元,已婚有一个孩子...
压缩后(80 token):
{
"name": "张三",
"age": 30,
"city": "北京朝阳",
"job": "软件工程师",
"salary": 30000,
"married": true,
"children": 1
}
LongLLMLingua(微软研究院)
用小模型给每个 token 打 perplexity 分
↓
删除"低信息量"的 token
↓
压缩比 5-20x,质量损失 5-10%
适用:
- 长 prompt 但内容冗余
- 不需要 100% 还原(有些损失可接受)
4. 多轮对话的累积成本
LLM 是无状态的——每次请求都要传完整历史
第 1 轮:u1 → a1 cost = u1 + a1
第 2 轮:u1+a1+u2 → a2 cost = (u1+a1+u2) + a2
第 3 轮:u1+a1+u2+a2+u3 → a3 cost = (u1+a1+u2+a2+u3) + a3
...
第 N 轮:cost = O(N²)
优化方法:
滑动窗口
保留最近 N 轮(如 5 轮)
丢弃更早的对话
适合:客服 / 短对话
缺点:丢失长期上下文
总结策略
第 10 轮时:
把前 8 轮 → 总结为 1 段(500 token)
prompt = 总结 + 后 2 轮 + new query
节省:原本 5000 token 历史 → 现在 500 + 2*query
关键事实提取
每轮对话后:
提取关键事实(用户偏好 / 上下文)→ 存外部存储
下次请求只带相关事实
例:
历史:用户讨论了房贷利率、提前还款、LPR 等
提取:{ topic: "房贷", focus: "提前还款", LPR_aware: true }
prefix cache + 历史
对话历史作为 prefix
新问题作为 suffix
prefix 命中缓存 → 只算新问题成本
5. token 计费的隐性成本
输入 vs 输出价格差异
Claude Sonnet 4:
输入 $3 / 1M
输出 $15 / 1M(5x)
GPT-4o:
输入 $2.5 / 1M
输出 $10 / 1M(4x)
含义:
写长答案比读长上下文贵
让 LLM "简洁回答" 能省钱
中文 token 密度
英文:1 词 ≈ 1.3 token
中文:1 字 ≈ 2-3 token
同等"信息量"中文 token 多 3-5 倍
实务:
能用英文交互的内部 prompt 用英文
用户面向的产品仍用中文
系统提示精简(中文 50 字 ≈ 100-150 token)
工具调用的 token
工具 definitions(input)
+ 工具调用参数(output)
+ 工具结果返回(input)
每次工具调用都增加 token
定义太多工具(如 20 个)
→ 每次请求都带定义 → 浪费
→ 优化:按场景动态加载工具
图片 / 多模态
GPT-4 Vision:
低质量图:85 token
高质量 1024×1024:765 token
不论图片多复杂都是固定 token 数
实务:
能用文字描述的不用图
能用低分辨率的不用高分辨率
实战架构建议
请求路径:
用户问题
↓
预处理(缓存 / 路由)
↓
─→ 简单问题 → 直接 LLM(无 RAG)
─→ 文档查询 → RAG 检索 → LLM
─→ 复杂任务 → Extended Thinking → LLM
─→ 多轮对话 → 历史总结 + prefix cache → LLM
↓
后处理
↓
返回用户
监控指标:
- 平均 input token / 请求
- 平均 output token / 请求
- prompt cache 命中率
- RAG 检索准确率
- 答案质量(人工评估 / LLM 评估)
- 成本 / 请求 / 用户
实战清单
✅ 必做:
- prompt cache 标记静态前缀
- 大文档用 RAG + 合理 chunk
- 系统提示精简
- 多轮对话滑动窗口 / 总结
- 监控 token 用量找优化点
❌ 避免:
- naive 把所有文档塞 prompt
- 系统提示长篇大论
- 不限 max_tokens 让 LLM 自由发挥
- 多轮对话不优化历史
- 中英混排不考虑 token 密度差异
LLM 长上下文是能力,但日常成本控制需要工程化——prefix cache、RAG、压缩是三大利器,组合用能把成本降到 5-10%。