Skip to main content

成本归因与预算熔断

"这个月花了多少"是能回答的,"是哪一行功能花掉的"通常说不清楚。而后者才是做优化、做取舍、做对外报价的依据。

这篇文章讲两层事:怎么把一笔总账拆到能行动的单位,以及预算超了之后系统该做什么——熔断是设计的一部分,不是事后加的开关。

组织看趋势与预测团队配额与告警会话硬拒绝软阈值 80%
图:预算分三层,越靠近具体会话,触发的动作越硬

1. 归因的三个层次​

总量。 一个月的总花费,通常由供应商账单给出。它能回答"有没有超支",不能回答任何"该怎么办"。

按租户与功能。 这是第一个能行动的单位。把一个月的账拆成"客服问答花了多少、商品文案生成花了多少、内部研发助手花了多少",优化才有靶子。对外提供 AI 能力的场景,这一层还是计费依据。

按单次任务。 一个工单、一次会话、一次批量任务各自花了多少。这一层用来发现异常:平均值没变,但 P99 突然高了一个数量级,通常是某类请求进了死循环。

三层的粒度不一样,用途也不一样:总量看趋势,租户与功能看取舍,单次任务看异常。只做其中一层,另外两类问题就会长期看不见。

2. 为什么"加起来对不上"​

最常见的问题是三层的数字加不总,原因通常有这几个:

一个请求服务了多个功能。 一次调用里既做了意图分类又生成了答复,成本该记给谁?需要在请求上带一个归因标签,而不是事后按接口路径猜。

Agent 一次任务是多次调用。 十步的任务有十次模型调用,中间还有工具调用和重试。必须有一个 task_id 把这条链上的所有调用串起来,否则你看到的是十笔独立的账(trace 怎么埋见《给大模型链路装仪表》)。

缓存让名义成本和实际成本分离。 命中缓存时,名义上处理了 8000 个 token,实际只付了缓存读取的钱。两个数字都要记:一个用于容量规划,一个用于对账。只记一个,要么高估成本,要么解释不清为什么账单和日志对不上。

估算值与权威值不一致。 请求之前可以估算输入 token 用来判断是否放行,但真正的计费要用响应里返回的权威用量。估算只用于拒绝,记账只用权威值——混用会导致台账长期漂移。

3. 单位成本比总成本有用​

总成本会随业务量涨跌,看不出系统本身变好还是变坏。有用的是单位成本:

每解决一个工单的成本
每千次会话的成本
每生成一条商品文案的成本
每条被采纳的 AI 建议的成本

单位成本把"做得更好"和"做得更多"分开了。而且它应该逐季下降,因为提示词在优化、缓存在命中、模型在降价、路由在变聪明。如果业务量翻倍而单位成本没降,说明优化没有跑赢规模。

有一点要防:单位成本下降不能靠降低质量换。所以单位成本必须和质量指标并排看——成功率、转人工率、用户采纳率。成本降了同时转人工率涨了,那不是优化,是把活推给了人。这个成对看的原则和《AI 研发基建怎么度量》里说的是同一条。

4. 预算三层与熔断动作​

预算不能只有一个数字,因为不同层级的"超了"含义完全不同。

组织层:软阈值告警,不拦请求。 80% 时通知,100% 时进入人工决策。这一层拦请求会把整个业务打死,代价远大于超支本身。它的作用是预测和预警。

团队/租户层:配额与限流。 超了就限流到某个较低的速率,或者直接拒绝新请求但允许在途请求完成。这一层可以拦,因为影响范围是可控的一个团队或一个客户。

会话/任务层:硬熔断。 单次会话超过 N 次调用或 N 个 token 就直接终止。这一层必须最硬,因为它能拦住最危险的情况——一个失控的 Agent 循环。几分钟烧掉一个月的额度,在日志里看起来只是一次普通请求。

越靠近具体会话,动作越硬;越靠近组织,动作越软。反过来设(组织层硬拦、会话层不管)是最常见的错误配置。

5. 熔断的三个失败模式​

熔断把生产功能打死了。 阈值定得太紧,正常业务的波峰就能触发,用户在最需要的时候被拒绝。防法是阈值按 P99 而非均值定,并且留一个人工放行的通道,带上操作人和原因记录。

只在总量上熔断。 结果是某个跑批任务触发全局熔断,所有在线功能一起挂。熔断必须按身份分层(见《统一接入层:AI 网关》里的预算分层)。

熔断之后没有降级路径。 拒绝是最简单的实现,但不一定是最好体验。可选的动作有四种:切到更便宜的模型、切到更小的上下文、要求用户缩短输入、或者直接拒绝。按场景选,而不是一律拒绝——客服场景拒绝一次的成本,可能比多花几分钱高得多。

6. 三类必须单独防的成本漏洞​

Agent 循环。 同一个工具被用同样的参数反复调用,Agent 自己不知道自己在原地打转。检测方式很便宜:连续 N 次相同工具加相同参数就判定为循环并中断。这一条在《Agentic 的十类风险》里从风险角度讲过,从成本角度讲它是唯一能在几分钟内造成重大损失的一类。

重试风暴。 上游超时,所有客户端同时退避重试,流量翻倍又触发新的超时。指数退避必须带抖动,否则重试会精确地对齐成尖峰(退避怎么设计见《限流的三个维度》)。

断连仍计费。 用户关掉页面,连接断了,但上游还在生成——已经生成的 token 照常计费。取消必须端到端传播到供应商。这一层在多租户下还有个额外后果:这些"没人读的 token"会被记到租户账上,客户会来问为什么。

7. 一个最小台账结构​

不需要复杂系统,一张表就够开始:

request_id   单次调用唯一标识
task_id 把一个 Agent 任务的多次调用串起来
tenant_id 租户/团队
feature 归属功能(归因标签)
model 实际服务模型(含是否走降级)
tokens_in 权威输入 token(含缓存读写拆分)
tokens_out 权威输出 token
cost 按权威用量计算
cache_hit 是否命中缓存

有了这张表,前面三层的账都能算出来,异常也能定位到具体请求。剩下的就是按 feature × model × 时间 做聚合。

8. 一条容易被忽略的纪律​

成本数据要能回溯到具体请求,而不只是聚合值。 平均成本上升 20% 是一个信号,但只有当能查出"是哪一类请求、从哪一天开始、走了哪个模型"时,它才是可修的。聚合看板负责发现问题,明细负责解决问题。

同时,成本要和路由放在一起看。换模型、加降级、调缓存,任何一个都会改变成本曲线。如果成本变了但你不知道路由是不是也变了,就无法归因。这一点《模型选型看哪几项》里提过:模型和缓存是耦合的,改一个会动到另一个。