日期:2026-08-16
我们的 AI 网关跑了小半年,一直觉得成本可控,因为每个应用都有月预算。直到有一天早上,业务方慌慌张张来说模型调用全被限流了。我们一查账单,前一天晚上有个应用的代码出了 bug,死循环里反复调模型,一晚上把整个月的预算烧光,网关的限流把所有其他应用一起掐了,无辜的业务全跟着遭殃。那次事故影响面不小,好几个核心业务停摆到上午才恢复。
这件事暴露的根本问题是,我们对成本的感知是滞后的,只能等月底或者限流触发才知道。我们需要的是实时看见每个应用花了多少、谁在异常烧钱,并且在烧爆之前就把它掐掉,而不是等它把整池子水抽干再殃及池鱼。
网关每天承载几百万次模型调用,分布在几十个应用、挂在不同模型上,单价还不一样。落地场景是:每一次调用在计量层实时算出这次花了多少 token、多少费用,按应用和模型累加;同时监控单个应用的消耗速率,一旦发现速率异常,比如短时间内突增几倍,就自动熔断这个应用,不影响别人;预算水位到警戒线提前告警,给业务方留缓冲。
熔断之后不是一断了之,要告警、要能人工确认恢复,避免误伤正常业务。我们还给重要业务设了白名单,异常时优先保它,不能因为要拦一个出问题的就把核心业务一起掐了,那和当初那次事故没区别。
实时核算的精度是第一个难点。调用计量本来就有异步对账,实时算的账和最终对账可能有微小出入。我们定位是,实时账用于监控和熔断,允许小误差,最终以异步对账为准,两者日终勾一遍,差异超过阈值才告警,这样既不误熔断也不漏真异常。这个边界定清楚之后,研发和财务都好接受,没人再纠结实时账差几分钱。
异常模式怎么识别。我们没用固定的绝对阈值,因为不同应用基线差很多,小应用突增十倍可能才几块钱,大应用涨一倍就不得了。我们用的是基于各自历史基线的速率突变检测,短时速率超过自身基线的若干倍才判异常,减少误报。基线每天滚动更新,避免把业务自然增长当成异常。我们还加了静默期,新上线的应用先观察几天再纳入突变检测,不然刚上线那点波动全是告警。
熔断误伤要防。熔断动作之前先发预警,给应用方几十秒的观察窗口,确认是 bug 再彻底熔断;恢复要走人工确认,防止刚恢复又疯跑。白名单应用即使触发也只降级不限流,保证核心业务不断。
这个机制上线后我们养成了一个习惯,每天早会看一眼前一天的消耗分布,哪个应用突然涨了就提前问一句,把问题掐在苗头。财务那边也从月底对账变成了每天看实时大屏,预算执行情况一目了然,再也不用等月底才发现某应用超支。运营也主动多了,有些应用方看到自己的消耗曲线,自己就把代码里的无效重试改了,良性循环就转起来了。有次一个应用方看到实时消耗暴涨,自己定位到是缓存没命中导致反复调模型,半小时内就修好了,要搁以前得等月底账单才知道。
上线实时核算之后,成本从月级可见变成分钟级可见,运营大屏上每个应用花了什么都摊开。异常消耗拦截,上线后抓到过三次类似当初那次死循环的事故,都在烧到月预算之前就被熔断,影响范围只限出问题的应用,其他业务零感知。预算超支事件按月统计从之前的好几起降到零。误熔断率我们盯着,因为用了基线突变加观察窗口,误熔断极少。
案例片段(已脱敏): 熔断规则的一段配置(yaml 示意): cost_control: realtime: true baseline_window: 7d spike_factor: 5 warn_window_sec: 30 circuit_break: true whitelist: ["core-service"] new_app_silent_days: 3 一次事件:应用 A 在 3 分钟内调用速率从基线 200/s 飙到 1800/s,触发 spike 预警,30 秒观察窗口内未回落,自动熔断,消耗止步于当日预算的 12%,其余应用不受影响。白名单应用 core-service 同日一次异常仅降级未限流。
网关成本治理这件事,最大的教训就是别等月底账单。我们那次烧光预算影响全平台的事故,如果当时有实时核算加熔断,最多影响出问题的那一个应用,不会连累所有人。这个教训是用一次停摆换来的,不希望别人也踩。
异常检测别用统一绝对阈值,每个应用的基线差太多。我们用各自历史基线做突变检测之后,误报少了一大半,小应用的正常波动不会惊动告警,真出事的大应用也瞒不住。基线滚动加新应用静默期这两点,是踩了几次误报之后补的。
熔断一定要带观察窗口和人工恢复。我们一开始想直接断,后来觉得万一是误判,断了重要业务更糟,加了几十秒的预警窗口和人工确认,误伤和响应速度都照顾到了。白名单机制也建议有,核心业务值得被优先保。