1. 收到账单那一刻Token 曲线背后的成本失控信号上个月打开Openlaw网关的成本账单时我第一反应是怀疑统计口径出了问题。月度Token消耗量环比涨了187%费用直接超掉预算线的六成同期业务调用量只增长了不到25%。翻译成大白话就是每处理一条请求花的钱都变贵了。Openlaw是我们团队内部搭的统一大模型网关——所有业务应用、智能体流程、后台任务要调用各家模型服务商都会先打到这一层由网关来做鉴权、路由、配额和Token计量。Token激增如果发生在业务服务器上大概率是用户量涨了发生在网关上根源八成在架构设计或调用治理。这篇文章记录我从看到账单到定位根因、再到落地优化的全过程东西不复杂但基本上都是真金白银换来的经验。1.1 我不是先看金额而是先看曲线形态收到账单邮件后我的习惯不是急着去压财务要明细而是先打开网关自带的监控面板把过去三十天的Token消耗曲线拉出来看。原因很简单账单金额是多因素叠加后的结果——实际请求量、输入输出Token的比例、缓存命中率、供应商单价差异——直接看钱你根本分不清大头在哪儿。曲线形态反而能快速告诉你事故的类型。我看到的第一件事是曲线既不是均匀上坡也不是随机毛刺而是两个非常清晰的“台阶”。第一个台阶出现在月初第3天日消耗从6000万Token直接跳到1.2亿之后稳定了整整一周第二个台阶在第11天又跳到1.8亿后面继续缓慢爬升。这种台阶式上涨说明不是流量突发而是某个常驻调用模式变了新增了定时任务、某块业务改成聊长上下文或者某个智能体的循环逻辑被放开。如果是毛刺型曲线基本是瞬时并发被顶上去了处理思路完全不同。把曲线按小时粒度展开后还有第二个发现凌晨2点到4点之间也有一批稳定消耗但那个时段业务侧根本没有真实用户。这说明有凌晨调度任务在跑或者是一些无效请求在反复打模型。这类消耗最隐蔽它不产生业务价值却每天稳定烧掉几百万Token是典型的“沉默成本”。1.2 把三类可疑流量打上标签光看总曲线不够我第二步是把所有流量按来源标签拆开。Openlaw网关本身支持为每个接入方分配AppKey我按照真实使用场景把它们归成三类C端交互型会话包括客服问答、助手类应用后台任务型包括文档批量总结、定时抓取、报表生成第三方集成指外部系统通过Webhook回调触发模型调用。拆分结果非常直接C端交互型会话占总消耗约80%后台任务占15%第三方集成单看量不大但单次请求消耗异常高。于是排查重心落到“一个普通C端会话为什么能吃掉这么多Token”上。这里我踩过第一个坑不能只按AppKey聚合还得在同一类流量里再按会话长度分桶。同样是客服问答有的会话聊2轮就结束有的一路聊到30轮。混在一起算平均Token你会得到一个看起来很正常的数字真正烧钱的那批超长会话反而被平均值稀释了。后来我在网关里加了会话级Token累积字段一眼就看到消耗排名前10%的会话吃掉了这一类近60%的Token。排查范围立刻从“所有流量”缩小到“超长会话”和“高频循环调用”两块。这一步是整个排查的转折点也是我后面决定做上下文压缩的直接依据。2. 顺着网关日志挖出来的四类 Token 黑洞2.1 多轮会话的上下文滚雪球第一类原因是所有做对话产品的团队都会遇到的经典问题多轮会话把历史消息全部塞进请求里一轮一轮滚下去上下文越来越长。Openlaw网关的日志里有些会话到了第40轮单次请求携带的上下文已经超过3万Token。就算后端的模型上下文窗口够大这3万Token也是实打实计费的。成本增长是平方级的。假设每一轮问答平均新增800个Token第N轮时单次请求的输入成本大约就是“固定系统提示词加N轮增量”。用户的第1次调用可能只要几百Token第20次调用可能已经上万如果这个会话继续挂机由定时任务自动续聊每一轮都是全新计费。更扎心的是很多团队调试功能时只测前三轮根本不会触发超长会话问题就被藏到了生产环境。我们在抽样日志里捞了一个典型会话一个用户断断续续问了5天问题累计48轮最后那几次请求每条的输入Token都在2.5万以上。这个会话在最后一周烧掉的Token比开头三周加起来还多。也就是说会话生命周期的后半程每花出去的一块钱里至少有七毛钱在重复传历史。2.2 工具调用循环导致“越干越费”第二类原因比第一类更隐蔽智能体的工具调用循环。Openlaw网关上接了不少智能体应用这些应用在执行任务时会反复调用搜索、查数据库、分析文档等工具。日志里我翻到过几次很夸张的调用链智能体为了回答“某条规定的有效期到什么时候”连续调了15次工具每次工具返回结果都长达1000多Token然后这个结果又被完整拼进下一次请求。最后那一条请求的输入超过2万Token而用户实际想要的答案第一轮工具结果里早就有了。问题出在智能体的“循环终止条件”上。很多智能体框架的默认逻辑是只要工具结果和当前问题还沾一点边就继续推理、继续调工具直到达到最大迭代次数。每一步都在往上下文里追加几千Token而这些追加内容大多是同一批数据的不同排列。网站在这个场景里的角色很尴尬它看得到Token消耗但看不到智能体内部状态机。所以我只能在网关侧做防御性限制——给单会话的工具调用次数加总量控制超过阈值强制返回把损失限制在可控范围内。这个限制不优雅但确实有效。2.3 超时重试引发的重复计费第三类原因要从网关本身的架构说起。Openlaw在设计之初负责对上游模型调用做超时控制和重试初衷没问题某家模型服务商偶尔变慢客户端等着急网关就自动换备选通道重试一次。但问题是我们只实现了“重试”却没有在重试之前先确认上一次请求到底有没有成功送达。有一次上游通道的响应时间超过了我们的重试阈值Openlaw判断请求失败自动重发了同一条请求实际上游已经在处理第一条请求了只是响应慢了几秒。于是同一条业务请求被执行了两次Token账单也记了两次业务侧却只收到一个结果。这类重复调用占总体消耗的比例不高大概5%左右但它是零价值消耗优先级必须排在高位。排查这类问题有个技巧重点看同一会话ID、同一时间窗口内有没有两条请求内容完全相同。网关日志里如果出现这种“双胞胎”请求多半就是重试逻辑在捣鬼。修复的方式很简单给每条业务请求加全局唯一请求ID重试时携带同样的ID在网关层做幂等过滤。2.4 模型路由失配杀鸡频繁用牛刀第四类原因在账单里看得最直接模型单价失配。Openlaw网关的默认配置是把所有请求都扔给能力最强的旗舰模型因为一开始团队想保证效果没有做分级路由。运行了一段时间之后我做了抽样分析发现大约35%的请求属于简单任务——提取一个日期、把一段话转成JSON、打一个情感标签。这类任务用轻量级模型完全能胜任价格却可能差3到5倍。举个例子同样是一万Token输入、五百Token输出旗舰模型的费用可能是轻量模型的五倍。如果能让这35%的简单请求走轻量模型总费用直接打六折。这个账算下来之后我才真正意识到网关不能只做“统一出口”还得做“分级出口”否则统一接入的便利性迟早会变成成本黑洞的温床。3. 为什么同样的调用量成本却差了三倍计价与缓存细节把四类黑洞都定位出来之后我开始做成本模拟结果发现即便把请求量压回到正常水平成本仍然比预想高不少。这时候我才醒悟问题不只是“调用太多”更是“每次调用太贵”。3.1 Prompt 缓存是个大金矿但默认没被利用现在很多主流模型服务商都提供Prompt缓存能力同一个请求如果前缀完全一致后续命中的部分会便宜很多有些甚至只有原价的十分之一。这个机制对大模型网关来说就是天然的省钱利器因为它最适合“多条请求共享系统提示词、共享同一批历史上下文”的场景。但Openlaw网关一开始并没有把这个机制用好。问题出在网关每次请求的Prompt头部都拼接了时间戳、请求ID、业务标识符这类动态字段。按缓存机制的要求只有前缀完全一致才能命中头部一旦变化整段前缀缓存全部失效。从服务商视角看我们几乎每一条请求都是“全新输入”按全价计费。后来我把这些动态字段统一挪到了Prompt尾部缓存命中率才从几乎为0提升到接近50%。这里要特别提醒Prompt缓存是按前缀匹配的所以任何带随机性或实时性的内容都不要放在头部。还有一种是团队内常见的操作失误——每周调整系统提示词版本版本一变旧缓存直接作废。为了兼顾迭代和缓存我在网关里给提示词做了版本化只有确认要发布新版本时才切换前缀避免把缓存周期切得太碎。3.2 输入输出差价与“隐藏的补全长度”第二个让我意外的细节是输入和输出的计价差异。绝大多数模型服务商对输出Token的单价要远高于输入Token有些甚至差到三倍以上。这意味着就算我们辛辛苦苦压缩了输入如果输出特别长成本依然下不来。于是我开始盯一个指标补全膨胀率也就是一次调用里输出Token数除以输入Token数。正常问答场景这个比值通常在0.1到0.3之间但在网关日志里有些智能体场景的比值竟然超过了1输出比输入还长。我顺着日志去翻发现是几个智能体在“思考过程”里把中间结果原样输出成最终回复有些甚至会把一段调试信息、工具日志也拼在里面发给终端用户。不光浪费Token体验也很差。这种输出膨胀的根因往往是提示词里要求“一步步详细推理”而没有限制输出格式。我把典型场景做了一个修剪在提示词里明确“只输出最终结论和必要依据”膨胀率立刻从0.8降到0.2左右。3.3 供应商计价模型的差异与网关记账误差第三个细节更偏向工程治理不同模型服务商对Token的计算方式并不完全一致。同一个中文字符在甲家的模型里可能算1个Token在乙家的模型里可能被切分成2到3个Token。网关侧做Token计量时只能按自己的方式预估于是记账误差就出现了。我对比过Openlaw网关记录的Token数和各家服务商账单上的Token数发现部分月份误差能到10%以上。这个问题不影响实际成本但会严重影响优化决策——如果网关记录的基线就是错的我们在压Token消耗时等于拿一把不准的尺子反复量长度。修复不复杂网关在收到每次调用后的usage回传字段时直接用服务商返回的真实Token数覆盖本地估算值再按应用和会话维度做累加。这样至少保证了后续分析的记账口径一致。这里我特别想强调一个经验网关这类中间层系统做计量一定要“以服务商回传为准”不要自己另起炉灶算一套两套数据并行的结果一定是两边都对不上。任务类型输入Token输出Token补全膨胀率优化手段客服问答短会话12002400.2保持现状客服问答40轮长会话300003000.01摘要压缩智能体工具调用16000120000.75循环限制、输出约束实体抽取简单任务5001200.24轻量模型路由4. 我落地的四板斧压缩、路由、缓存、限额定位问题和理解计价规律之后就进入动手优化阶段了。我并没有一上来就重写网关而是先做风险最低、见效最快的四件事每一件都能单独评估收益。4.1 上下文压缩把历史对话折叠成概要针对上下文滚雪球的问题我在Openlaw网关里新增了一个会话级上下文压缩器。核心逻辑不复杂当会话累计Token超过8000之后不再把完整历史追加进下一次请求而是先把超出窗口部分的历史交给轻量模型总结成概要然后只保留“最近两轮完整消息概要”装入请求。可能有朋友会问做总结本身不是也要花钱吗确实要但总结是一次性成本。拿一个40轮会话举例如果不压缩第41轮请求光是上下文就要3万Token压缩之后只需要3000 Token概要加最近两轮的1600 Token单次请求直接省了2.5万Token。就算把总结时花掉的2000 Token均摊进去长期看也是划算的。压缩器里有一个关键参数需要反复调历史保留窗口。窗口太大压缩省下的钱不够多窗口太小模型容易丢失早期的重要信息。我在客服场景里试过2轮、4轮、8轮三档最后选了4轮因为客服问答的有效信息高度集中在最近几轮更早的内容基本可以通过概要覆盖。4.2 基于任务难度的模型路由针对模型路由失配我在网关上实现了一个两层路由。第一层是规则路由根据请求里的业务类型关键词和接入方使用的接口名把明确属于简单任务的请求直接分发到轻量模型第二层是动态路由轻量模型返回时如果带上了低置信度标记网关自动把同一请求升级到旗舰模型再跑一次。规则路由我用的是非常朴素的实现一张任务类型到模型通道的映射表由各业务线自己上报。“实体抽取”“格式转换”“关键词分类”走轻量模型“复杂推理”“长文档分析”“多轮规划”走旗舰模型。拿不准的任务宁可先走旗舰模型也要保证质量不被优化动作拖垮。这一版路由上线后的收益很直观旗舰模型的调用量占比从100%降到62%但业务反馈并没有变差。原因是那38%的请求本来就只做简单转化轻量模型在单点任务上的表现并不差差的只是复杂推理和多步规划能力。4.3 语义缓存让相似请求只花一次钱针对重复问题我在网关上加了语义缓存。原理是每次请求进来先对Prompt做Embedding再用向量相似度去缓存库里找有没有高相似的请求如果余弦相似度超过0.95直接复用上一次的响应不再调用模型。这套方案特别适合问答场景里“用户换个说法问同一个问题”的情况。我们线上大概有15%的请求能命中语义缓存这15%是纯省下来的成本。不过这里有几个注意事项不是所有请求都适合缓存。凡是涉及实时数据、用户身份、当前时间的请求都必须绕开缓存。缓存键不能只用原始文本要带上业务线和会话ID避免跨用户的隐私内容泄露。必须有TTL过期策略我默认设了24小时避免旧数据被无脑复用。实现时踩过一个小坑Embedding模型本身也有Token消耗而且可能不算便宜。我一开始把每条请求都做Embedding结果缓存成本几乎吃掉了节省的钱。后来优化成“先做请求文本的简单哈希命中哈希白名单再做语义匹配”只有前几次完全相同的请求才进入语义流程成本才算降下来。4.4 网关侧配额与熔断把成本锁在预算内最后一道防线是配额与熔断。Openlaw网关开始支持按应用维度配置每日Token预算——比如客服应用每天上限2000万Token达到上限后网关直接返回429错误由应用方申请提升预算再放开而不是默默烧钱。实现上用Redis存计数器核心逻辑不长import redis r redis.Redis(hostredis.internal, port6379, decode_responsesFalse) def consume_quota(app_key: str, used_tokens: int, daily_limit: int) - bool: key fgateway:quota:{app_key}:{date_today()} pipe r.pipeline() pipe.incrby(key, used_tokens) pipe.expire(key, 86400) current, _ pipe.execute() return current daily_limit方案技术上很简单但它的治理意义远大于代码量本身把成本控制从“事后看账单”变成了“事前干预”给业务方一个明确预算上限超限不再是无感支出。跟配额配套我还加了每分钟调用数监控和告警正常波动阈值内不打扰一旦超过阈值立刻把消息推到负责人群里。这一板斧不替你省钱但能保证你不会在某天早上醒来突然发现账单爆炸。5. 下一步想做的研究方向优化上线到现在三周成本已经稳定在预算线以内四板斧解决的是“过去积累的问题”。运营过程中我又观察到几个新问题这也是接下来准备深入研究的方向。5.1 按业务线拆分的 Token 预算矩阵现在的配额是单应用单维度但实际业务是立体的同一个业务线可能同时跑三个应用三个应用共享一个总预算有些应用白天量小、凌晨有批处理任务如果只按“应用”配额度夜间时段容易出现额度空置。我准备做一张“业务线×应用×时段”的预算矩阵用历史数据自动算出每个格子的基准额度并支持月度滚动调整。比如客服线工作日白天给大头文档线凌晨批处理时段给大头额度如果没用完可以在月末结算时跨格子转移。这样能把每一分预算花在正确的时间窗口而不是每天一大笔额度在低峰期空转。5.2 Token 利用率UTL作为核心指标Token利用率是我最近特别关心的指标。它衡量的是模型输出的Token里有多少真正被下游任务采纳了。比如模型生成了500字总结但业务只用其中一句核心结论那另外480字就是浪费。顺着这个思路我准备在智能体场景里加一个“输出采纳率”埋点。可以在网关的日志里记录模型完整输出再对比业务端实际使用的文本长度自动算出每条请求的UTL。定位到UTL特别低的任务环节后优先优化那些环节效果可能比单纯压缩输入更好。5.3 全链路成本归因从请求到 Prompt 片段现在的监控只能看到“一次请求花了多少Token”看不到“这些Token花在系统提示词、历史消息、工具结果、当前输入各占多少”。我计划在网关日志里按Prompt片段做标记然后输出成本构成报告。比如一份报告会告诉业务方“你的历史消息占了60%的成本建议开压缩”“你的工具结果平均每次占3000 Token建议做结果截断”。这类建议如果能自动生成业务方就不需要理解网关内部实现照着建议改就行。这个方向本质上是把优化能力产品化从“网管帮你调”变成“网关告诉你哪里该调”。5.4 结构化输出约束与 Token 省流最后一个方向是优化输出端。现在很多调用都是让模型自由发挥文本业务端再做结构化解析这个过程的Token浪费很大。我准备针对高频接口强制注入JSON Schema或函数调用规范让模型直接输出结构化结果既省Token又减少解析失败。顺着这个方向我还想研究“输出预算”机制给每次调用设置最大输出Token限制超过就截断并触发一次二次压缩防止输出膨胀。很多模型支持在请求参数里设置max_tokens但有没有一种更智能的动态机制让它根据任务类型自动决定输出上限而不是所有请求都统一给一个最大值这需要收集更多数据才能做准。优化成本这件事我现在的体会是它不是一次性工程永远会有新场景、新模型、新业务逻辑冒出来。但只要网关这个“统一出入口”的观测和治理能力在每一笔钱花到哪里都是可以追溯的。下一步我最想跑通的还是Token利用率这个指标——它可能会暴露很多我们目前没意识到的问题。等有结论了我再写一篇跟各位分享。