随着AI应用大规模上线,LLM API成本正在成为很多团队的"隐形杀手"。本文从工程实践角度,系统梳理2026年最有效的LLM成本优化技术,帮助团队在不牺牲质量的前提下,将API账单降低50%-80%。
一、成本分析:钱都花在哪里了
在优化之前,先搞清楚成本结构。LLM API的费用通常由两部分构成:
- Input Token成本:发给模型的所有内容(系统提示+历史对话+用户输入)
- Output Token成本:模型返回的内容(通常是Input的2-5倍单价)
对于典型的AI应用,成本分布大致如下:
系统提示(重复发送): 20-40%
历史对话(累积增长): 30-50%
用户输入: 10-20%
模型输出: 15-30%
可以看到,系统提示和历史对话的重复传输是最大的浪费源。
二、Prompt Caching:最高ROI的优化手段
2.1 什么是Prompt Caching
Anthropic和OpenAI均已推出Prompt Caching功能——对于重复传输的前缀内容(如系统提示),只在第一次调用时计费,后续缓存命中时以极低价格(Anthropic为原价的10%)复用。
节省潜力:对于系统提示占比高的应用,Prompt Caching可直接将总成本降低30-60%。
2.2 Claude的缓存实现
import anthropic
client = anthropic.Anthropic()
# 构建可缓存的系统提示
SYSTEM_PROMPT = """你是一个专业的代码审查助手,具备以下能力:
1. 识别代码中的安全漏洞(XSS、SQL注入、权限越界等)
2. 检查代码规范(PEP8、代码复杂度、命名规范)
3. 发现性能问题(N+1查询、内存泄漏、算法复杂度)
4. 给出可执行的重构建议
你的回答应当:
- 按严重程度排序(Critical > High > Medium > Low)
- 给出具体的代码示例
- 说明问题的影响范围
""" * 10 # 假设是个很长的系统提示(>1024 tokens才有缓存价值)
def review_code_with_cache(code: str) -> str:
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=2000,
system=[
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"} # 标记为可缓存
}
],
messages=[{"role": "user", "content": f"请审查以下代码:\n```python\n{code}\n```"}]
)
# 查看缓存命中情况
usage = response.usage
print(f"缓存读取tokens: {usage.cache_read_input_tokens}")
print(f"缓存写入tokens: {usage.cache_creation_input_tokens}")
return response.content[0].text
2.3 OpenAI的自动缓存
OpenAI的Prompt Caching是自动生效的——只要请求的前缀相同(长度>1024 tokens),就会自动命中缓存:
from openai import OpenAI
client = OpenAI()
# 保持系统提示不变,OpenAI会自动缓存
def call_with_auto_cache(user_message: str) -> str:
response = client.chat.completions.create(
model="gpt-6",
messages=[
{"role": "system", "content": LONG_SYSTEM_PROMPT}, # 保持不变
{"role": "user", "content": user_message}
]
)
# 检查缓存情况
usage = response.usage
if hasattr(usage, 'prompt_tokens_details'):
cached = usage.prompt_tokens_details.cached_tokens
print(f"缓存命中: {cached} tokens")
return response.choices[0].message.content
关键实践:系统提示放在messages列表的最前面,并保持内容固定,这样最大化缓存命中率。
三、模型路由:用小模型处理简单任务
并不是所有任务都需要最强的模型。通过智能路由,将简单任务分发给轻量级模型:
import re
from dataclasses import dataclass
from typing import Callable
@dataclass
class ModelConfig:
name: str
cost_per_1k_input: float # 美元
cost_per_1k_output: float
max_context: int
MODELS = {
"nano": ModelConfig("gpt-4o-mini", 0.00015, 0.0006, 128000),
"standard": ModelConfig("claude-sonnet-4-6", 0.003, 0.015, 200000),
"pro": ModelConfig("claude-opus-4-7", 0.015, 0.075, 200000),
}
class CostAwareRouter:
"""基于复杂度的成本感知路由器"""
def route(self, prompt: str, task_type: str) -> str:
complexity = self._estimate_complexity(prompt, task_type)
if complexity < 0.3:
return "nano" # 简单问答、格式转换
elif complexity < 0.7:
return "standard" # 一般分析、代码生成
else:
return "pro" # 复杂推理、架构设计
def _estimate_complexity(self, prompt: str, task_type: str) -> float:
score = 0.0
# 基于任务类型的基础复杂度
base_scores = {
"qa": 0.2, # 简单问答
"summary": 0.3, # 摘要
"translation": 0.2, # 翻译
"code_review": 0.6, # 代码审查
"architecture": 0.9, # 架构设计
"reasoning": 0.8, # 复杂推理
}
score = base_scores.get(task_type, 0.5)
# 基于prompt长度调整(更长的prompt通常更复杂)
token_estimate = len(prompt) / 4
if token_estimate > 2000:
score = min(1.0, score + 0.2)
# 检测是否包含代码
if "```" in prompt or re.search(r'\bdef\b|\bclass\b|\bfunction\b', prompt):
score = min(1.0, score + 0.1)
return score
# 使用示例
router = CostAwareRouter()
def smart_complete(prompt: str, task_type: str) -> str:
model_tier = router.route(prompt, task_type)
model = MODELS[model_tier]
print(f"路由到: {model.name}(预计成本: ${len(prompt)/4 * model.cost_per_1k_input / 1000:.4f})")
return call_model(model.name, prompt)
四、上下文压缩:减少历史对话的Token消耗
多轮对话中,历史消息会快速累积。通过智能压缩策略减少冗余:
class ConversationCompressor:
"""对话历史压缩器"""
def __init__(self, max_tokens: int = 4000, compression_model: str = "gpt-4o-mini"):
self.max_tokens = max_tokens
self.compression_model = compression_model
def compress(self, messages: list) -> list:
"""压缩历史对话,保留最重要的信息"""
total_tokens = self._estimate_tokens(messages)
if total_tokens <= self.max_tokens:
return messages # 无需压缩
# 策略1:保留最近的N条消息(滑动窗口)
if total_tokens < self.max_tokens * 2:
return self._sliding_window(messages, self.max_tokens)
# 策略2:摘要式压缩(成本更低,信息保留更好)
return self._summarize_compress(messages)
def _sliding_window(self, messages: list, max_tokens: int) -> list:
"""保留最近的消息,直到不超过token限制"""
result = []
tokens = 0
for msg in reversed(messages):
msg_tokens = self._estimate_tokens([msg])
if tokens + msg_tokens > max_tokens:
break
result.insert(0, msg)
tokens += msg_tokens
return result
def _summarize_compress(self, messages: list) -> list:
"""将旧消息压缩为摘要"""
# 保留最近10条消息原文
recent = messages[-10:]
old = messages[:-10]
if not old:
return recent
# 用小模型压缩旧消息
summary_prompt = f"请用200字以内总结以下对话的关键信息:\n{self._format_messages(old)}"
summary = call_model(self.compression_model, summary_prompt)
summary_msg = {
"role": "system",
"content": f"[之前对话摘要] {summary}"
}
return [summary_msg] + recent
五、批处理与异步调用:提升吞吐、降低成本
OpenAI提供Batch API,对于非实时任务,批处理可以直接降低50%费用:
import json
from openai import OpenAI
client = OpenAI()
def batch_process_articles(articles: list[str]) -> list[str]:
"""批量处理文章摘要(非实时任务,使用Batch API节省50%成本)"""
# 准备批处理请求
requests = []
for i, article in enumerate(articles):
requests.append({
"custom_id": f"article-{i}",
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": "gpt-4o",
"max_tokens": 200,
"messages": [
{"role": "system", "content": "请为以下文章生成100字摘要"},
{"role": "user", "content": article}
]
}
})
# 写入JSONL文件
with open("batch_requests.jsonl", "w") as f:
for req in requests:
f.write(json.dumps(req) + "\n")
# 提交批处理任务
with open("batch_requests.jsonl", "rb") as f:
batch_file = client.files.create(file=f, purpose="batch")
batch = client.batches.create(
input_file_id=batch_file.id,
endpoint="/v1/chat/completions",
completion_window="24h" # 24小时内完成(对比实时调用节省50%)
)
print(f"批处理任务已提交: {batch.id}")
return batch.id
六、成本监控与预算控制
import time
from collections import defaultdict
class CostTracker:
"""实时成本追踪器"""
def __init__(self, daily_budget: float = 10.0):
self.daily_budget = daily_budget
self.daily_cost = 0.0
self.cost_by_model = defaultdict(float)
self.cost_by_feature = defaultdict(float)
self.reset_time = time.time() + 86400 # 24小时后重置
def track(self, model: str, input_tokens: int, output_tokens: int, feature: str = "default"):
"""记录一次调用的成本"""
cost = self._calculate_cost(model, input_tokens, output_tokens)
self.daily_cost += cost
self.cost_by_model[model] += cost
self.cost_by_feature[feature] += cost
# 预算告警
if self.daily_cost > self.daily_budget * 0.8:
self._alert(f"警告:今日AI费用已达预算的80%(${self.daily_cost:.2f}/${self.daily_budget:.2f})")
if self.daily_cost > self.daily_budget:
raise BudgetExceededError(f"今日AI费用超出预算:${self.daily_cost:.2f} > ${self.daily_budget:.2f}")
return cost
def report(self) -> dict:
return {
"daily_cost": self.daily_cost,
"budget_remaining": self.daily_budget - self.daily_cost,
"top_models": sorted(self.cost_by_model.items(), key=lambda x: x[1], reverse=True)[:5],
"top_features": sorted(self.cost_by_feature.items(), key=lambda x: x[1], reverse=True)[:5],
}
七、成本优化效果对比
| 优化手段 | 适用场景 | 预期节省 | 实现复杂度 |
|---|---|---|---|
| Prompt Caching | 系统提示>1K tokens | 30-60% | 低 |
| 模型路由 | 混合复杂度任务 | 40-70% | 中 |
| 上下文压缩 | 多轮对话 | 20-50% | 中 |
| Batch API | 非实时任务 | 50% | 低 |
| 输出长度控制 | 所有场景 | 10-30% | 低 |
| 综合应用 | 生产系统 | 60-80% | 中 |
八、总结
LLM成本优化不是一次性工作,而是需要持续监控和迭代的工程实践。核心策略:
- 先量化:建立成本追踪,搞清楚钱花在哪
- Caching优先:最低成本、最高收益的优化
- 模型分级路由:简单任务用小模型
- 压缩历史上下文:避免无效Token累积
- 批处理非实时任务:直接节省50%
- 持续监控:设置预算告警,防止成本失控
综合运用这些手段,将AI应用的API账单降低70%是完全可实现的工程目标。