多模型网关接入一个模型时,成本统计通常只是读取响应中的 Token 数量,再乘以单价。接入多个供应端、流式响应、自动重试和团队预算后,同样的做法很快会出现差异:业务系统只记录了一次请求,供应端却执行了两次调用;前端提前断开连接,网关没有拿到流式响应末尾的用量字段;模型发生降级,账单仍按原模型单价计算;缓存命中没有调用上游,却被当作完整推理收费;月底汇总时,财务账单、网关报表和项目负责人看到的数字彼此不同。
问题不在于乘法是否正确,而在于系统没有先定义“计量对象是谁、用量以什么为准、价格取哪个时点、失败调用由谁承担”。如果请求、上游调用、用量事件和成本分录混在一张日志表里,任何重试、流式中断或价格调整都会改变结果。本文给出一套可复现的实现:把一次业务请求拆成多个调用尝试,以供应端返回的用量为主要证据,以价格快照计算成本,用预占和结算控制并发预算,再通过幂等账本与供应端账单对账。
一、先确认“账单对不上”具体差在哪一层
成本问题不能只比较两个总金额。至少要分别核对请求数、上游调用数、输入 Token、输出 Token、缓存 Token、计费单价和币种。总额相同也可能隐藏错误,例如某个团队被多记、另一个团队被少记;总 Token 相同也不代表金额正确,因为输入、输出、缓存读取或不同模型可能采用不同计费规则。
常见差异可以分为五类。第一类是数量差异,业务请求一百次,上游调用却超过一百次,通常与重试、故障转移或影子流量有关。第二类是用量差异,本地估算 Token 与供应端返回值不同,可能由分词器、系统提示、工具定义或隐藏包装字段造成。第三类是归属差异,请求能计算成本,却缺少团队、项目或环境标签,只能落入“未分配”。第四类是价格差异,历史请求被当前价格重新计算。第五类是状态差异,超时、取消或流式中断到底是否产生费用没有明确证据。
排查时应先固定一个短时间窗口,按请求追踪标识导出明细,而不是直接争论月度总数。每条明细至少回答:用户发起了什么业务请求,网关执行了几个上游尝试,每个尝试调用了哪个实际模型标识,供应端报告了多少用量,使用哪份价格快照结算,最终成本归属到哪个预算单元。只要其中一项无法追溯,汇总报表就缺少审计基础。
二、区分业务请求、调用尝试、用量事件和成本分录
一个业务请求不一定等于一次上游调用。用户发送一次对话请求,网关可能先调用主模型,超时后重试一次,再切换备用模型。对用户而言只有一次请求,对供应端而言可能有三次可计费用量。因此数据模型至少需要四个对象。
request表示业务动作,负责保存租户、团队、项目、用户、环境、预算单元和追踪标识。attempt表示一次真实上游调用,保存供应端、实际模型标识、开始时间、结束状态、重试序号和故障转移原因。usage_event保存供应端返回或本地估算的 Token 明细及证据等级。ledger_entry是不可变成本分录,保存价格快照、计算过程、币种和正负方向。
这种拆分解决了一个重要问题:重试不再覆盖原调用。假设第一次调用已经在供应端完成,只是网关等待响应时超时;第二次重试成功后,如果把同一个请求记录更新为“成功”,第一次可能产生的用量就消失了。保留独立attempt后,两次调用都能进入待核对状态,业务请求的最终结果与供应端的实际执行成本也可以同时表达。
成本账本应采用追加方式。发现价格配置错误时,不要直接改写历史金额,而是写一条冲正分录,再按正确快照写入新分录。这样月度报表能够解释“为什么昨天显示的金额今天发生变化”,也能区分正常消费、补记、冲正和人工调整。直接更新原金额虽然简单,却会让审计记录失去前后关系。
三、用量来源要有优先级,估算值不能伪装成账单值
网关通常有三种 Token 来源。第一种是供应端响应中的usage字段,它最接近供应端计费口径,应作为主要结算证据。第二种是使用对应分词器在本地计算,适合请求前预算预占、响应缺失时告警和差异分析。第三种是按字符数或字节数近似估算,只适合粗粒度限流,不能作为精确账单依据。
每条用量事件都应携带source和confidence。例如provider_reported表示供应端明确返回,tokenizer_estimated表示使用声明的分词器估算,heuristic_estimated表示近似值,invoice_reconciled表示后来由供应端账单确认。报表默认应区分已确认成本与估算成本,不能把两者相加后只展示一个没有说明的数字。
本地估算与供应端用量不一致不一定代表供应端错误。网关看到的请求可能不是最终请求:客户端 SDK 增加了系统字段,路由层拼接了安全提示,工具定义被序列化,上游服务又可能采用不同的分词规则。正确做法是保留经过脱敏的请求结构摘要、估算器版本和供应端返回值,计算差异率并持续观察,而不是强行用本地数字覆盖供应端数字。
供应端没有返回用量时,也不应把估算结果标记为最终结算。可以先生成状态为provisional的分录,等待异步用量接口或账单文件对账;超过等待期仍无证据,则把该记录列入异常队列。团队预算可以暂时按较保守的估算值占用,但财务报表必须显示这部分金额尚未确认。
四、建立可审计的最小账本结构
账本不需要一开始就覆盖所有供应端字段,但主键和证据链必须稳定。下面使用关系数据库展示核心字段,金额以高精度十进制保存,不能使用二进制浮点数。示例省略了外键和分区配置,真实系统还应加入租户隔离与访问控制。
createtableai_request(request_id uuidprimarykey,trace_idvarchar(80)notnull,tenant_idvarchar(64)notnull,team_idvarchar(64)notnull,project_idvarchar(64)notnull,environmentvarchar(24)notnull,budget_accountvarchar(64)notnull,requested_modelvarchar(128)notnull,created_at timestamptznotnull);createtableai_attempt(attempt_id uuidprimarykey,request_id uuidnotnull,attempt_nointegernotnull,provider_idvarchar(64)notnull,resolved_modelvarchar(128)notnull,statusvarchar(24)notnull,retry_reasonvarchar(64),provider_request_idvarchar(160),started_at timestamptznotnull,finished_at timestamptz,unique(request_id,attempt_no));createtableusage_event(event_id uuidprimarykey,attempt_id uuidnotnull,event_typevarchar(32)notnull,sourcevarchar(32)notnull,input_tokensbigintnotnulldefault0,output_tokensbigintnotnulldefault0,cached_input_tokensbigintnotnulldefault0,provider_payload_hashchar(64),observed_at timestamptznotnull,unique(attempt_id,event_type,source));createtablecost_ledger(entry_id uuidprimarykey,attempt_id uuidnotnull,usage_event_id uuidnotnull,price_snapshot_id uuidnotnull,entry_kindvarchar(24)notnull,amount_minornumeric(30,0)notnull,currencychar(3)notnull,scaleintegernotnull,created_at timestamptznotnull,unique(attempt_id,usage_event_id,entry_kind));resolved_model必须记录路由后真正调用的标识,而不是用户请求中的别名。用户传入smart-chat,网关可能根据区域、容量和白名单解析为另一个平台模型标识;成本计算只能使用解析结果。若平台名称没有得到官方厂商文档或模型列表响应确认,就把它视为路由别名,不要在报表中擅自映射为某个官方型号。
provider_request_id是对账的重要关联字段。供应端支持请求标识时应完整保存,但不要将密钥、完整提示词或个人信息写入账本。若供应端不返回可关联标识,可以保存时间窗口、模型标识、用量和响应摘要组合成的匹配线索,同时把对账置信度标为较低。
五、价格必须快照化,不能用今天的配置重算昨天
价格是有生效时间的配置,不是模型表上的一个可变字段。一份价格快照至少包含供应端、实际模型标识、计费项、单位、币种、生效区间和来源说明。输入、输出、缓存读取、缓存写入、图片、音频或工具执行如果采用不同计费单位,应拆成不同价格项。系统不认识的计费项不能默认为零,应进入待配置状态并阻止正式结算。
price_snapshot_id:ps_example_202607provider_id:provider_aresolved_model:route-model-01currency:CNYeffective_from:2026-07-01T00:00:00+08:00items:input_tokens:unit_tokens:1000000amount_minor:120000output_tokens:unit_tokens:1000000amount_minor:360000source:type:manually_verified_configchecked_at:2026-07-18T08:30:00+08:00上面的数字只用于展示数据结构,不代表任何供应端的真实价格。生产环境应从可核实的合同、控制台或官方计费说明录入,并保留核对人员与时间。团队若通过统一接入平台维护模型路由,可将 https://178.nz/dn 作为接入资料的人工核对入口之一,但实际结算仍必须依据已确认的模型标识、供应端账单与本地价格快照,不能仅凭页面展示名称推断价格。
请求结算时应按调用开始时间或合同约定的计费时间选择快照,并把快照主键写入分录。之后即使价格表更新,历史分录仍指向原快照。需要按新合同追溯调整时,使用冲正和补记分录,而不是运行一条批量更新语句覆盖历史成本。
跨币种汇总也要保留两层金额:原始账单币种与报表本位币。汇率同样需要日期、来源和快照。不能把换算后的金额当成供应端原始金额,也不要先把每次微小调用四舍五入到分,再汇总;这样会在高频小额调用中积累明显误差。
六、流式响应要在“转发数据”和“收集用量”之间解耦
流式调用的难点是客户端可能提前断开,而上游仍在生成;也可能供应端只在最后一个事件中返回用量。若网关把客户端连接生命周期等同于上游调用生命周期,断开时立即销毁上下文,就会丢失最终用量。反过来,为了等待用量而无限读取上游,也会浪费资源。因此需要明确取消策略,并把每次尝试状态记录下来。
网关收到流式事件后,一边把允许的内容转发给客户端,一边在服务端解析事件。用量字段出现时写入usage_event;上游正常结束但没有用量时,记录usage_missing;客户端断开时,根据产品策略决定向上游发送取消,或继续短暂读取到终止事件。无论哪种选择,都要把client_disconnected、cancel_sent、upstream_finished和最终用量状态分开记录。
asyncdefproxy_stream(ctx,upstream,client):usage=Noneclient_open=Truetry:asyncforeventinupstream:ifevent.usageisnotNone:usage=normalize_usage(event.usage)ifclient_open:try:awaitclient.send(event.public_payload)exceptClientDisconnected:client_open=Falseawaitrecord_marker(ctx.attempt_id,"client_disconnected")awaitupstream.cancel_if_supported()finally:ifusage:awaitpersist_provider_usage(ctx.attempt_id,usage)else:awaitmark_usage_pending(ctx.attempt_id)这段代码只展示控制思路,不能直接复制为所有协议的实现。不同供应端是否在取消后继续计量、是否支持返回最终用量、用量位于哪个事件,都需要根据实际接口验证。关键是不把“客户端没收到完整答案”自动解释成“上游没有成本”,也不把“网关超时”自动解释成“供应端调用失败且免费”。
流式报表还应区分已发送输出与供应端计费输出。两者可以用于不同目的:已发送输出反映用户实际获得的内容,供应端输出反映成本。如果只保存前者,客户端断开后的生成量无法解释账单;如果只保存后者,又无法分析用户体验和无效输出。二者不能混为同一个字段。
七、重试与故障转移必须按调用尝试计量
重试是账单倍增最常见的来源。网关只有在确认请求没有被上游接受时,才能认为重试不会产生额外成本;网络超时往往无法提供这种保证。连接建立后发生读超时,供应端可能已经完成推理,只是响应没有到达网关。此时第二次调用即使成功,第一次也应保留为unknown_cost,等待供应端用量或账单确认。
重试策略需要区分连接失败、写入失败、首字节超时、流中断、限流和上游明确错误。连接尚未建立通常可以安全重试;请求体已经发送后发生超时则具有不确定性;流式输出已经向用户发送后再从头重试,还会造成重复内容。把所有异常统一包装为retryable=true,既可能增加成本,也可能破坏回答语义。
故障转移时要分别记录请求模型与实际模型。主模型失败后切到备用模型,两者价格和 Token 分布可能不同,不能按最初选择统一计算。路由日志应保存决策原因,例如容量不足、区域不可用、预算限制或模型白名单;成本分录则始终绑定每次尝试的resolved_model与对应价格快照。
若网关执行并行竞速,让两个供应端同时生成并采用先返回的结果,落选调用也可能产生费用。报表可以把胜出调用记为“交付成本”,落选调用记为“可靠性成本”,二者都归入真实总成本。这样团队能判断降低延迟的策略付出了多少额外支出,而不是把落选调用隐藏在无法解释的供应端账单里。
八、先预占、后结算,解决并发预算穿透
仅在响应结束后扣减预算,会产生并发穿透。假设某个项目剩余预算只能支持一次大请求,十个请求同时通过余额检查,结束后才发现预算已经超出。解决方法是请求前预估最大成本并预占,调用完成后按供应端用量结算,多退少补。
预占金额可以根据输入估算、最大输出限制、候选模型价格和安全系数计算。它不需要等于最终成本,但必须有明确上界。若调用支持工具循环或无法确定输出上限,应设置单请求硬限额。预算服务在同一原子操作中检查可用余额并增加reserved_amount,失败则拒绝或路由到符合策略的低成本模型。
available = limit - settled - reserved if estimated_max_cost <= available: reserved += estimated_max_cost create reservation else: reject or apply approved downgrade policy结算时以实际成本替换预占:先写成本分录,再释放该请求的预占差额。两个操作需要事务或可靠事件保证,避免账本成功但预占未释放。调用状态长期未知时,预占不能悄悄过期,应进入待核对队列;只有根据明确规则确认不会再产生费用,或由有权限的人员处理后,才能释放。
预算可以分为组织、团队、项目和环境多级限制,但扣减顺序必须固定。一个请求通常同时占用多个预算层级,任何一层不足都应阻止未授权调用。不要分别查询四次余额再分别更新,那会留下并发窗口;可在数据库中按固定顺序锁定相关预算行,或使用支持原子脚本的计数存储完成检查与预占。
九、幂等键要覆盖计量与结算,而不只是业务请求
客户端幂等键只能防止重复创建业务请求,无法自动消除供应端重试。内部还需要为尝试、用量事件和分录分别建立唯一约束。request_id + attempt_no保证同一次尝试不会重复创建;attempt_id + event_type + source防止消息重复消费后写入两份供应端用量;attempt_id + usage_event_id + entry_kind防止结算任务重复记账。
异步队列通常提供至少一次投递,同一用量事件被处理两次是正常情况。消费者应先根据稳定事件键插入,遇到唯一键冲突时读取既有记录并比较内容哈希。内容相同可视为安全重复;内容不同则说明供应端修正了用量或事件生成逻辑有问题,不能简单忽略,应进入冲突队列并生成冲正流程。
不要用“状态已经是成功”作为唯一幂等判断。任务可能在写入用量后、更新状态前崩溃,重启后再次执行;也可能状态已更新但分录事务回滚。正确性应由唯一约束、事务边界和可重放事件保证,状态字段主要用于查询与运维,不承担全部去重责任。
十、成本归因标签必须在请求入口确定
月底再根据 API Key 猜测成本归属,通常会遇到共享密钥、人员调岗和项目重命名。归因维度应在请求进入网关时解析并固化,包括租户、团队、项目、应用、环境、成本中心和调用目的。随后即使组织结构变化,历史分录仍保留当时的归属;管理报表可以另行映射到当前组织,但不能覆盖原始标签。
标签来源需要有可信顺序。受签名令牌或网关分配密钥绑定的团队信息可信度较高;客户端自由填写的team_id不能直接用于财务归因,否则调用方可以把成本转移给其他团队。推荐由网关根据身份和密钥策略填充强制标签,客户端只提交经过白名单允许的项目或业务场景,并在服务端校验其授权关系。
共享服务还需要处理二次归因。例如一个 RAG 应用先调用 Embedding,再调用 Rerank 和生成模型,三个调用都应关联同一个业务trace_id,同时保留各自的服务名称与尝试标识。项目负责人可以查看一次问答的完整成本构成,平台团队也能按基础服务统计,而不是把全部费用记到网关本身。
无法解析归属的请求不能默认为公共成本。可以进入unallocated预算单元并触发告警,设置允许占比上限。未分配成本长期过高,通常意味着存在绕过网关的密钥、旧客户端没有传递身份,或路由规则未覆盖新应用。把它平均分摊给所有团队只会掩盖治理缺口。
十一、不同类型调用不能强行套用文本 Token 模型
聊天生成、Embedding、图片、音频、批处理和工具执行的计量单位可能不同。统一账本不等于所有费用都换算为输入、输出 Token。更合理的做法是使用通用quantity + unit结构,并在文本模型的便利字段之外保存原始计费项。例如图片按张数或规格、音频按时长、托管工具按调用次数计量时,都应保留供应端真实单位。
工具调用还可能形成多轮循环:模型输出工具参数,应用执行工具,再把结果送回模型。用户只发起一次请求,却有多次模型调用。每轮都是独立尝试,归属于同一个请求和会话步骤。若只记录最终轮次的usage,前面用于规划和工具选择的 Token 会从网关报表消失。
Prompt 缓存需要区分缓存命中、缓存写入与普通输入。供应端返回细分字段时,规范化层应保留它们;供应端只给总输入时,不要根据本地命中率自行拆分成计费字段。报表可以另外记录本地缓存分析,但正式分录必须与可核对证据保持一致。
网关自身的响应缓存命中则属于另一种情况:如果没有发起上游尝试,就不应生成模型供应端成本。可以记录一条零上游成本的服务事件,用于分析缓存节省和网关资源消耗,但不要伪造一条 Token 用量。平台内部是否对缓存服务计费属于管理规则,应与供应端成本分开呈现。
十二、金额计算要使用整数最小单位和明确舍入规则
高频调用的单次成本可能小于货币最小单位。如果每次都先四舍五入到分,大量调用汇总后会产生偏差。可以把金额保存为更细的内部最小单位,例如在amount_minor外增加scale,表示该整数对应小数点后的位数;汇总到报表时再按合同规则舍入。
假设价格按每百万 Token 计费,内部计算应使用整数或十进制定点数:先计算各计费项的“Token 数乘单位价格”,汇总后再除以单位 Token 数。不要使用float或 JavaScript 普通浮点数保存货币。不同币种也不能在同一列直接相加,应先按指定汇率快照转换到报表本位币,并保留原始金额。
fromdecimalimportDecimal,ROUND_HALF_UPdeftoken_cost(tokens:int,unit_tokens:int,amount:Decimal)->Decimal:raw=Decimal(tokens)*amount/Decimal(unit_tokens)returnrawdefreport_amount(raw:Decimal,digits:int=6)->Decimal:quantum=Decimal(1).scaleb(-digits)returnraw.quantize(quantum,rounding=ROUND_HALF_UP)舍入规则必须写入价格快照或结算策略版本。供应端可能按请求、按日或按账期汇总后舍入,本地逐请求金额之和因此可能与发票存在微小差异。对账系统需要允许可解释的舍入差额,但阈值应基于合同规则设置,不能用一个很大的百分比掩盖漏记调用。
十三、日志脱敏不能破坏对账需要的关联字段
成本系统需要关联信息,但不需要保存完整提示词、密钥或用户隐私。建议记录trace_id、内部请求标识、供应端请求标识、模型标识、标签、Token 明细、价格快照和状态;对问题内容只保存经过租户隔离的哈希、长度区间或分类标签。密钥仅保存不可逆指纹和密钥策略 ID,不能保存原值。
供应端原始用量响应可做规范化后哈希,并把加密原文放入受限审计存储,设置较短保留期。普通运维日志只输出摘要和错误分类。需要调查争议账单时,由有权限的人员按审计流程读取原始证据,每次访问都记录操作者、原因和时间。
团队报表也要应用最小权限。项目负责人只能看到自己的明细,组织管理员查看跨团队汇总,财务角色可以读取金额但不必读取问题内容。导出文件应设置过期与下载审计,避免成本报表意外暴露项目名称、模型使用习惯或内部应用结构。
日志脱敏规则本身需要版本号。字段新增后如果没有进入脱敏清单,可能把工具参数或文档标题直接写入日志;过度脱敏则会删除供应端请求标识,导致无法对账。上线前应使用包含模拟密钥、邮箱、内部地址和工具参数的测试载荷,验证日志输出既能关联又不泄露敏感值。
十四、对账要从逐调用匹配开始,再汇总到团队
可靠的对账流程通常包含三层。第一层核对网关尝试与供应端用量明细,优先使用供应端请求标识匹配。第二层按模型、时间窗口和计费项汇总,用于发现明细接口缺失或时区边界问题。第三层才把确认后的成本按团队、项目和环境汇总。直接拿团队月报与供应端总账比较,只能知道存在差异,不能定位差异来自哪一批调用。
对账任务应输出差异类别,而不是只有“相等”或“不相等”。常见类别包括:网关有尝试但供应端无记录、供应端有记录但网关无尝试、Token 不一致、模型标识不一致、价格快照缺失、币种不一致、重复分录和舍入差异。每类问题对应不同责任方与修复动作。
{"reconcile_window":"example-window","provider_id":"provider_a","status":"needs_review","counts":{"matched":812,"gateway_only":3,"provider_only":2,"usage_mismatch":5},"note":"示例结构,不代表真实运行结果"}上面的数字只是演示对账输出格式,不能作为任何平台的测试结论。真实任务应保存匹配规则版本、账单文件摘要、时间范围和执行时间。对于provider_only记录,可检查是否存在绕过网关的直连密钥;对于gateway_only记录,应确认调用是否在连接前失败;对于用量差异,应比较供应端原始字段、规范化规则和本地估算器版本。
对账确认供应端数字后,如果与临时估算分录不同,应生成冲正与新结算分录。团队月报只汇总有效分录,不删除历史估算记录。这样既能显示最终成本,也能计算“月末有多少金额曾处于待确认状态”,帮助评估供应端用量返回和网关采集的稳定性。
账期边界还要明确时区和迟到事件的处理方式。网关可以按 Asia/Shanghai 生成团队日报,而供应端账单可能采用其他时区;同一次调用在两个系统中可能落入不同日期。对账时应先把原始时间与时区完整保存,再转换到统一比较窗口,不能只截取日期字符串。月底之后才到达的用量记录应进入下一次补充对账,并通过调整分录回写原账期,不能为了让报表“关账”而直接丢弃。报表需要同时标明业务归属期与分录入账期,这样使用者才能区分本月新增消费和上月迟到调整。
十五、用故障注入验证计量链路,而不是只测成功请求
成功请求只能验证最简单路径。上线前应主动构造流式断开、首次调用超时后重试、主模型失败后降级、队列消息重复投递、结算事务中断、价格切换边界和预算并发竞争。每个测试都要同时核对业务结果、尝试数、用量事件数、分录数与预算余额。
下面是一组可以本地复现的验收定义。表中的“期望”是系统不变量,不是虚构的性能数据。
| 场景 | 注入方式 | 期望结果 |
|---|---|---|
| 普通非流式调用 | 返回明确用量 | 一次尝试、一条确认用量、一组结算分录 |
| 流式末尾含用量 | 分段返回并正常结束 | 内容持续转发,最终用量只写一次 |
| 客户端中途断开 | 关闭下游连接 | 记录断开与取消状态,不假设成本为零 |
| 首次读超时后重试 | 第一次延迟响应 | 保留两次尝试,未知用量进入待核对 |
| 主模型降级 | 主路由返回可降级错误 | 两次尝试分别绑定实际模型与价格快照 |
| 用量消息重复 | 同一事件投递两次 | 唯一约束阻止重复分录 |
| 预算并发竞争 | 同时提交多个大请求 | 原子预占阻止余额被并发穿透 |
| 价格生效边界 | 跨越快照生效时间 | 每次尝试绑定符合计费时点的快照 |
还应验证失败恢复。可以让结算任务在写入分录后、释放预占前终止,再次运行时应识别已有分录并只完成未结束步骤;让对账任务在处理一半文件时停止,重启后不应重复生成冲正;让价格配置缺失,系统应将调用标记为待结算,而不是按零成本通过。
测试环境不要调用真实付费模型来制造大量失败。可实现一个本地模拟上游,按脚本返回事件、用量、延迟与错误,并生成带请求标识的模拟账单。这样可以确定性重放边界条件。模拟器验证的是网关计量逻辑,不代表真实供应端行为;正式接入后仍需用少量受控请求核对实际字段和账单口径。
十六、报表应同时展示成本、证据状态和治理信号
只显示团队金额排行榜会把成本治理变成追责工具,也无法解释数据质量。一个可用报表至少应同时展示已确认成本、估算成本、待核对金额、预占金额、未分配成本、重试成本、故障转移成本和缓存节省估算。任何估算项都要明确标注,不能与账单确认金额使用相同视觉语义。
趋势图可以按团队、项目、环境、实际模型和调用目的切分,但维度组合要限制,避免高基数查询拖垮账本数据库。明细账本适合写入和审计,分析查询可通过增量任务同步到专门的分析表。同步水位线与最后成功时间也应展示,防止用户把延迟报表误认为实时余额。
异常检测应关注可解释指标,例如单请求成本超过项目阈值、重试成本占比上升、未分配成本增加、估算用量长期未确认、某个模型的本地估算差异突然变化。不要在没有基线时宣称“智能识别浪费”。告警规则应保存版本、阈值来源和处理人,处理结果回写事件,形成可审计闭环。
预算接近上限时,可以通知、限制并发、缩小最大输出或执行经过批准的模型降级,但策略必须由团队预先配置。成本系统不能擅自把高质量模型切换为低价路由,更不能在报告中把质量变化隐藏起来。每次策略触发都应写明原请求模型、实际模型、原因和预算状态。
十七、分阶段上线,先把账算清再自动阻断
第一阶段只做旁路观测:建立请求、尝试、用量和价格快照,生成报表但不影响调用。目标是验证供应端字段解析、流式采集、标签覆盖率和账单差异。此时发现缺失可以修正规范化逻辑,不会误伤业务请求。
第二阶段启用预算预占但只告警,不直接拒绝。观察估算上界是否合理、预占能否及时释放、未知调用会占用多久。若预估过于保守,正常流量可能频繁触发告警;若过于宽松,并发穿透仍会出现。应根据实际差异分布调整,而不是随意指定一个安全系数。
第三阶段才对经过确认的团队启用硬限制,并提供明确的失败码、剩余额度和申诉流程。硬限制上线前必须具备紧急额度调整、审计记录和回滚开关。系统故障导致预算数据不可用时,是默认拒绝还是有限放行,需要根据业务风险预先决定,不能在事故现场临时讨论。
每个阶段都要有验收条件。旁路阶段要求供应端明细可关联、价格快照覆盖完整、未分配成本低于团队约定阈值;告警阶段要求预占与结算可以在失败后恢复;阻断阶段要求并发测试通过、权限隔离有效、策略触发可追溯。没有达到条件就继续观测,不用为了进度提前打开自动阻断。
结语
多模型网关的成本归因不是从日志里统计几个 Token 字段,而是建立一条从业务请求、上游尝试、用量证据、价格快照到成本分录的完整链路。请求与尝试分离,才能解释重试和故障转移;确认用量与估算用量分离,才能区分账单事实和预算预测;价格快照与追加账本结合,才能让历史结果可复核;预占与结算结合,才能在并发流量下真正执行预算。
当一次费用争议能够沿着trace_id找到每个上游调用、实际模型、用量来源、价格版本和归属标签,并能通过供应端账单完成逐项核对时,成本报表才具备工程意义。在此基础上实施限额、降级和预算治理,团队得到的才不是一个看似精确的总数,而是一套可解释、可恢复、可审计的计量系统。