云服务账单暴涨怎么办?我用一套多云成本监控方案把费用压下来了

0 阅读4分钟

背景

最近两个月,我们公司的云账单出现了比较明显的上涨。

起因是这轮全球云厂商集体涨价——AWS AI算力实例涨了15%-25%,我们有一个模型批处理任务一直跑在p3实例上,账单直接涨了将近20%,加上GPT API的调用频次这段时间上涨,综合下来每月多支出大概1.2万人民币。

负责人让我想办法控一下成本,于是我花了两周时间搭了一套轻量的多云成本监控+路由优化方案,顺带分享一下踩的坑。


技术方案

整体思路分三块:

  1. 成本可视化:搞清楚钱花在哪里
  2. 工作负载分级:能用便宜的就别用贵的
  3. 自动降级路由:预算超标自动切换到备用模型

环境准备

# 创建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发布后定价会不会比预期高。但成本感知和路由控制这套基础设施,早一点建,早一点对自己的系统有清晰认知,总归不是坏事。

有踩过类似坑的,欢迎在评论区聊聊你们是怎么搞的。