AI应用Token成本失控?从网关优化到上下文减负的实战指南
2026/9/7 14:05:00 网站建设 项目流程

Openlaw 平台上线一年多,我一直负责网关这一层。过去几个月遇到了这几年最头疼的事:token 消耗像开了闸一样涨,月末账单直接超标,运维群里炸了锅。当时所有人的第一反应都是"是不是被刷了"或者"模型服务商计价出错了",但查到最后发现,问题全出在我们自己身上。

这篇文章把我从排查、止血、再到系统性优化的全过程整理出来了,涉及网关层怎么做 token 计量、请求入口怎么限流、上下文怎么减负、模型路由怎么省钱,以及优化过程中踩过的各种坑。不管你是做 AI 应用开发的,还是负责网关基础设施的,只要你用的是 token 计费的模型服务,这些思路应该都直接用得上。

1. 先把问题说清楚:Openlaw 网关到底在干什么

1.1 网关在 AI 应用里的定位

Openlaw 是一个面向法律场景的 AI 应用平台,用户会做法律咨询、合同审查、文书生成这类事情。在技术架构上,前端服务后面挂着一层网关,所有的模型请求都会经过这里,由网关负责把请求转发给不同的模型服务商(OpenAI、Claude、智谱 GLM 等),同时做鉴权、计量、限流和日志记录。

我当时把网关的职责归纳成了四件事:

  • 路由:根据业务类型把请求发到合适的模型,避免所有流量都打到同一个后端
  • 计量:统计每次请求消耗的 token,按用户、按功能模块记账,这是成本核算的基础
  • 保护:限流、熔断,防止异常流量打垮下游模型服务,也防止成本失控
  • 观测:输出完整的调用链日志,方便事后回溯问题

这四件事里,计量是最容易被忽视但出事时最要命的。因为 token 计量直接关系到成本核算,如果这里的数据不准,你根本不知道钱烧在哪里。我们的网关其实一直都有日志,但平时只看接口成功率、延迟这些常规指标,token 消耗没有纳入每日监控,所以出问题时才发现数据是一片空白。

1.2 Token 成本超标的直接表现

事情是从一份月度账单开始的。运营同事把账单截图发到群里,当月模型 API 的费用是上个月的 2.8 倍,直接超了预算 80%。我当时第一反应是"不可能吧",因为业务量并没有明显增长,DAU 数据也就比平时多了 10% 左右,按理说成本不应该有这么大的变化。

为了快速止血,我干的第一件事是去网关日志里拉出 token 消耗的时序数据。不看不知道,一看吓一跳:日均 token 消耗从平时的 1200 万左右,两周内飙到了 3800 万,翻了 3 倍多。而且不是缓慢上升,是某次发布之后突然跳上去的。这个特征很关键——不是流量自然增长导致的,八成是某个版本改动引入了问题。

这里也提醒大家一个经验:AI 应用的成本监控不能只看账单,必须看实时指标。等月末账单出来再发现异常,早就来不及了。我在这次事故之后,把"token 消耗"和"单请求平均成本"这两项加到了每日巡检列表里,一旦出现超过 30% 的环比波动就立即告警。

2. 排查过程:Token 到底烧在哪里

2.1 从账单和日志反推消耗分布

排查的第一步,是建立"成本消耗的分布视图"。我把网关里的调用日志按这几个维度做了聚合:

  • 按功能模块分组(法律咨询、合同审查、文书生成、法条检索等)
  • 按模型分组(GPT-4、Claude、GLM 等)
  • 按请求类型分组(新对话、多轮对话、文档处理)
  • 按用户分组(找出异常消耗的大户)

这些聚合结果一出来,问题基本就有眉目了。下面是当时整理的一张简化表,按日均 token 占比排序:

功能模块平时占比出问题时占比变化
法律咨询(多轮)40%35%持平
合同审查(文档解析)20%45%突增
法条检索25%12%下降
文书生成15%8%下降

合同审查模块的 token 消耗涨幅最夸张,占比从 20% 直接跳到 45%。这个信号说明,问题出在文档处理这条链路上,而不是常规对话链路上。顺着这个线索往下挖,事情的真相才开始浮出水面。

