归因不了的成本你管不了,没有熔断器的预算只是一个愿望。
一张笼统的月度模型账单在运营上毫无用处:它告诉你你花了钱,却没告诉你是哪个功能、哪个租户、哪个用户花的,而且它在花钱决策已不可逆的一个月后才到。成本归因是这样一门纪律:给每次模型与工具调用打上你据以做决策的维度标签,让花费变成按功能、按租户的信号——而预算只有在超出时会在运行时做点什么、而非只在报表里做点什么,才能保护你。
厂商账单的粒度无法据以行动。
发票按 API key 与月份聚合——这正是你拿它没办法的两个维度。驱动决策的问题是:哪个功能不赚钱、哪个租户在被其他租户补贴、哪个用户是成本离群点、哪条代码路径在上次部署后回退了。这些都无法从账单回答。归因意味着把你据以决策的维度贯穿每次调用,让花费可沿功能、租户、用户、版本——你实际决策的那些轴——被查询。
给每次调用盖上成本上下文,并让它穿过扇出传播。
一个成本上下文对象在任务开始时创建,并随任务流动——包括进入每个子智能体与工具调用。每次模型响应的用量都记到那个上下文上。难的是传播:一个丢了上下文的子智能体把它的花费归给了无人,而扇出恰恰是昂贵花费藏身之处。
# cost/attribution.py — context travels with the task, into children class CostContext: def __init__(self, tenant, feature, user, version): self.tenant, self.feature = tenant, feature self.user, self.version = user, version def record(self, call): emit("spend", tenant=self.tenant, feature=self.feature, user=self.user, version=self.version, usd=call.cost_usd) def spawn_child(ctx): return ctx # SAME context — unattributed spend is the bug
经典的归因漏洞:子智能体与异步工具调用丢掉上下文,于是 30–60% 的花费落进一个"未知"桶——而那个未知桶里不成比例地是昂贵的深度工作。未打标签的花费不是舍入误差;它通常正是你最需要看见的那部分。
归因到你能据以行动的维度,而非好记录的那个。
记录模型名很容易,对决策却几乎无用。沿着能映射到杠杆的轴归因:功能(砍掉或重新定价)、租户(重新谈判或限流)、用户群(识别滥用,或一个值得学习的高级用户)、版本(抓住一次让成本翻倍的部署)。一个有用归因维度的检验标准:它上面一个明确的坏读数会在本周触发一个具体行动。如果什么都不会发生,这个标签就是遥测表演。
预算要么是运行时控制,要么只是个愿望。
一个你在仪表盘里拿来对实际值的月度预算什么也不做——等仪表盘红了,钱已经没了。真正的预算是一个计数器,按租户或功能划定范围,在请求路径上被扣减,越线时改变行为。是那个强制,不是那个数字,才是预算。
- 按租户上限,让一个客户的失控无法吞掉共享池、把其他人饿死。
- 先软后硬阈值——到软线时降级(更便宜的模型、更小的上下文、停用可选工具);到硬线时 fail-closed 并告警。
- 在请求路径上强制,在昂贵调用之前检查,而非之后对账。
熔断器止血;告警只是在旁白。
告警把一桩正在进行的成本事故讲给一个人听;等有人读到时,一个无界循环已经计了一小时的费。一个熔断器——按花费速率跳闸、按租户上限跳闸、按每任务成本超过历史中位数 N 倍跳闸——自动停下花费,并让请求降级或 fail-closed。告警与熔断互补:熔断器框住损失,告警解释损失。一个只有告警的系统,已经选择了为每一桩事故付全款。
什么时候细粒度归因省下的不如它花掉的。
按调用归因与热路径上的预算检查会增加延迟、存储与代码面;对一个低流量、预算固定的内部工具,这套埋点可能比它治理的花费还贵。按你决策实际用到的粒度归因,不要更细。始终运行按租户的熔断器——它便宜且框住灾难——但把按用户、按调用的归因留给那些定价与毛利决策确实依赖它的产品。