背景
最近两个月,我们公司的云账单出现了比较明显的上涨。
起因是这轮全球云厂商集体涨价——AWS AI算力实例涨了15%-25%,我们有一个模型批处理任务一直跑在p3实例上,账单直接涨了将近20%,加上GPT API的调用频次这段时间上涨,综合下来每月多支出大概1.2万人民币。
负责人让我想办法控一下成本,于是我花了两周时间搭了一套轻量的多云成本监控+路由优化方案,顺带分享一下踩的坑。
技术方案
整体思路分三块:
- 成本可视化:搞清楚钱花在哪里
- 工作负载分级:能用便宜的就别用贵的
- 自动降级路由:预算超标自动切换到备用模型
环境准备
# 创建Python虚拟环境
python3 -m venv venv
source venv/bin/activate
# 安装依赖
pip install boto3 anthropic openai tiktoken prometheus-client rich
多云API管理这块,我是通过 Ztopcloud 做的统一账号管理——阿里云、AWS、GPT、Claude这几个平台的企业级结算服务全部汇在一起,在这种涨价时期可以快速看到各家的余额和调用量对比,不用登进去好几个控制台。
技术原理科普:为什么GPU算力成本是非线性的
在做预算规划之前,有一个概念需要搞清楚:大模型的推理成本不是线性增长的。
主流大模型使用的Transformer架构,其注意力机制(Attention)的计算复杂度是O(n²),n是序列长度。
简单说就是:上下文从64K扩展到128K,计算量不是2倍,是4倍。GPT-6这次把上下文扩到200万Token,如果真的要用满,单次推理消耗的算力是100万Token的4倍,不是2倍。
这个非线性特性是很多团队做成本估算时最容易犯的错误。掉进去的理由通常是"我就用了多一倍的上下文嘛"——但实际账单会让你重新认识数学。
踩坑记录
踩坑1:tiktoken版本导致token计数偏差
我们的成本埋点代码用tiktoken做token预估:
import tiktoken
def estimate_tokens(text: str, model: str = "gpt-4o") -> int:
"""预估token数量,用于成本计算"""
try:
enc = tiktoken.encoding_for_model(model)
return len(enc.encode(text))
except KeyError:
# 模型不在tiktoken支持列表里,降级用cl100k_base
enc = tiktoken.get_encoding("cl100k_base")
return len(enc.encode(text))
问题出在GPT-6发布之后,tiktoken还没有更新对应的编码器。如果直接传model="gpt-6",会fallback到cl100k_base,但GPT-6据说换了新的tokenizer,实际token数和估算值偏差在8%-15%之间。
解法:在GPT-6的tiktoken支持稳定之前,预估时乘以1.15的安全系数。
def estimate_tokens_safe(text: str, model: str = "gpt-6") -> int:
"""带安全系数的token估算(GPT-6 tiktoken未稳定前)"""
base_count = estimate_tokens(text, "gpt-4o") # 用已知模型估算
if model in ["gpt-6", "gpt-next"]:
return int(base_count * 1.15) # 加15%安全余量
return base_count
踩坑2:自动降级路由写在业务层导致循环依赖
最开始我把降级逻辑写在业务代码里,大概长这样:
# ❌ 错误做法:降级逻辑散落在业务代码里
async def process_document(doc: str) -> str:
try:
return await call_gpt6(doc)
except CostLimitError:
return await call_gpt5(doc) # 在这里降级
except Exception:
return await call_local_model(doc) # 又降级
这样写了大概40个不同的API调用点之后,维护起来是噩梦。每次想改降级策略,要改40个地方。
解法:把路由逻辑抽成独立的Router层:
# ✅ 正确做法:集中路由层
class ModelRouter:
def __init__(self, daily_budget_usd: float = 100.0):
self.daily_budget = daily_budget_usd
self.today_spend = 0.0
# 降级链:按成本从高到低排列
self.fallback_chain = [
("gpt-6", 12.0), # $12/M output tokens
("gpt-5.4", 15.0), # $15/M output tokens
("claude-opus-4", 75.0), # $75/M output tokens
("qwen3-max", 1.2), # 最便宜的fallback
]
def get_model(self, task_complexity: float) -> str:
"""根据任务复杂度和当日预算返回最优模型"""
budget_remaining = self.daily_budget - self.today_spend
budget_ratio = budget_remaining / self.daily_budget
if task_complexity > 0.8 and budget_ratio > 0.5:
return "gpt-6"
elif task_complexity > 0.5 and budget_ratio > 0.3:
return "gpt-5.4"
else:
return "qwen3-max"
def record_spend(self, model: str, output_tokens: int):
cost_per_m = dict(self.fallback_chain).get(model, 0)
self.today_spend += output_tokens * cost_per_m / 1_000_000
小结
这套方案跑了两周,月度成本预测下降了约35%,主要来自工作负载分级和token预估精度提升。
老实说,我不确定这轮涨价到什么时候是终点,也不知道GPT-6发布后定价会不会比预期高。但成本感知和路由控制这套基础设施,早一点建,早一点对自己的系统有清晰认知,总归不是坏事。
有踩过类似坑的,欢迎在评论区聊聊你们是怎么搞的。