2.2 揪出三类典型的 Token 浪费场景

顺着合同审查链路继续查,最终锁定了三个问题,每一个单独拿出来都够让人头疼的。

第一类是"系统提示词膨胀"。某个版本更新后,为了提升合同审查的准确率,团队往系统提示词里塞入了一份很长的"审查规则",里面包含了各种法律条文和行业规范,总共约一万八千字。这份规则在每次请求时都会作为前置 token 发送。按中文 token 换算,一万八千字大概是 1.2 万到 1.5 万 token,这个开销在每次合同审查请求中都固定发生。哪怕真实的合同正文只有三四千字,系统提示词比用户内容还多好几倍,大部分成本就这么白白浪费了。

第二类是"文档重复注入"。合同审查的流程是用户先上传合同,系统把合同内容送到模型分析,然后进入人机多轮问答。最初的设计是每一轮问答都把合同全文重新发送一遍。一次合同审查平均 5-8 轮问答,一份三万字的长合同就会被重复发送 5-8 次。按中文 token 估算,三万字大概是 2 万到 2.5 万 token,重复 6 次就是 12-15 万 token,单次合同审查的成本直接高了一个数量级。

第三类是"重试风暴"。网关对模型服务设置的是 3 秒超时,模型响应一旦超过 3 秒就判定超时并重新发送请求。遇到模型服务端负载高的时候,一个请求可能在短时间内连续重试 3-4 次,每次重试都是全量的 token 支出。当时日志里有一个典型 case,同一个请求在 12 秒内重试了 4 次,最后一次成功时,总共烧掉了 5 倍多的 token。

2.3 定位这些问题用到了什么工具

说实话,定位这些问题并不需要什么特别高深的工具,关键是"日志要全、聚合要快"。我当时用到的几个方法论分享一下:

  • 网关日志要带着 model、prompt_tokens、completion_tokens、total_tokens、request_id、user_id、module、error_code 这些字段完整落库
  • 用 ClickHouse 存调用日志,查询快,聚合方便,亿级数据量查起来也就几秒
  • 每个请求的系统提示词版本号记录下来,方便对比不同版本之间的 token 消耗差异
  • 对长文本请求,单独记录输入字符数和估算 token 数,用于做异常断言和告警

这里我想重点强调 prompt 版本号这个点。之前我们一直没给提示词做版本管理,改一次就是直接改线上配置,导致出问题时根本不知道是哪个版本引入的。后来我们建了一个简单的"提示词版本表",每次改动都会记录版本号、变更内容、操作人,网关日志里也会带上当前版本号。就这么一个简单的动作,后面排查类似问题的时间缩短了一大半。

3. 优化方向一:请求入口的"节流"设计

3.1 请求级别的 Token 预算控制

排查清楚之后,我先做了最紧急的止血动作——在网关层加请求级别的 token 预算控制。这个方案是最快能落地的,因为不需要改业务代码,只改网关的转发逻辑。

具体逻辑是这样的:网关收到一个请求时,先估算这个请求的输入 token 数,如果超过预设的上限就直接拒绝或者走降级流程,不发给模型服务商。估算方法很简单,中文按"字数 × 1.3"粗算,英文按"字符数 ÷ 4"粗算,不追求精确,但能挡住最离谱的请求。

对于 Openlaw 的场景,我设置的初始阈值是:

  • 输入 token 上限:6000(单次请求)
  • 输出 token 上限:2000(单次响应)
  • 单用户单日 token 上限:5 万
  • 单功能模块单日 token 上限:100 万

这样设置之后,那几类最严重的"文档重复注入"请求在第一道关口就会被拦下来。当然,实际业务中不能光拦截,拦截之后要给出友好提示,比如"该请求上下文过长,请缩短文档或重新上传",保证用户体验不至于断崖式下跌。

3.2 用户级别的配额与熔断

请求级别控制只是第一层,用户级别的配额和熔断同样重要,尤其是对于自动化脚本、异常客户端这一类"无底洞"消耗来源。

我们遇到过一个真实 case:某个企业客户的集成脚本出了 bug,在循环里反复调用合同审查接口,一个晚上跑了 300 多次,消耗了 800 多万 token,直接烧掉了几千块。如果没有用户级配额,这种事故会反复出现。

