news 2026/10/10 4:35:26

省token省过头,一次重试就全赔:AI应用重试机制成本优化指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
省token省过头,一次重试就全赔:AI应用重试机制成本优化指南

把标题里这句话贴到项目群的时候,反馈清一色是“+1”。

AI 应用上线后的成本大头,很多时候不是你 prompt 设计得不好,而是重试机制在替你疯狂烧钱。你可能刚把单次请求的 token 消耗从 1600 压到 700,自信满满地跟大家说成本降了一半多,结果某个晚上模型服务抖动,SDK 默认重试三次,用户的客户端又手动刷新了两次,几分钟内烧掉的 token,比你前天省下来的全部家当还多。这不是段子,是真实发生过的线上事故。

这篇文章不聊虚的,核心就两件事:第一,重试到底多费 token,费在哪;第二,怎么设计重试机制,才能不把省 token 的成果变成给 API 厂商的赠礼。全程用我自己踩过的坑、算过的账来讲,适配正在做 AI 应用、接大模型 API、或者自己本地部署推理的同学。

1. token 计量和重试放大的底层逻辑

1.1 先看清计费模型里,哪一部分钱最伤

大多数 LLM API 的账单都是两条腿走路:输入 token 和输出 token,分别按不同的单价计费。输入侧包含系统提示词、多轮对话历史、检索回来的知识片段、用户这次发的消息;输出侧就是模型生成的内容。很多模型的输出价格是输入的三倍甚至更多,这就是为什么大家最先想到的省钱手段往往是压输入——因为输入量大,单价低但总量占比高,可操作空间非常可观。

但这里有个很多人没意识到的细节:输入侧的计费不是简单地按“你最终发出去的那一段字”收费,而是按模型真正处理过的 token 来算,模型处理到一半如果失败,已处理的输入也是要认账的。推理服务商通常会对连续请求中的相同前缀做缓存优化,如果你这次请求和上一个请求从系统提示词开始就完全一致,可以拿到一定折扣甚至免费用量;可一旦失败发生了,服务端保持的上下文很可能被释放掉,缓存能不能保住完全看平台实现。于是重试基本就是拿全价重新走一遍请求链路。这也解释了为什么“省 token”这个课题必须跟“重试策略”放在一起讨论,而不只是单纯比拼 prompt 字数。

1.2 一次重试到底会把成本放大多少倍

先看一个最常见的失败分布:请求发出去,服务端已经收到并开始预填充你的提示词,预填充到一半网络断了,或者服务端报错,连接直接断开。这时候你输入的 token 已经计费了一次,服务端照常收你这一份钱;如果请求是生成阶段才断开的,那么已经生成的这部分输出 token 也会一并计入账单。也就是说,重试并不是“原路再走一遍”,而是“多付一份几乎相同的全价请求”,可能还会附上上次失败的部分残值。

用生活化一点的说法:省 token 像在冰箱里囤菜,你觉得囤得够多、省得够狠,结果每隔一段时间因为火候不对就得倒掉一盘重新炒,每倒一盘都把冰箱里的存货又搭进去一点。重试的次数一旦放大,省下来的那点本钱在几分钟内就能被倒回去好几盘。实际项目里最典型的放大路径有这么几条:SDK 默认自动重试三次,应用层发现问题后又自己重发,用户看到界面报错再手动点一次“重新生成”,三层叠在一起,原来一次请求就变成了四到五次同等消耗的请求。如果上下文很大,那就是四五倍地乘以你拼装的整段 prompt 和上下文。

每次重试都是以原请求为分母做乘法增长。所以做成本优化的人不能只把注意力放在“单请求 token 缩减”上,还得回头算算,如果某个接口的错误率是 1%,重试两次,线上实际成本会变成多少。这个账,下面有具体的数字演示。

2. 常用的省 token 手法,为什么都容易踩重试的雷

2.1 压缩系统提示词,压出的是语义稳定性缺口

压缩提示词是最直接的省钱方式,但也最容易跟重试形成共振。原因很简单:prompt 里删掉的每一个边界条件,都可能在未来某个请求里变成模型自己的自由发挥,一旦输出不符合业务规则,程序会自动重试,或者触发用户手动重试。这种重试并不会因为你的提示词变短而变小,它照样按新请求完整收费。更麻烦的是,批量自动化流程里通常没有“人工看一次输出”这个环节,质量校验失败后可能连着重试几次,省下的输入 token 早被多出来的输出和重复请求吃干净。

