LLM 长上下文的成本工程:prefix cache、chunk 检索、压缩策略

· 约 6 分钟 🧠 LLM Token 计数

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-k5-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 评估)
  • 成本 / 请求 / 用户

实战清单

必做

  1. prompt cache 标记静态前缀
  2. 大文档用 RAG + 合理 chunk
  3. 系统提示精简
  4. 多轮对话滑动窗口 / 总结
  5. 监控 token 用量找优化点

避免

  1. naive 把所有文档塞 prompt
  2. 系统提示长篇大论
  3. 不限 max_tokens 让 LLM 自由发挥
  4. 多轮对话不优化历史
  5. 中英混排不考虑 token 密度差异

LLM 长上下文是能力,但日常成本控制需要工程化——prefix cache、RAG、压缩是三大利器,组合用能把成本降到 5-10%。

❓ 常见问题

100 万 token 的上下文真的能用吗?为什么实际很贵?

能用,但"全用"和"省着用"的成本差 10-100 倍主流模型上下文限制(2026):(1) Claude Opus / Sonnet:200K token 标准,1M 扩展(部分版本);(2) GPT-4 Turbo / GPT-4o:128K;(3) Gemini Pro / Ultra:1M-2M;(4) DeepSeek / Qwen 长上下文版本:128K-1M。成本计算(以 Claude Sonnet 4 为例):(1) 输入约 $3 / 100 万 token;(2) 100 万 token 上下文 + 1000 token 输出 = $3.05/ 次;(3) 每次请求都重新发完整 100 万上下文 = 高频访问会很贵;(4) 1000 用户每天 10 次 = 30,500 次 / 月 = $93,000 / 月 仅输入成本!优化前后对比:(1) 不优化:$93,000;(2) prefix cache(命中 90%):$13,000;(3) RAG(只发相关 5K token):$465;(4) 优化得当能省 95%+。实务:(1) 别 naive 把全部文档塞 prompt;(2) 用 RAG / 缓存 / 压缩组合;(3) 长上下文是"能力上限",不是"日常用法"。

prompt cache / prefix cache 怎么工作?怎么用?

给重复的前缀打缓存,第二次发请求只算新增部分机制:(1) 把 prompt 前缀(如系统提示 + 文档)标记为可缓存;(2) 服务端 hash 前缀,存中间状态;(3) 第二次请求带相同前缀 → 直接复用,跳过重新计算;(4) 只对"新增的尾巴"按完整 token 价计费;(5) 前缀部分按缓存价计费(约 1/10 - 1/3 原价)。Anthropic prompt caching:(1) 写时(cache_control 标记):$3.75 / 100 万 token(贵 25%);(2) 读时(命中):$0.30 / 100 万 token(便宜 90%);(3) TTL 5 分钟(默认)/ 1 小时(可选)。OpenAI prompt caching(自动 2024 年起):(1) 自动开启,不需手动标记;(2) 命中:50% 折扣;(3) TTL 几分钟。最佳场景:(1) 同一文档反复问问题——文档作为 prefix;(2) 同一系统提示 + 不同用户输入;(3) few-shot 示例 + 不同问题。陷阱:(1) prefix 必须 完全一致——多一个空格不命中;(2) 缓存有 TTL 过期;(3) 必须在 prompt 前部 而不是中间。

RAG(检索增强生成)和长上下文,应该选哪个?

两者互补,不是二选一RAG:(1) 把大文档拆 chunk + 向量化存入数据库;(2) 用户问问题 → 检索最相关的 5-20 个 chunk;(3) 只把相关 chunk 拼到 prompt;(4) 总 prompt 通常 5-20K token。长上下文:(1) 整个文档(100K+)直接塞 prompt;(2) 模型自己"读懂"全文;(3) 不需要检索基础设施;(4) 总 prompt 可能 100K-1M token。对比:(1) 成本:RAG 低(只发相关 chunk);长上下文高(每次发全文);(2) 召回准确性:长上下文 > RAG(模型看全图);(3) 延迟:RAG 短(小 prompt 快);长上下文长(大 prompt 慢);(4) 可扩展性:RAG 强(向量库可无限扩);长上下文有上限。实务建议:(1) 文档 < 100K → 直接塞 prompt + prefix cache;(2) 文档 100K-1M → RAG + 长上下文混合(检索 chunk + 周围上下文);(3) 文档 > 1M → 必须 RAG + 多级检索;(4) 追求精度 → 长上下文 + 慢但准;(5) 追求成本 → RAG + 智能 chunk 设计。

RAG 的 chunk 怎么切分最好?大小多少合适?

关键变量:chunk 大小、重叠、语义分割chunk 大小:(1) 太小(< 200 token)—— 上下文不足,检索不到完整意思;(2) 太大(> 2000 token)—— 一个 chunk 里多个不同主题,检索精度低;(3) 甜点 500-1000 token——平衡上下文和精度。重叠(overlap):(1) 每个 chunk 与前一个有 10-20% 重叠;(2) 防止重要信息正好在切割边缘;(3) 例:chunk 500 token + 100 overlap = 第一段 1-500,第二段 401-900,依此类推。切分策略:(1) 固定长度 —— 简单但可能切断句子;(2) 句子边界 —— 在句号 / 段落处切;(3) 语义分割 —— 用 embedding 相似度检测主题变化;(4) 结构化切分 —— 标题、章节、列表项作为天然 chunk 边界。对中文:(1) 中文一个字 ≈ 2-3 token(英文 1 词 ≈ 1.3 token);(2) 500 token chunk ≈ 200 字;(3) 中文断句标点 多用 "。" "!" "?" 不要切在逗号处。实务:(1) 起步 500 token + 100 overlap + 句子边界;(2) 测试 query 命中率 / 答案质量;(3) 调整 chunk 大小和检索 top-k;(4) 文档结构清晰(如 API 文档)→ 用结构切分。