在网关里,我为每个租户和用户维护一个 token 日配额计数器。请求经过网关时,把本次请求的 token 数累加到该用户的当日计数中。当计数超过配额的一定比例(比如 80%)时,返回一个告警头;超过 100% 时,直接拒绝请求并返回配额超限的错误。同时给运营后台加了配额调整入口,方便销售或客服针对重点客户临时调整上限,避免误伤正常业务。

3.3 超时与重试策略调整

超时和重试是 token 浪费的重灾区。我之前的默认超时是 3 秒,这对流式响应来说太短了,因为模型的首字延迟(TTFT)经常在 1-5 秒之间浮动。一个响应可能模型已经生成了一部分,但网关因为超时就把整个请求重发了,那已经生成的 token 等于白烧。

流式请求的超时判断不能只看"整个请求是否完成",要区分"首 token 到达时间"和"相邻 token 间隔时间"。正确做法是:

  • 首 token 超时设为 20 秒(具体看模型服务的 P95 延迟)
  • 相邻 token 的最大间隔设为 30 秒
  • 重试次数限制为 1 次,重试采用指数退避,初始间隔 2 秒,最大 30 秒

关键点是:不是所有错误都值得重试。如果模型服务返回的是 429(限流)或 5xx(服务端错误),重试有意义;如果是 400(参数错误)或 401(鉴权失败),重试是没用的,应该直接返回错误。我们的网关在重试前会判断错误码,只对可重试的错误码做补偿。

提示:流式响应场景下,超时判断的粒度要细化到"两个 token 之间的间隔"。一开始我们只设置了整体超时,结果首 token 到达很快,但中途卡住了,整体超时又没到,客户端就一直挂着,模型还在后台继续生成,最后白花了一堆输出 token。

4. 优化方向二:上下文管理的"减负"策略

4.1 系统提示词的压缩与蒸馏

止血之后,就要开始做长远的优化了。系统提示词是最容易被忽视的成本点之一,因为它在每次请求中都会重复消耗,积少成多,金额相当可观。

我们的做法分三步:

  1. 把系统提示词拆成"基础指令"和"领域指令"两层,基础指令每次都发,领域指令只在特定场景触发时才发
  2. 对领域指令做蒸馏,把冗长的规则描述改成结构化的、简洁的条目,删掉重复和修饰性语言
  3. 把大段的法条原文从提示词中移除,改为按需检索后注入到用户输入部分

以合同审查的系统提示词为例,改造前它大概是这样的(示意):

你是一个专业的合同审查专家。请对以下合同进行审查,重点关注: 1. 合同主体资格审查,包括签约方的资质、信用状况、经营范围等…… 2. 合同条款合法性审查,包括价款条款、履行期限、违约责任等…… 3. 违约责任条款审查,包括违约金比例、赔偿范围、争议解决方式等…… (长度:约 18000 字,包含大量法条原文和解释性内容)

改造后变成:

角色:合同审查专家 任务:识别合同中的高风险条款并提出修改建议 关注点:主体资格、合法性、违约责任、争议解决 输出格式:问题条款 -> 风险等级 -> 修改建议 (长度:约 800 字,法条原文全部移除,改为内部知识库按需检索)

这个动作的效果非常明显,合同审查模块的单次输入 token 从平均 2 万降到了 6000 左右,成本直接降了 60% 以上,而且因为干扰信息少了,审查意见的质量反而更稳定了。

4.2 对话历史的滑动窗口与滚动摘要

多轮对话是另一个 token 消耗大户。用户在咨询一个法律问题时,往往会问十几轮甚至几十轮,每次请求都把全部历史对话发送给模型,上下文就会越长越大,直到撑爆模型上限。

我们实现的方案叫"滑动窗口 + 滚动摘要":

当对话历史的总 token 数超过阈值(比如 8000)时,把最早的一部分对话压缩成一段摘要,用摘要替换原始对话内容,同时保留最近 N 轮完整对话。这样既不会丢失关键信息,又能把上下文控制在合理范围内。

