LLM 应用的 Token 成本工程:缓存、路由与网关的 5 个实战模式
约 24 分钟7147 字3 次阅读
LLM 应用的 Token 成本工程:缓存、路由与网关的 5 个实战模式
一篇写给「把 LLM 接进生产环境」的工程师的成本优化手册。
导言:从"能跑"到"能盈利"的距离
2025-2026 年,AI 应用的落地速度远超预期。几乎所有 SaaS 都在塞一个 Chat 框,几乎所有 Agent 都在跑多轮 LLM 调用。但一个更现实的问题也开始浮现:当你的日活从 1k 涨到 100k 时,你的 API 账单可能从 1k 美元涨到 100k 美元——而且在多数团队里,这条曲线不是线性的,是"指数级 + 抖动"的。
我在和几个 LLM 应用团队聊的时候发现一个共同现象:大家花了 80% 的精力优化 prompt 和模型选型,但只花了 20%(甚至更少)做"成本工程"。结果是,明明 prompt 已经很精炼了,月度账单还是失控。
这篇文章要解决的是另一半问题:当你已经用上最好的 prompt 和模型之后,怎么把每一美元 Token 的边际效用榨到最高。我会围绕 5 个实战模式展开:
- Prompt Caching——把"重复内容"的成本打到 1/10
- 模型路由(Model Routing)——把"该用便宜模型的请求"分流
- 语义缓存(Semantic Cache)——用 embedding 命中近似问题
- API Gateway 与 Pass-through 模式——把日志、重试、配额、审计交给一层
- Token Budgeting & 上下文工程——从根上限制 token 消耗
每节会给出可落地的代码或配置片段,并在最后一节给出"5 个模式的组合使用"。
数据基准(用于本文中的所有成本估算):
- Anthropic Claude 3.5 Sonnet:Input 15 / MTok(截至 2026-06 公开定价)
- Anthropic Claude 3 Haiku:Input 1.25 / MTok
- Anthropic Prompt Caching:Cache write 0.03 / MTok**(即相比 base input 价格 1/10)
- OpenAI GPT-4o / GPT-4o-mini 与 Anthropic 价差类似,核心结论通用
- 以上数据来自 Anthropic 官方 2024-12 公告与 2026-06 pricing 页面
- LiteLLM 50,104★、LangChain 139,087★、MLflow 26,451★(GitHub 2026-06-12 实时拉取)
一、Prompt Caching:把"重复前缀"打成 1/10
1.1 原理
几乎所有 LLM 应用的 prompt 都有这种结构:
[system prompt + few-shot examples + 工具定义] ← 几乎不变
[用户问题 + 检索到的上下文] ← 每次都变
[历史对话] ← 累加
后两段会变,但第一段——也就是"长前缀"——在 80% 的请求里是几乎一字不差的。Prompt Caching 的核心思想就是:把"前缀"标记成 cache,让 API 厂商在前缀不变时按 1/10 价格计费。
Anthropic 2024-12-17 上线 Prompt Caching 时给出的官方数据是 "成本降低 90%、长 prompt 延迟降低 85%"(来源:anthropic.com/news/prompt-caching)。对 Haiku 来说,cache_read 价格是 0.25 / MTok——8.3 倍的差价。
OpenAI 紧随其后在 2024-10 给 GPT-4o 加了类似机制(来源:openai.com/index/api-prompt-caching),按命中自动打 50% 折扣,不需要手动标注。两家路线略有差异,下面会展开。
1.2 Anthropic 路线:显式 cache_control 断点
Anthropic 要求你显式在内容块上加 cache_control 标记,指定"从哪个 token 开始是 cache 区域"。
import anthropic
client = anthropic.Anthropic()
LONG_SYSTEM = "...(5000 token 的工具定义 + few-shot)" * 1 # 稳定的前缀
resp = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system=[
{
"type": "text",
"text": LONG_SYSTEM,
"cache_control": {"type": "ephemeral"} # ← 关键
}
],
messages=[{"role": "user", "content": user_input}]
)
print(resp.usage) # cache_creation_input_tokens / cache_read_input_tokens
关键参数:
ephemeralcache 默认 TTL 5 分钟,可以配到 1 小时- 最小 cache 单元 1024 token(Sonnet/Opus),2048 token(Haiku)
- 命中条件:前缀完全相同(字节级),且至少 1024 token
1.3 OpenAI 路线:自动隐式 cache
OpenAI 走的是另一个方向——不显式标记,由服务端自动检测前缀匹配:
- Cache 命中窗口:5-10 分钟(最近一次响应的前缀)
- 最小 token:1024
- 折扣:cache 命中 token 收 50% 价格
- 限制:只对长度超过 1024 token 的前缀生效
这种"零配置"路线对小团队很友好,但控制粒度差——你没法精确控制"哪一段应该 cache",而 Anthropic 路线可以。
1.4 一个真实账单的算术
假设一个客服 Agent 应用:
| 指标 | 数值 |
|---|---|
| 日活对话 | 50,000 |
| 平均每轮 system + tool 长度 | 6,000 token |
| 平均每轮 user + history | 1,500 token |
| 平均 output | 500 token |
| Sonnet 价格 | 15 / MTok out |
不用 cache(只用 Sonnet):
- Input: 50,000 × 6,000 = 300M token → $900 / 天
- Output: 50,000 × 500 = 25M token → $375 / 天
- 小计 38,250 / 月
用 cache(假设 70% 命中):
- Cache write(首次):50,000 × 6,000 = 300M token → $90 / 天
- Cache read(70% 命中):50,000 × 0.7 × 6,000 = 210M token → $6.3 / 天
- Non-cache(30%):50,000 × 0.3 × 6,000 = 90M token → $270 / 天
- Output:$375 / 天
- 小计 22,239 / 月
省下 42%,**0.30/MTok 远低于 base $3/MTok),不是"完全免费"。
1.5 落地的几条铁律
- 必须"前缀稳定"——你不能在 system prompt 里塞
current_time这种每次都变的值,否则永远命中不了 - 慎用拼接 string 的方式——把可变部分放在 cache 区域"后面",让稳定前缀尽量长
- 监控 cache hit rate——Anthropic 响应里
cache_read_input_tokens / (cache_creation_input_tokens + cache_read_input_tokens)是核心 KPI,目标 > 60% - 配合 streaming——cache 也享受 streaming,首 token 延迟能砍 85%
- 不要 cache 用户隐私内容——cache 是按 prefix 字节级匹配的,敏感字段要么脱敏、要么放在 cache 区域之后
二、模型路由:让"该便宜的地方便宜"
2.1 一个反直觉的事实
很多团队有一个"用最好模型"的执念。但如果你看真实 LLM 应用的请求分布:
- 30%-50% 的请求是"打招呼、闲聊、简单问答"——Haiku 4 / GPT-4o-mini 就能搞定
- 20%-30% 是"中等难度"——Sonnet / GPT-4o 即可
- 只有 10%-20% 是"真需要最强模型"——Opus / o-series
用 Sonnet 一把梭的账单 vs 三档路由的账单,差 5-8 倍。
2.2 路由策略的 4 个维度
| 维度 | 例子 | 适用 |
|---|---|---|
| 关键词/规则 | "如果用户问天气、订外卖、查快递 → Haiku" | 简单业务分流 |
| 意图分类器 | 用一个小模型先分类,再分发 | 业务复杂但模式固定 |
| 成本约束 | "这个会话已花 $0.50 → 下次用更便宜模型" | 长对话 |
| 质量/置信度 | "主模型 confidence < 0.6 → escalate" | 严谨任务(代码、SQL、合同) |
2.3 一个 LiteLLM 路由配置
LiteLLM(GitHub 50,104★,2026-06-12 实时数据)是当前最流行的多模型代理库之一。它支持声明式 routing 配置:
# litellm_config.yaml
model_list:
- model_name: gpt-4o
litellm_params: { model: openai/gpt-4o, api_key: os.environ/OPENAI_API_KEY }
- model_name: gpt-4o-mini
litellm_params: { model: openai/gpt-4o-mini, api_key: os.environ/OPENAI_API_KEY }
- model_name: claude-haiku
litellm_params: { model: anthropic/claude-3-haiku-20240307, api_key: os.environ/ANTHROPIC_API_KEY }
router_settings:
routing_strategy: usage-based-routing-v2
num_retries: 2
timeout: 30
redis_host: os.environ/REDIS_HOST
redis_port: 6379
cache: true
cache_params:
type: redis
ttl: 3600
from litellm import Router
router = Router(config_file="litellm_config.yaml")
# 自动按 cost / latency / 失败率路由
resp = router.completion(
model="gpt-4o-mini", # 指定"任务档位"
messages=[{"role": "user", "content": user_input}]
)
LiteLLM 内置 7 种 routing strategy:simple-shuffle、usage-based、latency-based、cost-based、usage-based-routing-v2 等。生产里最常用的是 usage-based-routing-v2——按 RPM/TPM 限额自动切到次选模型。
2.4 选型速查
| 任务 | 推荐档位 | 备注 |
|---|---|---|
| 闲聊 / 问候 / FAQ | Haiku / GPT-4o-mini | $0.25 / MTok |
| 简单摘要 / 改写 | Haiku / GPT-4o-mini | 同上 |
| 中等推理 / RAG 回答 | Sonnet / GPT-4o | $3 / MTok |
| 长文分析 / 复杂规划 | Opus / o3 | $15 / MTok |
| 代码生成 / 重构 | Sonnet (claude-sonnet-4-x) | 性价比最佳 |
| 复杂数学 / 多步推理 | o-series / Claude Opus 4 | 别省 |
注意:以上价格为 2026-06 公开定价快照;Anthropic / OpenAI 都会频繁调价,生产环境必须以你 dashboard 上的实际数字为准。
2.5 容易踩的坑
- 不要在 routing 里写死
if-else——业务一改你就改 50 个地方;用配置驱动 - 不要用 GPT-4 给"is this user input a question?"分类——用 haiku 干这个活,省 100 倍
- 降级到便宜模型后要 monitor 质量——有个 eval suite 在跑,定期抽样
- 不要给"严肃任务"(合同审阅、医疗)降级——这部分成本就别省了
三、语义缓存:embedding 命中近似问题
3.1 Prompt Caching 的盲区
Prompt Caching 只对"字节级完全相同的前缀"有效。但实际上,用户问的问题往往是近似但不完全相同:
- "怎么重置密码?"
- "忘记密码怎么办?"
- "登录不上去,密码错了"
- "如何修改我的登录密码?"
这 4 句话语义相同,但用 prompt cache 一点都命中不了。这时候就需要语义缓存(Semantic Cache)。
3.2 核心机制
用户问题 → embedding → 向量库查 top-1 相似度
│
┌───────────────┴───────────────┐
│ sim > threshold (e.g. 0.92) │
↓ ↓
直接复用上一次的回答 走正常 LLM 流程
关键参数:
- embedding 模型:
text-embedding-3-small($0.02/MTok) 或开源bge-large - 向量库:Redis(已支持 vector search)/ Pinecone / Qdrant / Milvus
- 相似度阈值:0.85-0.95,越高越严格
- TTL:视业务而定,FAQ 类设长一点(24h),时效性强的设短(10min)
3.3 GPTCache / Cloudflare AI Gateway
两个现成方案:
GPTCache(开源)
from gptcache import cache
from gptcache.adapter.api import init_similar_cache
from langchain.chat_models import ChatOpenAI
from langchain.globals import set_llm_cache
set_llm_cache(init_similar_cache(cache.init()))
# 之后的 LangChain 调用就自动带语义缓存
llm = ChatOpenAI(model="gpt-4o-mini")
llm.predict("怎么重置密码?")
llm.predict("忘记密码怎么办?") # 命中 cache,不再调用 LLM
Cloudflare AI Gateway(托管)
Cloudflare AI Gateway 把"caching"和"rate limiting"作为 core feature 免费提供(来源:developers.cloudflare.com/ai-gateway/pricing),不需要自己架 Redis:
curl https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/openai/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "cf-cache-ttl: 3600" \
-H "cf-similarity-threshold: 0.85" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"怎么重置密码?"}]}'
关键卖点:Cloudflare AI Gateway "inference pricing from providers is passed through with no markup"(同上来源)——只收缓存/日志基础设施的钱,token 走的是原始 API 价格。这一条对中小团队来说非常关键:你可以白嫖一层基础设施而不会因此多付 token 钱。
3.4 语义缓存的雷区
- 不要给"个性化"问题开 cache——"我的订单 #12345 到哪了"这种带 session 信息的不能 cache
- 不要给"时效性"问题开 cache——"今天北京天气" 24h 内应该重算
- 阈值定高了——假阴性多,省不了钱;阈值定低了——假阳性多,用户看到 24h 前的答案。强烈建议做 A/B 测试
- 需要定期 sweep——某些答案可能因模型升级而过时,cache 要有过期机制
- embedding 本身也是钱——别用
text-embedding-3-large来算 cache key,省出来的 token 不够付 embedding 的
四、API Gateway:把"运维层"从应用里抽出来
4.1 为什么需要 Gateway
当你的 LLM 应用超过 3-5 个下游模型/路由/重试/限流/日志规则时,在应用代码里维护它们就是一个灾难。典型痛点:
- 想给 Anthropic 加 retry-with-backoff → 散在 30 个
.py文件里 - 想换 OpenAI 的 base URL 走代理 → 30 个地方都要改
- 想加"按用户 ID 限流" → 每个调用点都要加 wrapper
- 想统一打 observability log → 每个调用点都得记得 log
API Gateway 的核心价值:把"调用 LLM 之前/之后"的横切关注点(cross-cutting concerns)从应用代码里抽出去。
4.2 三层 Gateway 选型
| 层级 | 代表 | 形态 | 适合 |
|---|---|---|---|
| 客户端库层 | LiteLLM、LangChain | import 一次就生效 | 快速接入多模型 |
| 自托管代理层 | LiteLLM Proxy、Portkey、MLflow | 独立服务,统一 URL | 多团队、多应用 |
| 托管服务层 | Cloudflare AI Gateway、Portkey Cloud | 不用自己运维 | 中小团队、想 focus 业务 |
4.3 一个 LiteLLM Proxy + 监控的最小部署
# 安装
pip install 'litellm[proxy]' prometheus_client
# config.yaml (前面路由一节里的)
# 启动
litellm --config /path/to/litellm_config.yaml --port 4000
应用代码接入:
import openai
client = openai.OpenAI(
base_url="http://litellm.internal:4000", # 所有请求走这里
api_key="anything" # 真实 key 在 proxy 里
)
resp = client.chat.completions.create(
model="claude-haiku", # proxy 自动路由到 Anthropic
messages=[...]
)
这一行 base_url 切换就把:
- 重试
- 限流
- 缓存
- 路由
- 日志
- 配额
- 成本统计
全部接住了。强烈建议 5 个以上 LLM 调用点的团队就上 proxy。
4.4 Cloudflare AI Gateway 的一行接入
如果不想自己运维代理,Cloudflare AI Gateway 几乎是零成本方案(来源:同上 pricing 页面,caching & rate limiting 是 free core feature):
import openai
client = openai.OpenAI(
base_url="https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/openai",
api_key=os.environ["OPENAI_API_KEY"],
default_headers={"cf-aig-metadata": json.dumps({"user_id": user_id})}
)
一行 base_url 切换 + 一个 metadata header,就有:
- 全量请求日志
- 按 user / model / route 的成本分摊
- Rate limit(per-user)
- Cache + semantic cache
- DLP(敏感信息扫描,免费)
对成本敏感但又不想自己搭基础设施的团队,托管 Gateway 是最优解。
五、Token Budgeting & 上下文工程:从根上省
5.1 一个反直觉的数字
如果你把一个 LLM 应用的 token 消耗画成直方图,80% 的 token 用在了 20% 的请求上——通常是:
- 长上下文 RAG 检索:为了"完全性"塞 50k token 进去,但实际只用 5k
- 多轮对话 history:每轮都拼上去,10 轮后突破 20k
- Few-shot examples:为了"质量"塞 30 个 example,每个 1k token = 30k
前端不控制,缓存和路由都救不回来。
5.2 三条治理手段
1. RAG 上下文"硬上限"
def retrieve(query: str, k: int = 5, max_tokens: int = 4000):
docs = vector_db.search(query, k=k*2) # 多取
out, total = [], 0
for d in docs:
t = count_tokens(d.text)
if total + t > max_tokens:
break
out.append(d)
total += t
return out
核心理念:不要用 "k=5" 硬编码,要用"max_tokens=N"硬上限。同一个 query 检索 5 个 500 token 的 doc 和 5 个 1500 token 的 doc,成本差 3 倍。
2. 对话历史"压缩/截断"
def compress_history(messages, max_tokens=8000):
if count_tokens(messages) <= max_tokens:
return messages
# 策略 A:保留 system + 最近 5 轮 + summary(老的)
# 策略 B:让 LLM 先把老 history 总结成 200 token
old = messages[:-5]
summary = client.messages.create(
model="claude-haiku-4-5",
max_tokens=200,
messages=[{"role": "user", "content": f"Summarize in 200 tokens: {old}"}]
).content[0].text
return [
messages[0], # system
{"role": "assistant", "content": f"[Earlier summary]: {summary}"},
*messages[-5:]
]
注意:summary 这步本身也要花钱,但 haiku 4.5 比 sonnet 便宜 12 倍,省钱+省 token 双重收益。
3. Few-shot 动态选择
def few_shot_prompt(query, examples_pool, k=3):
# 不用"全 30 个 example",用 query 相似度选 top-3
sims = embed(query) @ embed_matrix(examples_pool).T
top_k = np.argsort(-sims)[:k]
return [examples_pool[i] for i in top_k]
这个套路能从 30k token 砍到 3k,直接省 90% input cost——而且很多 benchmark 显示质量几乎不掉。
5.3 上下文工程的"成本视角"
把上面三条和 Anthropic 提出的 Context Engineering(参考 Anthropic 2024-09 文章)结合起来,核心是"动态决定哪些上下文值得放进去",而不是"放进所有能想到的东西"。
LLM 应用的每个环节都该有 token budget:
| 环节 | 建议 budget(示例) |
|---|---|
| system prompt | 1-2k |
| 工具定义 | 1-2k |
| few-shot examples | 1-3k |
| RAG 检索 | 2-5k |
| 对话历史 | 2-8k |
| 用户问题 | 0.5-1k |
| 总计 input | 8-20k |
| output | 0.5-2k |
经验值:一个健康的 LLM 应用,平均 input 应该在 5-15k token 之间。超过 30k 的几乎一定有优化空间。
六、组合拳:一个 5 万 QPS 应用的实战架构
最后把 5 个模式拼成一张架构图:
图表加载中…
5 个模式的协同:
- Cloudflare AI Gateway 入口——统一日志 / 配额 / 重试
- Semantic Cache——30-50% 的相似问题直接返回,省 token
- Model Router——按 query 难度分 3 档,默认走 Mini
- Prompt Caching——长 system + tool 定义在 LLM API 这一层 cache
- Token Budgeting——RAG 检索 / 对话 history / few-shot 都有上限
真实账单对比(50k QPS、Sonnet 一把梭 vs 5 模式组合):
- 一把梭:约 $38k / 月
- 5 模式组合:约 $7-9k / 月
- 降本 75-80%
七、5 条工程守则
把全文浓缩成 5 条工程守则:
- 不要"模型一把梭"——路由能省 5-8 倍成本,先评估哪些请求可以降级
- 能 cache 一定 cache——prompt cache + semantic cache 双层,是最直接的杠杆
- token budget 在源头控制——RAG / history / few-shot 都要有 max_tokens 硬上限
- API Gateway 必上——超过 3 个 LLM 调用点就该用 LiteLLM Proxy 或 Cloudflare AI Gateway
- 每个月初跑一遍成本审计——把"哪些 prompt 用了最多 token"列出来,逐个优化
不要做的事:
- ❌ 把"max_tokens"设成 4096 但实际只用 500
- ❌ 在 system prompt 里塞当前时间、随机数
- ❌ 给所有用户无差别路由到最强模型
- ❌ 把"secret" / "API key" 写在 system prompt 里
- ❌ 假设 embedding 的钱可以忽略
总结
LLM 应用的"成本工程"和传统软件的性能优化逻辑完全一致:
- 缓存(prompt cache + semantic cache)
- 路由(按任务难度选模型)
- 网关(cross-cutting concerns 抽离)
- 预算(token budget 在源头控制)
- 监控(cache hit rate / 路由分布 / 实际账单)
把这 5 个模式的成本节省加在一起,75-80% 是完全现实的。对一个一年烧 750k——这钱够再雇 5 个工程师。
但最重要的是:成本优化不能等到账单失控才开始。等老板问你"为什么这个月多了 $50k"时再回头看,已经晚了。
下一步行动(如果你刚开始):
- 先在 dashboard 上看上个月的真实账单,按 model 拆解
- 给最贵的 1-2 个调用点加 prompt caching(5 行代码)
- 给"闲聊"和"重排序"这种场景换 Haiku
- 上一个 LiteLLM Proxy 或 Cloudflare AI Gateway,1 小时内能看到全量调用分布
- 重复
参考资料
- Anthropic, Prompt caching is now available on the Anthropic API, 2024-12-17. https://www.anthropic.com/news/prompt-caching
- Anthropic, Effective context engineering for AI agents, 2024-09. https://www.anthropic.com/news/context-engineering
- OpenAI, Prompt caching in the API, 2024-10. https://openai.com/index/api-prompt-caching/
- Cloudflare, AI Gateway Pricing, accessed 2026-06. https://developers.cloudflare.com/ai-gateway/pricing/
- BerriAI, litellm GitHub repository, 50,104★ as of 2026-06-12. https://github.com/BerriAI/litellm
- LangChain, langchain GitHub repository, 139,087★ as of 2026-06-12. https://github.com/langchain-ai/langchain
- MLflow, mlflow GitHub repository, 26,451★ as of 2026-06-12. https://github.com/mlflow/mlflow
- Portkey, AI Gateway & Observability platform. https://portkey.ai/
本文 GitHub Star 数与定价数据均为 2026-06-12 实时拉取快照。模型定价、API 政策、缓存 TTL 等关键参数会随厂商更新而变化,生产环境实施前请以官方文档为准。