我见过最典型的例子是一个文本分类服务,调用方为了让请求更便宜,把原来一条条列举的分类边界规则压成了三个词,结果模型把若干个高度相似的类别混在一起,线上红灯每小时触发一批重试。每一批重试都用完整分类规则去请求,本来只需一次跑完的事情,最后结算下来费用不但没降,反而在高峰期翻了一倍。这类问题不是说不能压缩 prompt,而是压缩时要留足测试集,否则省的钱通常只在账面上显示,真实成本全藏在重试渠道里,普通监控根本看不见。

2.2 截断对话历史,裁掉的是抗失败的能力

另一派常见的省 token 操作是截断对话历史,把 20 轮历史全部截断到最近 5 轮,或者干脆只保留最后一条。表面上每次请求的输入 token 少了很多,但随之而来的问题是上下文信息断裂。模型回答的时候记不起用户最开始提过的需求,生成结果跑偏,多轮对话里一出现偏航,用户的常规操作就是“你重新回答一下”。

注意,用户点一次“重新回答”和系统自动重试,表面上都是调用一次 API,但成本计算方式并不总是一样的。如果业务代码规定重试时必须把完整上下文拼回去,或者因为裁剪逻辑只在正常请求里生效,而重试路径直接走未裁剪的原始序列,那么一次重试的成本就会比正常请求还高。这就是标题里“省 token 省过头,一次重试就全赔回去”的常见现场:正常时你拿浓缩摘要发请求,失败那条却意外拿着全文去补发,浓缩省多少钱,重试就按原价花多少,甚至倒贴。

2.3 限制 max_tokens,输出被切断的次数反而变多

还有一种做法是 max_tokens 设置得过于紧张,比如明明要生成 500 字的内容,只给了 300,结果大量请求在生成中途被强制截断,业务拿到的内容不完整,于是自动进入重试流程。这种重试除了把输入重新付一遍,还把浪费掉的输出也付了一遍——第一次已经生成的那 300 token 不返还,第二次生成完整的又收一份。更难受的是,截断本身不会让模型学会写简洁,你只是把问题从“生成太多”转变成“生成不够”,而重新生成往往对着同一份完整上下文再来一次,成本不减反增。

所以 max_tokens 一定得按实际输出分布去设置。最稳妥的办法是先在开发环境里跑几百条真实输入,统计输出 token 的 p95,然后再往上面加一点缓冲。宁可让它生成完整,也不要靠短 max_tokens 硬扛,不然就是在给重试机制创造开工条件。

3. 给重试机制装刹车:错误分类、退避与预算

3.1 把错误先分门别类,不能一律重试

重试的第一原则,是判断这次失败“有没有重试的价值”。很多 SDK 默认的策略真的无脑,什么错都给你重试几遍,这在鉴权失败、参数错误面前完全无效,还要一遍遍浪费网络开销和账目成本。我自己在实践中会把错误粗暴地分成三类,哪一类怎么处理,在测试环境里提前排查好。

类别典型错误处理策略
可重试超时、502/503、连接重置、限流指数退避后重试,限流必须先读取 Retry-After
不可重试400 参数非法、401 鉴权失败、403 无权限、refresh_token 为空、token 失效不发起 LLM 调用,直接刷新登录态或回错误给业务层
条件重试模型返回质量不合格、内容被审核拦截、显存不够、模型未加载换模型、改提示词或等待模型就绪后再重试,不能原地硬碰硬

后面第 5 部分会逐条展开那些真实报错,这里先记住大方向:每多一次无条件重试,就是在给成本池里加一层溢价。把有条件的重试当成真正的业务逻辑去排查,而不是丢给统一重试中间件一把梭。

3.2 指数退避公式、抖动和重试次数上限

重试要说绝对不做也不现实,网络抖动、偶发超时就是需要重试的真实场景。问题是怎么重试。直接用固定间隔重试三次,是方案最省事但也是最容易触发“重试风暴”的做法,最好换成指数退避加随机抖动。

import random import time def next_delay(attempt: int, base: float = 1.0, cap: float = 30.0) -> float: exp_delay = min(cap, base * (2 ** attempt)) return exp_delay + random.uniform(0, max(0.1, exp_delay * 0.25)) # 示例:第 0 次失败后约等 1 秒,第 1 次约等 2~2.5 秒,第 2 次约等 4~5 秒 for attempt in range(3): delay = next_delay(attempt) time.sleep(delay)

