每月省下 70% 的 AI API 费用?我是这样做到的
本文从实际项目出发,分享一套可落地的 AI API 成本控制方法论。不讲虚的,全是能直接用的技巧。
写在前面
用 AI API 做开发的人,大概都经历过这个阶段:
月初看账单,心想「也不贵嘛」。月底一看,GPT-4 单月跑了 $200+,心里一紧。
更难受的是,你根本说不清钱花在哪了。是哪个接口调用频率太高?是 prompt 写得太长?还是哪个用户在薅羊毛?
这篇文章就是聊这个问题。我会从成本构成分析 → 具体优化策略 → 实战工具推荐,完整过一遍。
一、先搞明白:AI API 的钱到底花在哪?
要降成本,先要知道成本来自哪里。
主流大模型的计费方式都是 Token 定价,分 Input Token 和 Output Token 两部分:
| 模型 | Input (per 1M tokens) | Output (per 1M tokens) |
|---|---|---|
| GPT-4o | $2.50 | $10.00 |
| GPT-4o mini | $0.15 | $0.60 |
| Claude 3.5 Sonnet | $3.00 | $15.00 |
| Claude 3 Haiku | $0.25 | $1.25 |
| Gemini 1.5 Pro | $1.25 | $5.00 |
| Gemini 1.5 Flash | $0.075 | $0.30 |
关键结论:Output Token 通常比 Input Token 贵 3-6 倍。 这意味着让模型「废话少说」往往比压缩 prompt 更有价值。
典型的成本失控场景:
- System Prompt 过长:每次请求都带着 2000 token 的 system prompt,日积月累是一笔大开销
- 没有做路由:所有请求都打到最贵的模型,即使是「查今天天气」这种简单任务
- 没有缓存:相同的 prompt 反复调用,每次都付全价
- 输出没有限制:没有设置
max_tokens,让模型随意生成
二、七个实战优化策略
策略一:模型路由——让对的模型做对的事
这是单次收益最大的优化。
核心思路:不同复杂度的任务用不同等级的模型。
简单任务(分类、提取、格式化)→ Flash/Haiku/mini 级别
中等任务(摘要、翻译、问答)→ Sonnet/4o 级别
复杂任务(推理、代码生成、长文写作)→ Opus/o1/Pro 级别
以一个客服机器人为例,对话分类可以这样做:
import anthropic
def classify_complexity(user_input: str) -> str:
"""用最便宜的模型判断问题复杂度"""
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-3-haiku-20240307", # 最便宜的 Claude,够用了
max_tokens=10,
messages=[{
"role": "user",
"content": f"""判断以下问题的复杂度,只回答 simple/medium/complex:
问题:{user_input}"""
}]
)
return response.content[0].text.strip()
def smart_chat(user_input: str) -> str:
complexity = classify_complexity(user_input)
model_map = {
"simple": "claude-3-haiku-20240307",
"medium": "claude-3-5-sonnet-20241022",
"complex": "claude-3-opus-20240229"
}
model = model_map.get(complexity, "claude-3-5-sonnet-20241022")
# ... 实际调用逻辑
实测效果:一个日均 10 万次调用的应用,引入路由后月度账单从 $1,200 降到 $380,降幅 68%。
策略二:Prompt 瘦身——每个 Token 都有价格
Prompt 优化有几个方向:
1. 去掉客套话
❌ 冗余写法:
你是一个非常专业的、经验丰富的文案写作助手,你擅长撰写各种类型的营销文案,
请根据用户的需求,以专业且友好的态度,帮助用户完成以下任务:
将下面这段产品描述改写为适合朋友圈发布的文案。
✅ 精简写法:
将以下产品描述改写为朋友圈文案(100字以内,口语化):
token 数:约 100 → 约 30,省 70%。
2. 用结构化格式替代自然语言描述
❌ 自然语言式:
请你分析这个用户反馈,找出用户的主要情绪是正面还是负面的,
同时提取出用户提到的具体产品功能点,并给出一个0到10的满意度评分
✅ 结构化指令(更短 + 输出更可控):
分析用户反馈,输出 JSON:
{"sentiment": "positive|negative|neutral", "features": [], "score": 0-10}
3. Few-shot 示例要精简
Few-shot 示例是 prompt 膨胀的大户。规则:
- 每个示例 ≤ 3 个
- 示例本身要简短,能说明格式就够了
- 复杂场景考虑 fine-tuning 替代 few-shot
策略三:语义缓存——相同的问题不付两次钱
普通缓存是 key-value 完全匹配,对 AI 请求来说命中率极低(用户的表达方式千变万化)。
语义缓存的思路:把 prompt embedding 向量化,找相似度 > 阈值的历史请求直接返回缓存结果。
import numpy as np
from openai import OpenAI
client = OpenAI()
cache = {} # 生产环境用 Redis + 向量数据库
def get_embedding(text: str) -> list[float]:
response = client.embeddings.create(
model="text-embedding-3-small", # 极便宜,1M token 仅 $0.02
input=text
)
return response.data[0].embedding
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def cached_completion(prompt: str, threshold: float = 0.95) -> str:
embedding = get_embedding(prompt)
# 查找语义相似的历史请求
for cached_prompt, (cached_embedding, cached_response) in cache.items():
similarity = cosine_similarity(embedding, cached_embedding)
if similarity >= threshold:
print(f"缓存命中!相似度:{similarity:.3f}")
return cached_response
# 未命中,实际调用 API
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}]
)
result = response.choices[0].message.content
# 存入缓存
cache[prompt] = (embedding, result)
return result
适用场景:FAQ 系统、知识库问答、重复性报告生成。命中率通常在 30-50%,对应成本降幅直接同比。
策略四:设置 max_tokens——给输出装个水龙头
这是最容易被忽略的优化,但效果立竿见影。
# ❌ 没有限制:模型可能输出几千 token
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "介绍一下人工智能"}]
)
# ✅ 设置合理上限
response = client.chat.completions.create(
model="gpt-4o",
max_tokens=500, # 根据实际需要设置
messages=[{"role": "user", "content": "介绍一下人工智能(200字以内)"}]
)
建议:在 prompt 里明确要求字数,同时在 API 层设置 max_tokens 双重限制。
策略五:流式输出 + 早停——实时控制输出长度
使用 streaming 模式可以在用户满意后提前终止生成,避免为「没用的尾部内容」付费:
def stream_with_early_stop(prompt: str, stop_signal: callable) -> str:
"""支持提前终止的流式输出"""
result = []
with client.chat.completions.stream(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
) as stream:
for text in stream.text_stream:
result.append(text)
print(text, end="", flush=True)
# 用户触发停止,或者检测到内容已完整
if stop_signal():
stream.cancel() # 立即停止,不再计费后续 token
break
return "".join(result)
策略六:批量请求——把单价打下来
很多模型提供 Batch API,价格通常是实时 API 的 50%。
适用场景:离线数据处理、大批量文档分析、定时报告生成。
# OpenAI Batch API 示例
import json
# 准备批量请求
requests = [
{
"custom_id": f"request-{i}",
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": f"分析评论:{comment}"}],
"max_tokens": 100
}
}
for i, comment in enumerate(comments)
]
# 写入 JSONL 文件
with open("batch_requests.jsonl", "w") as f:
for req in requests:
f.write(json.dumps(req, ensure_ascii=False) + "\n")
# 提交批处理任务(异步,24小时内返回,价格 -50%)
batch_file = client.files.create(
file=open("batch_requests.jsonl", "rb"),
purpose="batch"
)
batch = client.batches.create(
input_file_id=batch_file.id,
endpoint="/v1/chat/completions",
completion_window="24h"
)
print(f"批处理任务已提交:{batch.id}")
策略七:多供应商路由——坐拥最低价
这是最近比较流行的一个做法:通过一个 统一 API 网关接入多个模型供应商,根据实时价格、限流状态、响应速度自动路由。
好处:
- 哪家便宜用哪家
- 某家限流时自动切换,不影响业务
- 只维护一套 API 接口,不用改代码
# 接入统一网关后,你的代码不需要改,只需要换 base_url
from openai import OpenAI
client = OpenAI(
api_key="your-gateway-api-key",
base_url="https://your-api-gateway/v1" # 替换为网关地址
)
# 之后的调用方式完全一样,网关负责路由
response = client.chat.completions.create(
model="gpt-4o", # 网关会找最优的实际供应商
messages=[{"role": "user", "content": "Hello!"}]
)
国内可用的方案有 Praka.ai(支持 100+ 模型,含 OpenAI/Claude/Gemini/DeepSeek),接入改动量极小。
三、成本监控:不能管理就不能优化
优化策略再好,没有监控就是盲飞。几个必须追踪的指标:
| 指标 | 含义 | 预警阈值建议 |
|---|---|---|
| 每次请求平均 token 数 | 判断 prompt 是否过长 | > 2000 token 需审查 |
| Input/Output token 比 | Output 占比高 → 限制输出 | Output > 60% 需关注 |
| 模型使用分布 | 高端模型占比 | 高端模型 > 30% 需路由 |
| 缓存命中率 | 语义缓存效果 | < 20% 说明内容重复度低 |
| 用户级 token 消耗 | 识别异常用户 | 单用户 > 均值 5x 需限速 |
简单的 token 计数中间件:
import tiktoken
from functools import wraps
enc = tiktoken.encoding_for_model("gpt-4o")
def track_tokens(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 统计输入 token
messages = kwargs.get("messages", [])
input_text = " ".join([m["content"] for m in messages])
input_tokens = len(enc.encode(input_text))
response = func(*args, **kwargs)
# 从响应获取实际 usage
usage = response.usage
print(f"Input: {usage.prompt_tokens} | Output: {usage.completion_tokens} | "
f"Cost: ${usage.prompt_tokens * 0.0000025 + usage.completion_tokens * 0.00001:.4f}")
return response
return wrapper
四、汇总:按场景选策略
| 场景 | 优先用这些策略 | 预期降幅 |
|---|---|---|
| 用户输入千变万化(聊天机器人) | 模型路由 + 语义缓存 | 40-60% |
| 大批量文档处理 | Batch API + Prompt 精简 | 50-70% |
| 对话轮次多(上下文长) | 对话摘要压缩 + max_tokens | 30-50% |
| 多供应商可选 | 统一网关路由 | 20-40% |
| 输出质量要求高 | 先用小模型草稿,再用大模型精修 | 40-55% |
写在最后
AI API 的成本优化没有银弹,是多个策略叠加的结果。从我自己的项目经验来看,模型路由 + 语义缓存 + Prompt 精简这三板斧叠加,通常能实现 50-70% 的成本降幅,且对响应质量几乎没有影响。
最后一个建议:先做监控,再做优化。不知道钱花在哪,优化就是瞎蒙。
如果你也在做 AI API 成本控制,欢迎评论区交流,说说你在实际项目里踩过的坑 👇
觉得有用的话,点个赞收藏下次找得到 🙏
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)