大模型API的成本曲线通常不是线性增长,而是在某个阈值后突然变陡。原因在于很多调用链路没有设置预算上限,也没有根据请求价值选择模型。一个简单的文本分类任务每次都要消耗数千token,并且调用的是最高规格模型;同时,重试逻辑又把失败请求放大数倍。先看一组常见数据:一个问题回答接口平均输入1800 token,输出600 token,如果单日调用量为20万次,按每百万token 2美元粗略计算,单日成本可能超过900美元。这个数字在没有预算熔断的情况下还会继续增长。因此成本治理首先要解决的不是降价,而是让每一次支出都有明确边界。

一、成本失控的三个直接原因
第一个原因是所有请求都走同一条高价模型链路。很多业务在接入大模型时为了快速上线,直接用能力最强的模型处理全部请求,包括摘要、分类、关键词提取、简单问答。实际上,这些任务里相当一部分用轻量模型就能满足质量要求。高价模型的单价可能是轻量模型的数倍甚至数十倍,当请求规模放大后,差价会被完整计入账单。
第二个原因是缺少预算熔断。团队可能设置了月度预算,但这个预算只用于事后统计,并不阻断调用。某个定时任务死循环、前端重复提交、或者上游服务异常重试,都会在短时间内产生大量请求。如果系统没有在接近阈值时拒绝新请求或降级处理,预算就形同虚设。
第三个原因是上下文长度缺少约束。不少接口直接把历史对话、文档片段、系统提示词拼在一起,没有做截断或压缩。输入token越多,单次成本越高,同时响应时间也会变长。尤其是检索增强生成场景,如果检索结果不加过滤,很容易把数十万token塞进上下文。
二、预算控制的实现:从统计值变成硬性熔断
预算控制应该分成三个层次:请求级、窗口级和聚合级。请求级限制单次最大token数,窗口级限制每分钟或每小时调用次数,聚合级限制每天或每月总花费。统计值需要实时写入,熔断动作要尽量靠近调用入口。
以Python网关层为例,可以先维护一个基于Redis的预算计数器。每次调用前检查本月已用金额、今日已用金额和当前并发数,超限时直接抛出预算异常。下面是一个简化实现:
import redis
import time
from dataclasses import dataclass
@dataclass
class BudgetConfig:
daily_limit: float = 500.0
monthly_limit: float = 12000.0
max_input_tokens: int = 4000
max_output_tokens: int = 1000
class BudgetGuard:
def __init__(self, r: redis.Redis, config: BudgetConfig):
self.r = r
self.config = config
def _current_day_key(self):
return time.strftime("budget:day:%Y-%m-%d")
def _current_month_key(self):
return time.strftime("budget:month:%Y-%m")
def check(self, estimated_cost: float) -> bool:
day_key = self._current_day_key()
month_key = self._current_month_key()
day_spend = float(self.r.get(day_key) or 0)
month_spend = float(self.r.get(month_key) or 0)
if day_spend + estimated_cost > self.config.daily_limit:
return False
if month_spend + estimated_cost > self.config.monthly_limit:
return False
return True
def record(self, cost: float):
day_key = self._current_day_key()
month_key = self._current_month_key()
pipe = self.r.pipeline()
pipe.incrbyfloat(day_key, cost)
pipe.incrbyfloat(month_key, cost)
pipe.expire(day_key, 86400 * 2)
pipe.expire(month_key, 86400 * 31)
pipe.execute()
上面的代码把预算检查放在调用前,把记录放在调用完成后。estimated_cost可以根据输入token和输出token上限提前估算,这样即使模型提供方返回超长输出,也能在请求发起前阻止明显超支。预算异常不建议直接透传给终端用户,可以在网关层转换成统一的限流提示。
为了让预算控制更可靠,还需要给窗口级限制配置独立的限流器,例如令牌桶或滑动窗口。窗口级限制与金额预算不同,它关注的是突发流量。两者同时生效时,即使某个调用估算金额很低,也不会因为高频调用把今日预算提前耗尽。
三、模型降级策略:让请求价值决定模型规格
模型降级不是简单地在报错时换一个小模型,而是在请求进入时根据任务类型、上下文长度、期望延迟和预算余量动态选择模型。常见做法是维护一个模型路由表,把任务分为高价值、中价值和低价值三个等级。高价值任务比如复杂推理、代码生成、长文写作,使用高规格模型;中价值任务比如多轮问答、信息抽取,使用中等模型;低价值任务比如关键词提取、情感分类、简单改写,使用轻量模型。
动态路由最好配合规则和统计。规则解决大部分确定性场景,统计解决规则覆盖不到的长尾。下面是一个按任务类型选择模型的简化示例:
MODEL_ROUTES = {
"complex_reasoning": {"model": "gpt-4o-mini", "fallback": "gpt-4.1-mini"},
"chat": {"model": "gpt-4.1-mini", "fallback": "gpt-4o-mini"},
"extraction": {"model": "gpt-4.1-nano", "fallback": "gpt-4.1-mini"},
"summarization": {"model": "gpt-4.1-mini", "fallback": "gpt-4.1-nano"},
"classification": {"model": "gpt-4.1-nano", "fallback": None},
}
def route_model(task_type: str, budget_remaining_ratio: float) -> str:
route = MODEL_ROUTES.get(task_type)
if route is None:
return "gpt-4.1-mini"
if budget_remaining_ratio < 0.2:
return route.get("fallback") or route["model"]
return route["model"]
当剩余预算比例低于20%时,系统会优先选择fallback模型。如果任务本身没有fallback,则维持原模型。这个策略可以在预算紧张时自动降低高规格调用比例,而不是直接拒绝所有请求。
模型降级还需要评估质量损失。建议为每类任务建立一个小型评测集,在切换模型前对比准确率、召回率或人工评分。比如分类任务从高规格模型降到轻量模型,准确率可能只下降1%到2%,但成本能下降60%以上。这种数据比单纯的成本数字更能说服业务方接受降级方案。
四、把预算与降级接入统一监控闭环
预算控制和模型降级如果只在代码里生效,仍然可能出现配置陈旧、规则遗漏的问题。需要把每次调用的成本、模型、任务类型、预算剩余量和是否降级都写入日志或指标系统。监控维度至少包含按模型统计的调用量、按任务类型统计的平均token消耗、按小时统计的花费趋势,以及降级触发次数。
当出现连续触发预算熔断或降级比例异常升高时,告警应该通知到负责成本的同学。告警阈值可以设置为:今日花费达到预算的80%、单小时花费超过过去7天同时段均值的3倍、降级率超过30%等。这样既能及时发现问题,也能避免预算用尽后才收到通知。
最终的优化闭环是:监控数据反映哪类任务消耗最多,模型降级策略根据评测集调整路由表,预算控制根据真实成本基线调整阈值。三者循环迭代后,成本不会再次失控,同时业务体验也能保持稳定。