抖动那一点随机时间,看着不起眼,关键时刻能避免一大帮客户端在同一秒扎堆重试。重试次数我通常上限就设 2 次,极端情况最多 3 次,尽量用常量集中管理,方便哪天真出事故时一改生效。这里的“上限”不仅指单次 API 调用的尝试次数,也指同一个事务整体允许重试的次数,两个维度都得锁死。

设置重试次数的逻辑也带数学味道:假设每次重试成功的概率是 p,那么两次重试带来的边际收益是 (1-p) 的概率能捞回来一次成功,但边际成本是货真价实的一整份请求。如果成功率持续低于 60%,多出来的重试基本就是烧钱。很多线上故障中服务一直处于半死状态,重试再多也只是把费用烧高,不会把故障治好。

3.3 给重试做台账:请求存档、幂等键和可视化

重试要长记性,最好给每次请求建立台账:唯一 request_id、prompt 的哈希、请求时间、模型名、输入/输出 token 估算、失败原因、这是第几次重试。很多项目在出问题的时候查不到 token 用量,对不上账,说的就是这种情况。没有台账,你就无法回答“到底哪次重试吃掉了预算”这个根本问题。

配合台账可以做幂等键。幂等键就是业务侧生成一个固定 ID 传给服务网关,网关发现同一 ID 的请求已经在处理或者已处理完,就不要再重复调用模型。AI 生成接口虽然每次内容可能不同,但业务上如果同一个用户在同一时刻点了很多次按钮,应用层最好能把这些重复请求合并掉,否则每一次点击都是一次全价调用。虽然不是严格意义上的重试,但它在账面上产生的效果和重试一模一样。

操作层面我还会加一个“重试预算”指标:一个窗口期内允许的重试次数除以总请求数,超过 2% 就开始告警,超过 5% 直接把重试策略降级为单次且不重试,等待人工介入后再恢复。这种保护看起来老土,在真实事故里救过我好几次,没有熔断设置的话,自动化重试会让故障费用和时间一起线性上升。

4. 省钱和可靠兼修的正确姿势

4.1 给恢复路径准备轻量摘要,而不是把完整历史背在身上

现在回到主题:怎么既保证失败之后能恢复,又不让恢复请求变成“全价再来一次”。我的首选方案是给长对话维护一份增量摘要。每聊几轮,就把前面的内容用模型或规则生成一段摘要,比如控制在 500 token 以内,新请求正常只用“摘要 + 最近几轮原文”来构造。

万一某个请求失败了,重试路径也不要直接把全部历史一箩筐拼回去,而是把同一份摘要取出来,加上最近几轮原文。这样恢复请求的输入规模跟正常请求差不了太多,不会出现正常时用压缩版、重试时用原版的反差。对话摘要的更新需要做成异步任务,别阻塞主请求;摘要丢了也没关系,退路是最近几轮原文,而不是把整段聊天全量回放。如果担心摘要损失太多细节,可以在摘要里保留用户明确提出过的关键偏好,多花几十 token 换稳定性,绝对划算。

4.2 成本务实测算:一次重试风暴吃掉几天节约

为了让“一次重试就全赔回去”这个说法落地,我拿一套长上下文应用来算笔账。假设某模型输入价格 30 元/百万 token,输出价格 90 元/百万 token,原始请求需要 20000 输入 token 并生成 2000 输出 token,单次成本 0.6 + 0.18 = 0.78 元。经过提示词瘦身和摘要策略之后,输入压到 6000,输出还是 2000,单次成本 0.18 + 0.18 = 0.36 元,每次省 0.42 元。如果每天有 500 次调用,一天下来省 210 元,看起来相当美好。

但如果凌晨模型服务抖了半小时,期间 200 个请求全部超时,SDK 默认重试 5 次,就会产生 1000 个重试请求,每个 0.36 元,多加 360 元。也就是说,这一场半小时的故障,已经把 1.7 天的节省赚回去了。如果重试路径不小心走了原始 20000 token 的完整版本,每个 0.78 元,那就是 780 元,接近四天的节省全打水漂。失败率只要略高一点,再把多级重试叠上去,最终做出来的实际成本表,往往会吓自己一跳。