具体做法上,摘要由模型在对话进行中异步生成。当某一次对话结束后,如果历史 token 数超过阈值,后台任务会调用一次摘要模型,把旧的对话压缩成一段 300-500 字的中文摘要,存入会话状态。下一次请求时,网关用"摘要 + 最近 N 轮完整对话"作为上下文,而不是把全部历史都塞进去。

这里有一个经验总结:摘要必须做 token 预算控制,建议摘要输出控制在历史 token 总量的 10% 以内。否则摘要本身可能比原始对话还贵,得不偿失。另外,摘要生成要放到异步任务里,不能卡在主请求链路上,否则会增加用户可见的延迟。

4.3 法律条文与案例的检索式输入

Openlaw 这类法律场景有一个特点:模型并不需要知道全部法条,只需要针对当前案件引用相关的法条和案例。如果直接把整部法律文本塞给模型,成本高且准确率未必高,因为模型的注意力会被无关内容稀释。

我们把这条链路改成了 RAG(检索增强生成)模式:

  1. 先用 embedding 模型对用户的问题做向量化
  2. 从法条库和案例库中检索 top 5-10 条相关内容
  3. 把检索结果组装成"参考资料"段,注入到系统提示词后面
  4. 模型只根据注入的参考资料来回答

这个改动后,法条检索模块的单次请求 token 从 8000 左右降到了 2000-3000,而且答案的准确率反而提升了,因为模型不再被大段无关法条干扰。

很多人做 RAG 容易犯一个错误:把检索到的内容原封不动地全部塞进去。实际上,检索结果里往往有大量冗余信息,应该再做一步压缩和去重,只保留与问题高度相关的段落。我们在检索后加了一个轻量级的"剪枝"逻辑:对每个检索段落算一个相关度得分,低于阈值的段落直接丢弃,保证注入的参考资料不超过 1500 token。

5. 优化方向三:响应侧的成本优化

5.1 模型路由与分级调用

不同模型的价格差异非常大,动辄差一个数量级。对于 Openlaw 这种有大量简单问答场景的平台,统一使用高端模型是巨大的浪费。我们做了一个"模型路由层",根据请求的复杂度把请求分发到不同价位的模型。

简单的判断维度包括:

  • 功能模块类型:法条检索、简单问答用低价模型;深度分析、合同审查用高端模型
  • 用户身份:免费用户走低价模型,付费会员走高端模型
  • 输入长度:短文本用低价模型,长文本、复杂文档用高端模型
  • 意图分类:用一个便宜的分类模型先做意图判断,再决定路由

这里的成本收益要算清楚。假设一个简单问答用高端模型需要 0.05 元,用低价模型只需要 0.005 元,一天 10 万次简单问答,每天就能省 4500 元。这个优化在一个月内就能把之前的成本超支全部追回来。

5.2 缓存层的设计与落地

缓存是 Token 成本优化的另一个大头。Openlaw 的一些问题在法律场景中非常典型、高频,比如"合同违约怎么处理""离职补偿怎么算"。这些问题每天被问上千遍,每次都要调用模型白算一遍,非常浪费。

我们实现了两级缓存:

  1. 精确缓存:请求的 token 序列完全一致时直接命中缓存,返回历史响应
  2. 语义缓存:请求与历史请求的语义相似度超过阈值(余弦相似度 > 0.9)时,返回历史响应

语义缓存的核心是 embedding 向量。每次请求到达网关时,先用一个轻量 embedding 模型算出请求的向量,然后在缓存中做相似度搜索。命中缓存时,网关直接返回缓存的响应,不再调用下游模型。

实际运行中,语义缓存的命中率在 15%-25% 之间,具体取决于业务分布。对于高频重复问题较多的业务,命中率会更高。虽然语义缓存有一定的误判风险(相似问题可能答案不同),但我们通过设置较高的相似度阈值(0.92-0.95)和增加业务模块维度约束,把误判率控制在了可接受范围内。

5.3 限制输出 Token 的指令与参数

输出 token 的控制往往比输入更难,因为输出是模型自己生成的,不好精确控制。但通过参数和指令仍然可以显著压缩。

