做 AI 应用头三个月,我踩的最大的坑不是模型选型,也不是提示词调优,而是账单和接口这两件看起来特别"运维"的事。上线第一周,一个日活不到两千的小工具,因为一段没做长度限制的对话历史,单日 Token 消耗直接冲到预算的三倍;第二周,主力接口在晚高峰连续超时,用户侧看到的就是转圈和报错,客服群里炸锅。那段时间我才真正理解,做 AI 应用和做传统 CRUD 完全是两套逻辑——模型调用是按量计费的远程依赖,它既是你成本结构里最不可控的一块,也是你系统里最脆弱的一环。这篇就把我这几个月在 Token 成本控制和接口稳定性上摸出来的东西完整摊开讲,包括怎么算账、怎么砍成本、怎么做降级、怎么排查那些"看起来是模型问题其实是工程问题"的故障。不管你是刚接第一个大模型接口的新手,还是已经上线在跑但账单开始失控的开发者,应该都能从里面抄到点能直接用的东西。
1. 先搞清楚钱花在哪:Token 成本的构成与计量逻辑
1.1 Token 到底是什么,为什么它决定了你的账单
很多人第一次看账单会懵:明明用户就输入了一句话,怎么扣了这么多。核心原因是大模型按 Token 计费,而不是按字数或请求次数。Token 是模型处理文本的最小单位,可以粗略理解为"词片段"。英文里一个常见单词大约是 1 个 Token,中文则更"贵"——一个汉字通常要占 1 到 2 个 Token,具体取决于分词器。这就意味着同样一段话,中文的成本天然比英文高,做中文应用的朋友要有心理预期。
更关键的是,计费是双向的:输入(prompt)算钱,输出(completion)也算钱,而且输出的单价通常比输入贵好几倍。我实测过一个典型场景,输入 800 Token、输出 400 Token 的一次调用,输出部分的费用能占到总费用的六成以上。所以"让模型少说废话"不只是体验问题,是实打实的省钱手段。
这里有个容易被忽略的点:多轮对话的历史会累积。如果你把每一轮的问答都原样塞回上下文,那么第 10 轮请求的输入 Token 可能是第 1 轮的十倍。这就是我第一周账单爆炸的直接原因——对话历史没有任何截断策略。
1.2 一次调用的成本怎么算:手把手带你算一遍
假设你用的是某主流模型,输入单价为每百万 Token 若干元,输出单价是输入的数倍(不同厂商差异很大,这里只讲方法,具体数字以你实际接入的价目为准)。计算逻辑是:
单次调用成本 = (输入Token数 / 1,000,000) × 输入单价 + (输出Token数 / 1,000,000) × 输出单价举个我实际遇到的例子。一个客服问答场景,系统提示词 500 Token,用户问题平均 100 Token,检索回来的知识片段 1500 Token,模型回答平均 300 Token。那么单次调用:
- 输入 = 500 + 100 + 1500 = 2100 Token
- 输出 = 300 Token
如果日调用量是 5000 次,一天输入就是 1050 万 Token,输出 150 万 Token。这个量级下,输入单价哪怕只差一点点,一个月下来就是几百上千块的差距。所以成本优化的第一优先级永远是砍输入,因为输入才是大头。
我建议你在项目里做一个简单的成本埋点:每次调用后记录输入 Token、输出 Token、耗时、是否命中缓存。跑一周你就能画出成本分布图,知道钱到底花在哪个环节。没有这个数据,所有优化都是拍脑袋。
1.3 那些悄悄吃掉预算的隐藏成本
除了显性的 Token 费用,还有几块成本经常被漏算:
- 重试成本:接口超时后自动重试,如果没做幂等和次数限制,一次失败可能触发三次调用,钱照扣。
- 失败请求成本:有些情况下请求发出去了、模型也开始生成了,但中途因为网络断开,你拿不到结果,可 Token 已经消耗了。
- 流式输出的"半截"成本:用户中途关闭页面,流式响应被中断,但已经生成的部分照样计费。
- 调试和测试成本:开发阶段反复调提示词,如果直接打生产接口,测试流量也是真金白银。
提示:开发调试尽量用独立的小额度 Key,和生产 Key 分开,避免测试流量污染成本统计,也避免误操作把生产额度跑光。
2. 把 Token 成本砍下来的六个实操手段
2.1 上下文管理:别把整段历史都塞回去
这是性价比最高的一招。多轮对话不要无脑拼接全部历史,我一般用"滑动窗口 + 摘要"的组合策略:
- 保留最近 N 轮完整对话(N 取 3 到 5 就够大多数场景)
- 更早的历史压缩成一段简短摘要,由模型自己生成
- 系统提示词里明确告诉模型"以下是历史摘要"
实测下来,一个原本输入 4000 Token 的长对话,压缩后能降到 1200 Token 左右,成本直接砍掉七成,而回答质量几乎没变化。因为大多数场景里,用户真正关心的就是最近几轮的信息。
具体实现上,我会维护一个消息数组,每次请求前做一次"裁剪":
def build_context(history, max_recent=4, summary=None): recent = history[-max_recent:] messages = [] if summary: messages.append({"role": "system", "content": f"历史摘要:{summary}"}) messages.extend(recent) return messages摘要的更新时机可以放在对话轮数超过阈值时触发一次,异步生成,不阻塞主流程。
2.2 提示词瘦身:系统提示词是最容易被浪费的地方
系统提示词每次调用都要带上,所以它是最"高频重复"的成本。我见过有人把整个产品说明书、几十条规则全塞进系统提示词,一次就 2000 Token,调用一万次就是两千万 Token 的纯浪费。
优化思路:
- 能删就删:规则里那些"不要做 XX"的负面约束,很多模型默认就不会做,删掉。
- 能合并就合并:把多条相似规则合并成一条。
- 能外置就外置:把大段知识放到检索环节,按需注入,而不是常驻系统提示词。
- 用结构化格式:同样的信息,用简洁的列表或 JSON 表达,比自然语言长句省 Token。
我把一个项目的系统提示词从 1800 Token 压到 600 Token,回答质量没掉,月度成本降了将近三成。这个投入产出比非常高,值得花半天时间专门做。
2.3 缓存策略:相同的问题不要问两遍
如果你的应用里有大量重复或高度相似的问题(客服、FAQ、知识问答类特别明显),缓存能省下惊人的成本。分两层做:
- 精确缓存:对输入做哈希,命中直接返回上次结果。适合完全相同的请求。
- 语义缓存:把用户问题向量化,相似度超过阈值就复用答案。适合"换个说法问同一件事"的场景。
语义缓存要注意阈值调优,太松会答非所问,太紧又命中率低。我一般从 0.92 左右开始试,根据业务容忍度上下调。缓存命中率做到 30% 以上,成本下降就很可观了。
2.4 模型分级:不是所有请求都值得用最贵的模型
这是很多团队忽略的一招。把请求按复杂度分级:
| 请求类型 | 推荐模型档位 | 理由 |
|---|---|---|
| 简单分类、意图识别 | 小模型/轻量模型 | 任务简单,大模型是浪费 |
| 常规问答、摘要 | 中等模型 | 性价比最优区间 |
| 复杂推理、长文生成 | 大模型 | 需要强能力,值得花钱 |
我做过一个实验,把意图识别从大模型换成小模型,准确率只掉了不到 2 个百分点,但成本降了八成。先用小模型兜底,识别出复杂请求再升级到大模型,这个"路由"思路能省很多钱。
2.5 输出长度控制:让模型学会"说重点"
输出 Token 单价高,所以控制输出长度收益明显。手段包括:
- 在提示词里明确要求"回答不超过 X 字"
- 用
max_tokens参数硬性截断(注意别截断到一半,影响体验) - 要求模型用要点式而非段落式回答
- 对不需要解释的场景,直接要求只输出结果
我有个场景原本模型爱写小作文,平均输出 500 Token,加了"用三句话以内回答"的约束后降到 150 Token,用户反而觉得更清爽。
2.6 批处理与异步:把零散请求攒起来
如果业务允许延迟(比如离线分析、批量打标),把多个请求合并成一次调用,或者用批处理接口,通常有折扣。实时性要求不高的任务,异步跑在低峰期,既省钱又避开高峰的接口压力。
3. 接口稳定性:比成本更致命的隐形杀手
3.1 为什么 AI 接口比普通接口更"脆"
传统后端调数据库,延迟稳定、成功率接近 100%。但大模型接口不一样:
- 延迟波动大:同样的问题,快的时候 1 秒,慢的时候 30 秒,取决于模型负载和输出长度。
- 有速率限制:每分钟请求数、每分钟 Token 数都有上限,超了直接拒绝。
- 偶发失败:高峰期超时、返回空、返回格式错误都可能出现。
- 流式响应中断:网络抖动导致流断掉,用户看到半截回答。
这些特性决定了,你不能像调普通接口那样"发出去等结果",必须为失败和延迟设计一整套应对机制。我第二周那次事故,就是因为代码里只有一个裸调用,没有任何超时和重试,接口一慢整个页面就卡死。
3.2 超时、重试、退避:三件套一个都不能少
这是稳定性的地基。我的标准配置:
- 连接超时:5 到 10 秒,防止连接阶段卡死
- 读取超时:根据场景设,问答类 30 到 60 秒,长文生成可以更长
- 重试次数:2 到 3 次,不要无限重试
- 退避策略:指数退避 + 随机抖动,避免所有请求同时重试造成雪崩
import time import random def call_with_retry(fn, max_retries=3, base_delay=1.0): for attempt in range(max_retries): try: return fn() except (TimeoutError, RateLimitError) as e: if attempt == max_retries - 1: raise delay = base_delay * (2 ** attempt) + random.uniform(0, 0.5) time.sleep(delay)注意:重试只对"可重试错误"生效,比如超时、限流、5xx。对于参数错误、内容违规这类 4xx,重试没有意义,只会浪费钱。
3.3 降级方案:主模型挂了,用户不能看到白屏
降级是稳定性的最后一道防线。我一般准备三级:
- 同级备用模型:主模型不可用时切到另一个能力相近的模型
- 降级模型:切到更小更快的模型,牺牲一点质量保可用
- 兜底话术:全部失败时返回友好提示 + 引导用户稍后重试,而不是报错
关键是要提前配置好切换逻辑并定期演练,别等真出事才临时改代码。我见过太多团队写了降级代码但从没测过,真到用时发现配置是错的。
3.4 限流与排队:保护自己,也保护上游
你自己的服务也要做限流,防止突发流量把上游额度打爆。常用手段:
- 令牌桶/漏桶:控制单位时间的请求速率
- 并发上限:限制同时在飞的请求数
- 队列缓冲:超出容量的请求排队,而不是直接拒绝
我一般会给每个用户或每个租户设一个配额,避免单个用户刷爆全局额度。这个在 To B 场景尤其重要。
3.5 监控与告警:出问题要第一时间知道
没有监控的稳定性都是玄学。我必看的几个指标:
| 指标 | 含义 | 告警阈值参考 |
|---|---|---|
| 成功率 | 成功请求 / 总请求 | 低于 98% 告警 |
| P95 延迟 | 95% 请求的耗时上限 | 超过设定值告警 |
| 限流触发次数 | 被上游拒绝的次数 | 突增即告警 |
| Token 消耗速率 | 单位时间消耗量 | 异常飙升告警 |
| 重试率 | 重试请求占比 | 高于 5% 关注 |
这些指标我建议做成看板,每天扫一眼。成本异常和稳定性异常往往是同时出现的——接口不稳会导致重试增多,重试增多又推高成本,是个恶性循环。
4. 成本与稳定性的联动:它们其实是一件事
4.1 重试是把双刃剑
前面说重试能提升稳定性,但重试也在烧钱。一次失败重试三次,成本就是四倍。所以重试策略要精细:
- 只对可重试错误重试
- 限制重试次数
- 重试前先判断是否已经产生部分计费
- 对高成本请求(长上下文、大模型)降低重试次数
我现在的做法是,把请求按成本分级,高成本请求最多重试一次,低成本请求可以重试三次。这样在稳定性和成本之间找到平衡。
4.2 缓存同时优化了两件事
缓存是少有的"既省钱又提稳定性"的手段。命中缓存的请求根本不碰上游接口,既省 Token 又降低了对上游的依赖,高峰期还能扛住更多流量。所以缓存策略值得优先投入。
4.3 降级也在省钱
降级到小模型,单次成本可能只有原来的十分之一。所以降级不只是保可用,也是保成本。当上游大模型限流时,切到小模型既能继续服务,又不会让账单失控。
5. 常见问题与排查技巧实录
5.1 账单突然翻倍,怎么快速定位
按这个顺序查:
- 看调用量是否突增:是不是有爬虫、异常用户或死循环
- 看平均输入 Token 是否变大:是不是上下文没截断
- 看重试率:是不是接口不稳导致大量重试
- 看是否有测试流量混入:是不是调试 Key 用了生产额度
- 看缓存命中率是否下降:是不是缓存失效了
我遇到过一次账单翻三倍,最后发现是某个定时任务配置错了,每分钟跑一次全量数据,白白烧了两天。
5.2 接口偶发超时,怎么排查
先区分是"你的问题"还是"上游的问题":
- 看是不是集中在某个时间段(上游高峰)
- 看是不是集中在某个模型(该模型负载高)
- 看是不是特定请求(长上下文、复杂任务天然慢)
- 看网络链路是否有抖动
如果是上游问题,只能靠重试、降级、错峰来缓解;如果是自己的问题,比如连接池太小、并发太高,那就调参数。
5.3 流式响应中断怎么办
流式场景要处理"断流":
- 前端要能感知流结束,区分"正常结束"和"异常中断"
- 后端要捕获中断异常,记录已生成内容
- 提供"重新生成"入口,而不是让用户干等
- 对关键场景,可以先用非流式拿到完整结果再分段展示
5.4 常见问题速查表
| 现象 | 可能原因 | 处理方向 |
|---|---|---|
| 成本飙升 | 上下文未截断/重试过多/测试流量 | 检查上下文策略、重试配置、Key 隔离 |
| 接口频繁超时 | 上游高峰/并发过高/超时设置过短 | 错峰、限流、调超时 |
| 限流报错 | 超出速率限制 | 加令牌桶、排队、申请提额 |
| 回答质量下降 | 降级触发/上下文被裁太多 | 检查降级日志、调整裁剪策略 |
| 缓存不命中 | 阈值太严/输入变化大 | 调阈值、做归一化预处理 |
5.5 几个我踩过的坑
- 别用生产 Key 做压测:一次压测可能烧掉一个月预算。
- 别忽略 max_tokens:不设上限,模型可能生成超长内容。
- 别把重试写成递归:递归重试容易栈溢出,用循环。
- 别只监控成功率:延迟和成本同样重要。
- 别等出事才做降级:降级逻辑要定期演练。
6. 一套可落地的最小实践方案
如果你现在正准备上线一个 AI 应用,我建议至少做到这几件事,投入不大但收益明显:
第一,做成本埋点,每次调用记录输入输出 Token 和耗时,跑一周看分布。第二,上下文做滑动窗口,别全量拼接。第三,系统提示词瘦身,能删就删。第四,加超时和指数退避重试,只对可重试错误生效。第五,配一个降级模型,主模型挂了能顶上。第六,做基础监控告警,成功率和成本异常要能第一时间知道。
这六件事做完,你的应用在成本和稳定性上就已经超过大多数同类项目了。剩下的优化,等有了数据再针对性做,别一上来就追求完美架构。
我在实际项目里的体会是,AI 应用的工程难点往往不在模型本身,而在这些"围绕模型的脏活累活"上。模型能力是厂商给的,但成本控制和稳定性是你自己的护城河。把这两件事做扎实,应用才能从"能跑"走到"能长期跑"。最后分享一个小技巧:每周固定花半小时看一次成本和稳定性看板,很多问题在变成事故之前,其实数据里早就有苗头了。