这类情况并不是“重试机制一定不好”,而是重试机制缺少预算控制。假设失败率是 5%,完全没有辅助保护措施,让重试风暴自动滚起来,省钱项目可能直接变成烧钱项目。所以我强烈建议在每个接口的指标面板上同时挂两个图:一个是单请求平均 token,一个是重试放大率,后者一旦趋势往上走,就应该立刻重新审视某条链路。

4.3 熔断和降级:把省下来的预算用在刀刃上

熔断思路大家都很熟,套到 AI 调用上就好比给家里总闸装一条保险丝。我实践下来的基本做法是:如果某个模型连续失败达到 N 次,不再立即重试,而是把这个请求打进一个“慢队列”,等模型服务恢复后再补发。如果没有补发能力,那起码把重试降到 0,把错误及时透传给上游,让业务层决定怎么往下走。

同时,熔断的时候可以做一个模型降级:主模型挂了,备胎模型还在,把请求切到更小的模型上应急。但要注意备胎模型的输出质量可能不如主模型,要配合“切换模型后重试”来使用,不要死磕主模型。降级期间成本可能会偏低,但质量阈值要把关:一旦备胎返回质量不过关,不应该继续无限重试,而应该记录一次质量失败,再切回主模型或者交给人工处理。

整体上,省钱和可靠并不是对立面。把可靠的基础设施搭好之后,你反而可以更放心地压缩 prompt,因为一旦失败,恢复路径不会突然变贵。两个目标放在同一个成本体系里看,才会有更健康的平衡。

5. 热词报错现场:越重试越亏的几种典型案例

5.1 鉴权类 token 失效:重试不是“运气游戏”,是“付费游戏”

在各个平台的反馈里,能看到大量“token exchange failed”、“token endpoint returned status 403 forbidden”、“your access token could not be refreshed. please log out and sign in again.”这类报错。这类信息背后是同一个现实:你的 refresh_token 可能已经过期、被吊销,或者存储层里的 refresh_token 是空字符串。这种错误经常是会话状态坏了,凭据已经没法换取新的访问令牌,这时候你按多少次“重试”都不可能走到 LLM 调用那一步,因为鉴权环节直接拦住了。

真正该做的是回到登录态管理,刷新 token、重新登录,然后再谈调用。很多同学把 refresh_token 存进了缓存,结果缓存清除后变成空字符串,日志里出现 “invalid 'refresh_token': empty string. expected a string with minimum length 1, but got an empty string instead.” 这种 bug 的重点不是重试 API,而是去查初始化流程,看看数据库里的令牌字段是否真的持久化,是否被反序列化错误覆盖成了空值。我的经验是:凡是鉴权失败,第一件事就是停止所有重试,避免拿这种必然会失败的请求去反复消耗额度。

5.2 429 限流:读 Retry-After,别用固定的 1 秒

“您最近作出的请求太多了。请稍候,然后重试。”这是典型的 429 限流。AI 网关限流的目的就是保护下游模型资源,而你一顿疯狂重试只会加重困境。很多人以为只要隔 1 秒重试就没问题,结果所有客户端在同一时间窗口内都发出同样的等待,下一秒又是一个新的限流高峰,形成死亡螺旋。

正确做法是把失败的请求先收进一个本地或分布式队列,根据响应头里的 Retry-After 决定延迟发出,同时降低该模型的并发度。重试次数上也要做更严格限制,因为限流类失败通常意味着资源已经紧张,持续重试只会让资源更紧张。如果业务下游是对话机器人,能主动降级返回一个缓存结果,反而保护了核心链路。

5.3 模型加载失败、显存不足与“请切换模型或重试”

还有一类报错是模型侧问题,比如调用某个编码模型报错,提示“请切换模型或重试”,或本地部署时显存不足导致进程崩溃。这类问题的共同点是模型本身没有在线,或者推理进程已经挂掉。如果业务代码照着提示做固定重试,第一次重试可能重新加载模型,结果又 OOM,再重试又加载一次,开销全在显存和延迟上。

我的建议是区分“模型未就绪”和“临时过载”:前者用健康检查确认模型服务已经起来再放量;后者才是真正的重试场景。特别是小显存部署的模型,比如用 6G 显存强行跑 27B 量化模型的玩法,对内存和显存的要求都非常苛刻,预加载本身就要好几秒甚至几十秒,重试机制一触发,整条链路的响应时间就会崩,还会把其他请求一起带崩。这个时候不要固定重试次数地放大招,而是把请求切到健康的后备模型,或者直接返回友好错误让用户稍后再试,才是更合理的成本保护。

