Skip to content

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:限制单次输出的最大长度;
  • 预算告警和熔断:日、月费用达到阈值时告警,超过上限时自动停止调用或切换到便宜的模型;
  • 按功能统计成本:每次调用都标记来自哪个功能,才能知道钱花在了哪里。

排查思路 ​

  1. 成本异常:按功能、按用户、按模型统计用量,找到增长最快的那一项;再看平均输入长度、输出长度和重试率哪个变了。
  2. 延迟异常:区分“开始输出前的等待时间”和“总生成时间”。前者长通常是输入太长或服务端排队,后者长通常是输出太长。
  3. 效果下降:确认模型版本、参数、提示词有没有变化;用评测集复现。
  4. 缓存效果差:统计缓存的命中率,抽样查看未命中的请求,看是否因为输入里带了每次都变的内容。

检查清单 ​

  • [ ] 上线前按真实输入长度估算过月成本
  • [ ] 每个任务用评测选出“最便宜的够用模型”
  • [ ] 长对话有摘要或窗口截断
  • [ ] 重试有上限,并记录在用量统计中
  • [ ] 缓存键包含影响结果的用户维度;固定内容放在提示词前部
  • [ ] 交互场景使用流式输出,设置了 max_tokens
  • [ ] 有备用模型和降级方案,并经过评测
  • [ ] 固定模型版本,换模型前跑评测
  • [ ] 有按用户和功能的限流、预算告警和熔断

延伸阅读 ​

为复用而记录,为理解而整理。