上下文压缩是什么?怎么压缩 prompt 而不丢信息?

用更短的表达保留关键信息几种策略:(1) 冗余去除 —— 删除示例 / 重复内容 / 不相关章节;(2) 摘要替代 —— 把长段落用摘要替代;(3) 结构化 —— 自然语言 → JSON / 表格 / 列表;(4) 关键词提取 —— 只保留重要实体和关系;(5) LLM 自压缩 —— 用一个小 LLM 把长文档压缩为短摘要。实测压缩比:(1) 普通自然语言 → 摘要:3-10x(保留 80% 关键信息);(2) 自然语言 → 结构化 JSON:1.5-3x;(3) 删除冗余:1.2-2x。LongLLMLingua / LLMLingua(微软):(1) 用小模型给每个 token 打 perplexity 分;(2) 删除"低信息量" token;(3) 压缩比可达 5-20x,质量损失 5-10%。实务:(1) 系统提示压缩 —— 把"请你扮演一个有耐心的客服..."缩短为"客服角色";(2) few-shot 例子压缩 —— 删掉冗余例子,留 2-3 个核心;(3) 文档摘要 —— 用 LLM 先生成摘要再放 prompt;(4) 结构化重写 —— "用户名是张三,他 30 岁" → {"name":"张三","age":30}陷阱:(1) 过度压缩损失关键信息;(2) 测试压缩前后答案质量是否下降;(3) 不要压缩 user query(用户原意必须保留)。

思考模式 / extended thinking 会增加成本吗?

会,但提升复杂任务质量机制:(1) Claude Extended Thinking / OpenAI o1 系列;(2) 模型在回答前"内部推理"——产生大量"思考 token";(3) 思考 token 不展示给用户但计费;(4) 适合数学 / 逻辑 / 编程 / 复杂推理。成本:(1) 思考 token 按输出 token 价格;(2) 简单任务可能多花 5-10x;(3) 复杂任务质量提升显著。实务建议:(1) 数学 / 逻辑 / 复杂编程 → 开启思考模式;(2) 简单问答 / 改写 / 翻译 → 关闭(直接回答);(3) A/B 测试 —— 看任务是否真需要思考;(4) 预算控制 —— 设置最大思考 token 数;(5) 缓存效果减弱 —— 思考过程不可缓存。Anthropic budget_tokens 参数:(1) 设置 max_tokens 为思考 + 回答总和;(2) 设置 thinking.budget_tokens 限制思考量;(3) 模型在 budget 内决定思考多深。

多轮对话的成本怎么控制?为什么对话越长越慢越贵?

每次请求都发完整对话历史机制:(1) LLM 是无状态的——每次请求都要传完整历史;(2) 第 1 轮:发 user1 → 收 assistant1,cost = user1 + assistant1;(3) 第 2 轮:发 user1+assistant1+user2 → 收 assistant2,cost = (user1+assistant1+user2) + assistant2;(4) 第 N 轮:发完整历史 + new query → 收新回答;(5) 累计成本是 关系(每轮都重发更多)。优化方法:(1) prefix cache——前面的对话作为 prefix,命中后只算 query;(2) 总结老对话 —— 第 10 轮时把前 8 轮总结成 1 段,prompt = 总结 + 后 2 轮 + new query;(3) 滑动窗口 —— 只保留最近 N 轮;(4) 关键事实记忆 —— 把对话中提取的关键事实存外部存储,每次只带相关事实;(5) 对话分段 —— 主题切换时开新对话。实务:(1) 客服 / 短对话 —— 滑动窗口 5-10 轮;(2) 长任务对话 —— 总结策略;(3) 代码助手 —— 关键上下文(当前文件)+ 总结历史;(4) 个人助理 —— 长期记忆(向量库)+ 当下对话。

token 计费的细节有哪些容易踩坑的?

几个容易忽略的点1. 输入 vs 输出价格不同 —— 输出通常是输入的 3-5 倍单价;(1) Claude Sonnet 4:输入 $3 / 输出 $15(5 倍);(2) GPT-4o:输入 $2.5 / 输出 $10(4 倍);(3) 写长答案比读长上下文贵很多。2. 中文比英文 token 多 —— 中文一个字 ≈ 2-3 token(英文一词 ≈ 1.3 token)→ 同等内容中文 token 多 3-5 倍。3. 系统提示也算 token —— 不要写超长系统提示。4. 工具调用 token —— tool definitions 也算 input;tool calls 算 output;tool results 算 input。5. 图片 / 多模态 —— 图片转 token 数固定(如 GPT-4 Vision 一张 1024×1024 = 765 token),不论实际复杂度。6. 响应被截断 —— 设置 max_tokens 太小 → 答案被截 → 仍然按已生成的 token 计费 → 还要重发再算一次。7. 失败重试 —— 超时 / 限流重试也按 token 计费。8. JSON 模式 token 多 —— 严格 JSON 输出会比自然文本多 token(多了引号 / 大括号)。实务:(1) 监控 token 使用量;(2) 设合理 max_tokens;(3) 中文优化(直接产出英文用户满意的话);(4) 定期 review 长 prompt 找压缩空间。

🧠 打开 LLM Token 计数 GPT/Claude/Gemini 多模型·实时分词可视化·上下文占用·费用估算·本地运行

📖 同一工具的其他教程