5.4 建立一张错误代码速查表

结合最近的反馈,我个人会维护一张“报错 → 第一反应”的速查表,粘到监控告警的备注里。这段时间整理下来大概是这样:

报错关键词第一反应是否正确触发重试
token exchange failed / token endpoint returned 403刷新登录态、检查 refresh_token 是否还在有效期内不该
invalid refresh_token empty string修存储层空值 bug不该
access token could not be refreshed强制用户重新登录不该
请求太多,请稍后重试读 Retry-After 并用队列削峰可以,但要延迟
模型调用失败,请切换模型或重试检查模型加载、显存、后端健康状态条件性可以
网络异常,请稍后重试指数退避,限制次数可以,但设上限

这张表看着平淡,每次线上出问题时,能省去至少一半的分析时间。做 AI 应用的同学都清楚,出问题时最贵的往往不是修复代码的时间,而是无脑重试带来的真金白银。

最后分享一个我这半年来觉得最管用的经验:把“重试”做成一个独立的服务层函数,而不是散落在业务代码里的 while 循环。所有可重试的请求必须经过同一个管道,管道给每个请求分配一个重试预算,并且给每一次失败记录打上 cost 估算。自那以后,我几乎没有再遇到过“省 token 省过头,一次重试就全赔回去”的事故。

如果你也刚刚开始给自己做的 AI 应用做 token 成本优化,我建议先别急着压缩字数,先把你现在的重试机制录一遍像,统计一下有多少重试属于本来就不该发的。你会发现,省下来的第一桶金往往不在 prompt 那一层,而在重试机制这一层。方向调整对了,再回头去优化 token,收益会明显踏实很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 4:35:08

AI智能体批量生产爆款视频的工程化系统

1. 项目概述:这不是“AI写脚本”,而是构建一个可批量运转的爆款内容生产系统“AI智能体实战:批量生成1000条爆款视频,1条爆款轻松涨粉2000(万字图文)”——这个标题里藏着三个被多数人忽略的关键事实&#…

作者头像 李华
网站建设 2026/10/10 4:34:18

智能体安全三防线:输入清洗、推理约束与输出校验实战

1. 这不是技术讨论,是一次真实压力测试的现场复盘 “18000 条帖子之后 智能体的安全边界该划在哪一层”——这个标题刚在内部技术群刷出来时,我正盯着后台实时滚动的日志流。第17998条、17999条、18000条……每一条都来自不同IP、不同设备、不同语种&am…

作者头像 李华
网站建设 2026/10/10 4:34:16

Android五子棋开发实战:自定义View、触摸事件与AI对弈全解析

简介:面向Android开发学习者与计算机专业课程设计的一款五子棋小游戏完整项目,采用Android Studio开发,实现人机对战与人人对战两种玩法。人机对战部分通过棋盘落子得分评估AI决策,单人可以挑战AI并实时记录比分;人人对…

作者头像 李华
网站建设 2026/10/10 4:34:13

深度学习入门:从神经网络原理到PyTorch手写数字识别实战

很多朋友问我,经常刷到"深度学习"这个词,想学却不知道从哪下手。我也经历过那个阶段:看了几篇帖子,装了框架,跑了示例,然后被一堆术语按在地上摩擦。这篇作为"深度学习1"的开篇&#x…

作者头像 李华
网站建设 2026/10/10 4:33:06

16G显存跑40GB大模型:量化、CPU卸载与混合加载实战

这阵子我一直盯着硬盘里那个体积逼近 40GB 的模型文件,再看看手边这块只有 16G 显存的显卡,脑子里反复蹦出来的就两个字:硬上。事情是这样的。我平时主要拿笔记本接显卡坞干活,机器本身没有独显,全靠一块雷电坞拖着一块…

作者头像 李华
网站建设 2026/10/10 4:33:06

给Claude装上长期记忆:跨会话上下文持久化实战解析

用过 Claude 的人大概都有过这种体验:今天聊的方案,明天打开新会话,它一脸茫然;刚调好的偏好设置,过两天一问,忘得一干二净;同一个项目背景,每次都要从头解释一遍。这不是错觉&#…

作者头像 李华