在网关层,我们固定了每个功能模块的 max_tokens 参数:

  • 简单问答:max_tokens = 300
  • 合同审查意见:max_tokens = 800
  • 深度法律分析:max_tokens = 1500

同时,在 prompt 中明确要求"回答简洁,控制在 200 字以内"。模型虽然不一定严格遵循字数限制,但实际效果是响应长度平均下降了 40% 左右。

还有一个容易被忽略的点是停止符的使用。对于结构化输出(比如 JSON 格式的审查意见),我们在调用时设置 stop 参数,让模型在输出完必要的结构化内容后立刻停止生成,避免额外输出解释性文字。这里可以省下大约 20%-30% 的输出 token。

6. 常见问题与排查技巧实录

6.1 日志中的典型异常特征

在优化过程中,我们积累了一套快速识别的特征模式,整理成了一张速查表:

异常特征可能的根因快速处理方式
某功能模块 token 占比突然上升提示词改动、文档重复注入对比最近一次发布的内容改动
单请求平均 token 持续上涨对话历史未清理、上下文无限增长限制最大轮数或启用滑动窗口摘要
重试率超过 5%超时设置过短、模型服务端波动调整超时参数、限制重试次数
某用户 token 消耗异常高脚本 bug、自动化批量调用用户级配额熔断
输出 token 远超预期缺少 max_tokens 限制、prompt 未约束设置响应长度上限、添加停止符

6.2 优化上线后的效果观测

优化不是一锤子买卖,上线之后必须持续观测效果。我们建立了两个核心监控指标:单请求平均 token 消耗(成本效率指标)和单请求平均成本(折合成人民币)。这两个指标如果出现异常波动,就会触发告警。

在优化方案全部上线后的第二周,我们做了完整的数据对比:

指标优化前(峰值)优化后降幅
日均 token 消耗3800 万780 万79.5%
月度成本超标 80%预算的 70%显著改善
p95 响应延迟4.8s2.6s提升 45%

6.3 容易忽略的四个成本陷阱

最后分享几个在这次优化中发现的、特别容易忽略的成本陷阱,都是我踩过的坑:

  1. 部分评测流量绕过网关直接调用模型服务,导致这部分成本完全不在网关的计量范围内。排查成本账单时,这部分流量查不到,很迷惑人。解决办法是统一所有模型调用都经过网关,禁止任何裸调用,这个约束要在代码评审里卡死。

  2. 定时任务没做错峰。Openlaw 有一些后台定时任务,比如定时汇总用户案件信息、生成周报等。这些任务如果在下班高峰期集中执行,因为模型服务端的负载和限流,成本会更高、成功率也会下降。建议把这类任务的执行时间错开,安排在凌晨低峰期。

  3. 流式输出与客户端断开。流式输出时,如果客户端中途断开了连接,模型可能还会继续生成一段输出,这部分 token 是白花的。网关层应该在检测到客户端断开时,主动向模型服务端发送终止信号,不能让模型继续生成。

  4. 中英混排的 token 开销。法律场景经常出现中英混排文本(比如合同包含英文条款),混合文本的 token 数会比纯中文高不少。如果可能,尽量要求模型用单一语言输出,或者在预处理阶段做语言分割,把不同语言的段落分开处理。

7. 写在最后的一点体会

这次优化做完,我的一个核心体会是:Token 成本优化不是一次性项目,而是需要持续经营的系统工程。它需要网关层有完整的数据,业务层有合理的 prompt 设计,模型层有聪明的路由策略,三者缺一不可。

还有一个比较颠覆我认知的地方是,省 token 和提升质量很多时候并不矛盾。我们以为把上下文压缩了、把法条改成检索式输入,模型质量会下降。但实际上,因为输入信息更聚焦、干扰更少,模型回答的准确率和用户满意度反而提升了。这也让我更坚定了一个原则:给模型的信息应该是"少而精"的精选集,而不是"大而全"的资料库。

踩过这么多坑之后,我现在做任何 AI 应用的改动,都会先问一句:这次改动的 token 消耗影响是什么?如果说不清楚,那就说明还没有为成本做过设计。希望这篇分享能让大家少走一些弯路,把省下来的钱花在真正有价值的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询