LLM 多模型选型与成本控制
模型接入很容易,账单和体验的问题往往在上线后才出现:
- 第一个月的账单是预估的十倍,查下来是长对话的后几轮特别贵;
- 简单的意图分类也用了最强的模型,又慢又贵,准确率却没高多少;
- 模型服务商故障了两个小时,相关功能完全不可用;
- 为了省钱换了一个模型,结果有一类问题的回答质量明显下降,过了一周才发现。
选型和成本控制的核心是:按任务匹配模型,算清楚钱花在哪里,并为失败做好准备。
读完这篇你会
- 理解 Token 计费的结构,在上线前估算成本
- 按任务难度给模型分档,用评测选出“最便宜的够用模型”
- 用缓存、压缩、流式输出、降级和预算熔断控制成本与体验
1. 原理:像打车一样按里程计费
调用模型的费用,可以类比打车:
- 按里程计费:费用按 Token 计算;
- 上车说目的地相当于输入:你发给模型的指令、对话和资料;
- 行驶的里程相当于输出:模型生成的内容,单价通常比输入更贵;
- 选车型:经济型、舒适型、专车价格不同,短途代步没必要叫专车。
text
单次费用 = 输入 Token × 输入单价 + 输出 Token × 输出单价上线前用一个简单的函数估算月成本,比上线后看账单再着急好得多:
js
function estimateMonthlyCost({ callsPerDay, avgInputTokens, avgOutputTokens, inputPrice, outputPrice }) {
// inputPrice / outputPrice:每百万 Token 的价格,以所用模型的官方价格表为准
const perCall = (avgInputTokens * inputPrice + avgOutputTokens * outputPrice) / 1_000_000
return perCall * callsPerDay * 30
}估算时,输入 Token 要按真实场景计算:系统指令、历史对话、检索资料、工具定义都算在内,往往比用户的问题本身长得多。
2. 按任务难度给模型分档
| 任务 | 例子 | 模型档位 |
|---|---|---|
| 简单分类、抽取 | 意图识别、情感判断、字段提取 | 小模型:快、便宜 |
| 改写、摘要 | 润色、格式转换、总结 | 中档模型 |
| 复杂推理、生成 | 多步规划、代码生成、长文写作 | 强模型,确实需要时才用 |
怎么确定某个任务该用哪一档?用评测来选:准备一组有标准答案的测试用例(见 LLM 评测与 Guardrails),从便宜的模型开始测,达标就用它,不达标再升一档。不要凭感觉,也不要默认从最贵的开始。
3. 坑一:对话越长,每一轮越贵
现象:单次问答的费用很低,但长对话的后几轮费用是第一轮的几十倍。
原因:模型是无状态的,每一轮都要把之前所有的历史重新发送一遍(见 LLM 基础)。第 20 轮对话,输入里包含前 19 轮的全部内容。
修复:
- 只保留最近几轮的原文,更早的对话总结成一段摘要;
- 资料类内容用检索按需提供,而不是每轮都带上全部文档;
- 限制单个会话的最大长度,超出后提示用户开始新会话。
4. 坑二:重试让费用悄悄翻倍
现象:调用次数统计和预估一致,账单却高出不少。
原因:格式校验失败的重试、超时后的重试、被安全策略拦截后的重试,每一次都要全额计费。而这些重试往往没有被统计在“调用次数”里。
修复:在日志里记录每次调用的原因(首次 / 第几次重试)和用量;给重试设置次数上限;如果某个场景的重试率很高,说明提示词或输出格式需要改进,而不是继续重试。
5. 坑三:缓存命中率很低
现象:明明有大量重复的问题,加了缓存后命中率却很低。
修复:
- 应用层缓存:缓存键使用归一化后的输入(去掉多余空格、统一大小写),排除时间戳等每次都变的参数。注意:如果回答依赖用户身份、权限或个人数据,缓存键必须包含这些维度,否则会把 A 的答案返回给 B(和 缓存策略与一致性 中的“串号”问题完全一样);
- 服务商的提示词缓存:很多模型服务支持对“重复出现的提示词前缀”打折计费。要利用好它,把固定不变的内容放在前面(系统指令、工具定义、固定资料),把每次变化的内容(用户问题)放在最后。前缀里只要有一个字变了(比如在开头插入了当前时间),缓存就失效了。
即使温度设为 0,模型的输出也不保证完全一致,所以缓存的是“当时的一个合理答案”。知识更新后,相关的缓存要能失效。
6. 坑四:用户觉得太慢
现象:回答质量不错,但用户要盯着空白页面等十几秒,很多人等不及就关掉了。
修复:
| 方法 | 效果 |
|---|---|
| 流式输出(边生成边显示) | 用户几百毫秒就能看到第一个字,体感大幅改善 |
限制输出长度(max_tokens、提示词里要求简洁) | 生成是逐个 Token 进行的,越短越快,也越便宜 |
| 减少输入长度 | 输入越长,开始生成前的处理时间越长 |
| 换更小的模型 | 简单任务用小模型,速度快得多 |
交互场景首先用流式输出;后台批处理任务对延迟不敏感,可以用更便宜的模型,甚至使用服务商提供的批量接口。
7. 坑五:服务商一出故障,功能就不可用
现象:模型服务商出现故障或者限流,依赖它的功能全部报错。
修复:把“模型调用失败”当作常态来设计:
- 重试:对超时、限流做有限次数的退避重试;内容被安全策略拒绝的情况不要重试;
- 备用模型:主模型不可用时切换到另一家或另一个模型;
- 降级方案:模型都不可用时,回退到规则、模板回复或人工处理,并明确告诉用户“智能功能暂时不可用”。
注意:备用模型的效果也要经过评测。切换过去之后回答质量大幅下降,可能比直接提示“暂时不可用”更糟糕。
8. 坑六:换了模型,效果悄悄变了
现象:为了降低成本换了一个模型,大部分问题回答得都不错,但某一类问题的回答明显变差,一周后才被用户反馈出来。
修复:
- 固定模型版本:调用时指定具体的模型版本,不使用会自动更新的别名,避免服务商升级模型导致行为突然变化;
- 换模型前跑评测:用同一套测试集对比新旧模型,按问题类型分别看结果,而不只看整体平均分;
- 灰度切换:先让一小部分流量使用新模型,对比两边的用户反馈和指标。
9. 坑七:账单失控
现象:某个用户写了脚本反复调用接口,或者程序出现了循环调用,一晚上的费用超过了一整个月。
修复:
- 按用户、按功能限流:每个用户每天的调用次数、Token 用量设置上限;
- 设置
max_tokens:限制单次输出的最大长度; - 预算告警和熔断:日、月费用达到阈值时告警,超过上限时自动停止调用或切换到便宜的模型;
- 按功能统计成本:每次调用都标记来自哪个功能,才能知道钱花在了哪里。
排查思路
- 成本异常:按功能、按用户、按模型统计用量,找到增长最快的那一项;再看平均输入长度、输出长度和重试率哪个变了。
- 延迟异常:区分“开始输出前的等待时间”和“总生成时间”。前者长通常是输入太长或服务端排队,后者长通常是输出太长。
- 效果下降:确认模型版本、参数、提示词有没有变化;用评测集复现。
- 缓存效果差:统计缓存的命中率,抽样查看未命中的请求,看是否因为输入里带了每次都变的内容。
检查清单
- [ ] 上线前按真实输入长度估算过月成本
- [ ] 每个任务用评测选出“最便宜的够用模型”
- [ ] 长对话有摘要或窗口截断
- [ ] 重试有上限,并记录在用量统计中
- [ ] 缓存键包含影响结果的用户维度;固定内容放在提示词前部
- [ ] 交互场景使用流式输出,设置了
max_tokens - [ ] 有备用模型和降级方案,并经过评测
- [ ] 固定模型版本,换模型前跑评测
- [ ] 有按用户和功能的限流、预算告警和熔断
延伸阅读
- LLM 基础:Token 与上下文
- LLM 评测与 Guardrails:用数据选模型
- 并发与限流:限流和熔断的通用做法