公司动态
大模型 API 成本评估指南:拆解计费结构、沉淀可复现对比流程
在实际项目的模型选型讨论中经常会出现一句话把成本问题一笔带过的现象“某个模型更便宜直接换过去就行。”比如当大家看到“GLM-5.3 成本仅为 FABLE 5 的八分之一”这类结论时不要急着把它当既定事实写进方案。大模型 API 的计费并不是“一个单价 × 调用次数”这么简单真正的账单由输入 token、输出 token、缓存命中、上下文长度和调用链路共同决定。如果只拿一份公开价格表对比算出来的结论很可能和实际账单差出好几倍。这篇文章从成本评估方法入手先拆解大模型 API 的计费结构再给出一个可复现的对比脚本最后说明如何验证“八分之一成本”这类结论是否在自己的任务上成立以及怎样避免被单点价格误导。无论是做技术选型、模型切换还是日常的账单排查都可以沿用这套流程。1. 成本对比不是看单价表就够的先拆解大模型 API 的计费结构1.1 为什么按“千 token 单价”直接比大小往往会算错很多平台在文档里都会列出每百万 token 的价格看起来只要把两个模型的价格放在一起相除就能得出结论。但实际项目里两个模型的计费口径经常不一致常见的差异点包括同一段中文文本在不同模型的分词器下token 数量可能相差 20% 到 50%。一个模型按 300 token 计费另一个模型可能按 450 token 计费即使单价相同实际成本也不同。输入和输出的单价往往不一样输出 token 通常更贵。如果任务偏长输出输出价格会成为总成本的主要部分只对比输入价格没有意义。部分模型支持上下文缓存命中缓存的输入 token 会按折扣价计费不命中的输入 token 按原价计费。缓存命中率不同同样请求的账单就不一样。不同模型对上下文的处理方式不同。有的模型会自动截断或压缩历史消息有的模型必须把完整历史重新发送导致每次请求的输入 token 数量完全不同。所以正确的做法不是比较“单价”而是比较“完成同一个任务的实际支出”。单价只是成本模型里的一个参数不是结论本身。1.2 一次完整请求的成本由哪几部分叠加一个标准的大模型 API 请求账单通常可以拆成四个部分输入 token用户发送的 prompt、历史会话、工具定义等所有进入模型的文本按输入单价计费。输出 token模型生成的补全内容按输出单价计费一般比输入单价高。缓存写入 token第一次携带相同前缀请求时系统把前缀写入缓存产生的费用。缓存读取 token后续请求命中缓存时读取缓存前缀产生的费用通常远低于普通输入价格。计算单次请求成本的公式可以写成单次成本 输入token数 × 输入单价 输出token数 × 输出单价 缓存写入token数 × 缓存写入单价 缓存读取token数 × 缓存读取单价如果接口不暴露缓存的拆分字段计算时就退化为前两项。生产环境中多轮对话、批量任务和带系统提示词的任务缓存字段对总账单的影响非常大这一点放到第 4 节专门展开。1.3 成本对比的必要前提相同任务、相同输出语义成本对比最容易犯的错误是“任务不一致”。同样叫“让模型总结一段会议纪要”如果任务 A 要求 200 字以内的中文摘要任务 B 要求 1000 字的英文要点输出 token 差距可以达到 5 倍以上。没有统一任务样本任何成本结论都没有可比性。做对比前至少要统一这几个维度维度对比时必须统一的内容任务类型文本摘要、代码生成、信息抽取、对话问答等要分开测试输入长度同一条输入或按相同长度分布构造输入样本输出长度通过 prompt 约束或 max_tokens 参数保持一致量级温度参数同一 temperature避免输出随机性放大成本波动调用方式同步、流式、批量接口分别统计因为批量可能有折扣缓存状态冷启动和缓存命中后的成本要分开记录只有在这些条件一致时“GLM-5.3 成本仅为 FABLE 5 的八分之一”这句话才是一个可以验证的命题而不是一个无法追溯的营销表述。2. 搭建一个可复现的成本评估环境2.1 环境准备和依赖安装成本评估脚本只需要普通的 Python 环境不需要额外服务。推荐 Python 3.10 以上版本使用 requests 或各模型的官方 SDK 都可以。为了脚本通用这里使用 OpenAI SDK 风格的接口因为大多数平台都兼容这套请求格式。python -m venv venv source venv/bin/activate pip install openai如果目标平台不提供 OpenAI 兼容接口也可以直接用 requests 调 HTTP 接口核心逻辑不变区别只是请求头和响应报文。评估环境的重点是记录每次请求的 usage 数据。绝大多数平台在响应里都会返回类似下面的结构{ usage: { prompt_tokens: 512, completion_tokens: 128, total_tokens: 640 } }部分平台还会返回prompt_tokens_details或prompt_cache_hit_tokens之类的字段用来区分缓存命中部分。脚本里要优先收集这些字段拿到最细粒度的数据。2.2 用统一脚本记录请求级 token 消耗下面这个脚本封装了一次 API 调用并把请求文本、响应文本和 token 明细记录成一条 JSON 记录。实际评估时把脚本跑在同一个环境、同一个网络条件下避免网络重试和超时策略对结果产生干扰。import json import time from openai import OpenAI def call_and_record(client, model, messages, output_record, max_tokens1024): start time.time() resp client.chat.completions.create( modelmodel, messagesmessages, max_tokensmax_tokens, temperature0.2, ) usage resp.usage record { model: model, prompt_tokens: usage.prompt_tokens, completion_tokens: usage.completion_tokens, total_tokens: usage.total_tokens, latency_ms: round((time.time() - start) * 1000, 2), request: json.dumps(messages, ensure_asciiFalse), response: resp.choices[0].message.content, } if hasattr(usage, prompt_tokens_details): cached getattr(usage.prompt_tokens_details, cached_tokens, 0) record[prompt_cache_hit_tokens] cached or 0 output_record.append(record) return record记录字段里最关键的是prompt_cache_hit_tokens。如果不记录这个字段后面计算成本时只能按全部输入原价计算会高估那些命中次数多、上下文长但增量小的请求。2.3 建立任务样本集避免用单条提示词下结论成本对比不能只跑一条提示词。单条结果受随机性和任务偏差影响很大建议构造一个至少 20 到 50 条的样本集覆盖真实业务中占比最高的几类任务。样本集保存成 JSONL一行一个任务{task: summary, input: 请用 150 字以内总结以下会议纪要..., max_tokens: 300} {task: extract, input: 从以下合同中抽取甲方、乙方、金额和期限..., max_tokens: 200} {task: codegen, input: 用 Python 写一个读取 CSV 并统计分组的函数..., max_tokens: 500}样本集需要满足三个条件一是和线上任务同分布二是输入长度有一定跨度从几百 token 到几千 token 都有三是输出长度通过 prompt 和 max_tokens 都做了约束避免不同模型输出长度差异过大导致成本失真。一个可供参考的样本构成是摘要类 30%、信息抽取类 25%、代码生成类 20%、多轮对话类 15%、短文本改写类 10%。比例按自己的业务调整重点是测试结果要能代表真实流量结构而不是只代表最容易跑通的那一条示例。3. 用实际脚本计算并对比单次任务成本3.1 通过 API 返回的 usage 字段统计成本有了样本集和调用记录下一步就是按统一价格计算单次请求成本。计费价格不建议硬编码在脚本里而是放到一个单独的配置文件中便于按平台和有效期修改。下面是一个价格配置的例子pricing: glm_5_3: input_per_million: 0.5 output_per_million: 2.0 cache_write_per_million: 1.0 cache_read_per_million: 0.1 fable_5: input_per_million: 4.0 output_per_million: 16.0 cache_write_per_million: 8.0 cache_read_per_million: 0.8这里的数字只是演示结构不是实际价格落地前必须替换成目标平台当前文档中的真实报价。计算成本时按照之前提到的公式逐条记录累加def compute_cost(record, price): prompt_tokens record[prompt_tokens] completion_tokens record[completion_tokens] cache_hit record.get(prompt_cache_hit_tokens, 0) cache_miss prompt_tokens - cache_hit cost ( cache_miss * price[input_per_million] / 1_000_000 cache_hit * price[cache_read_per_million] / 1_000_000 completion_tokens * price[output_per_million] / 1_000_000 ) return round(cost, 6)如果平台没有暴露缓存字段就把cache_hit按 0 处理。此时计算结果会偏保守适合做最坏情况估计而平台实际对缓存命中的折扣不会体现在该结果里所以还要结合平台文档单独测算缓存收益。3.2 预估成本和实际成本之间的偏差来源本地估算和平台账单对不上通常不是脚本算错了而是下面几个原因输出 token 被max_tokens截断。响应里如果带了finish_reasonlength说明输出还没生成完就被截断实际内容不完整但依然按完整 token 计费同时会影响任务质量。请求重试。每次超时重试都会重新产生 token 费用账单上会出现一模一样的重复请求。模型别名路由。部分平台用同一个模型名接入多家服务商实际被路由到不同模型价格自然不同。上下文自动扩展。多轮对话中每次请求都会携带完整历史输入 token 随轮数增长和单轮预估完全不一样。所以在对比两个模型成本时不能只看一两次调用要统计整批样本的平均成本、中位数成本和 P95 成本。平均值会被个别超长请求拉高中位数更接近普通请求体验P95 则是容量规划时应该关注的上限。3.3 验证“八分之一成本”类结论要检查哪些点当有人说“A 模型成本只有 B 模型的八分之一”时按下面的清单逐项核对检查项核对内容对比口径比的是输入单价、输出单价还是单次任务实际成本样本任务两个模型跑的是同一批任务吗输出长度限制一致吗计费时段价格有没有包含限时折扣、新用户优惠或批量折扣缓存状态是否都处于冷启动状态有没有一个模型命中缓存而另一个没有输出质量便宜的那一方的失败率、拒答率和格式错误率是否在可接受范围并发与配额便宜模型是否限制了并发数导致需要更多实例从而摊低成本优势只有这六项都核对通过成本倍率才有工程意义。否则“八分之一”很可能只是某个特定 prompt、某个特定时段下的偶然结果。4. 影响最终成本的隐藏因素缓存、模型路由、超时与重试4.1 上下文缓存如何改变实际账单上下文缓存是目前影响大模型账单最明显的隐藏因素。它的原理是平台把请求前缀按内容哈希缓存起来下次请求如果前缀相同就直接复用缓存不再把整段前缀重新计算一遍。对系统提示词长、历史会话多的业务来说缓存命中率可以占到总输入 token 的 80% 以上。缓存对成本的影响可以从两个方向看场景缓存不命中缓存命中结论第一次请求全量输入按原价计费无缓存可用冷启动成本高后续相同前缀请求重新计算费用高按折扣价计费费用低热请求成本明显下降前缀经常变化每次部分写入、部分命中命中率不稳定成本波动大对比两个模型时如果 A 模型缓存命中率 85%B 模型只有 30%即使两者单价相同实际账单也会差出不少。因此评估脚本里必须单独记录缓存命中 token并且用真实业务中的前缀分布来测试而不是在脚本里人工拼一个固定前缀。注意缓存命中率必须在真实前缀分布下测试才有效。临时构造的固定前缀会得到虚高的命中率会严重低估实际成本。4.2 模型路由和降级策略对成本的影响在线上系统里成本对比往往不是“单模型 vs 单模型”而是“路由策略 vs 路由策略”。很多团队采用主模型加备用模型的降级方案主模型用低成本模型遇到限流、超时或质量不达标时降级到高成本模型。降级策略会显著改变总成本。假设低成本模型单次任务 0.01 元高成本模型单次任务 0.08 元如果低成本模型的失败率是 15%则平均成本变为平均成本 0.01 × 0.85 0.08 × 0.15 0.0205 元失败率从 15% 提高到 30% 时平均成本变成 0.031 元几乎翻倍。所以选型时不能只看模型单价还要看这个模型在真实任务上的成功率。低成本模型如果经常触发降级综合成本不一定比高成本模型低。4.3 超时重试和流式输出带来的成本波动请求超时重试是另一个容易忽略的成本项。一个请求如果第一次超时第二次成功那么第一次请求消耗的 token 同样会计费。把超时时间设得过短会导致高并发下大量请求重复发送账单成倍增长把超时时间设得过长又会拖慢用户体验。建议的做法是评估时按真实业务的重试策略统计“有效请求数”和“总请求数”计算一个重试放大系数。成本公式修正为实际成本 单次请求成本 × (1 重试率)流式输出本身不改变 token 总数但会影响超时和断连判断。客户端如果在中途断开流式连接有些平台会按已经生成的 token 计费有些平台会取消计费。这个差异也要在选型前确认否则线上日志里出现大量半截响应账单仍然照收。5. 常见问题与排查路径5.1 现象账单金额和本地估算差别很大可能原因出现了重试请求账单里有重复记录。缓存字段没有取到漏算了缓存读取折扣或缓存写入费用。平台对同一请求额外收取了系统级 token比如自动注入的指令或安全提示。一次业务操作触发了多个模型调用本地却只算了一次。检查方式从平台导出按 request_id 维度的用量明细和本地记录的request_id、prompt_tokens、completion_tokens逐条对比。先找多出来的请求再找 token 数对不上的请求。5.2 现象同一请求在不同时间价格不同可能原因平台的计费规则调整价格表有版本差异。请求前缀的缓存状态变化第一次没有命中之后命中价格自然会降。平台在不同时段的批量折扣不同比如夜间批量任务有折扣。通过不同的接入端点调用价格本来就不同。检查方式保留每次请求的时间戳、模型名和端点信息按小时或按天聚合再看趋势。如果时间、端点和模型名都一致价格仍然不同就要重点看缓存命中字段。5.3 现象长输入短输出的任务反而比想象中贵可能原因多轮对话中每轮都带完整历史输入 token 是轮数乘以单轮历史长度。长输入没有触发缓存每次都按全量输入原价计算。prompt 被平台自动拆分成多次内部调用比如需要检索或分析的过程产生了额外 token。处理建议这类任务优先开启上下文缓存并且把系统提示词、工具定义等固定内容放在前缀最前面保证前缀稳定。不要让用户消息随机插入系统提示词之前否则缓存命中率会明显下降。5.4 排查顺序建议遇到成本异常时按照下面的顺序排查确认对比口径本地脚本和平台账单是否使用同一请求 ID 和同一统计周期。确认请求数量先数次数再数 token最后才算钱。次数对不上后面都白算。确认 token 明细逐条比对prompt_tokens、completion_tokens和缓存字段。确认重试与降级看日志里是否有失败后重试或降级转发的记录。确认计费规则阅读平台最新的计费文档看是否有最低消费、按请求计费或附加费用。注意排查成本问题不要只看总额一定要拿到请求级明细。总额只能告诉你“贵了”请求级明细才能告诉你“贵在哪里”。6. 成本评估的落地建议和可复用检查清单6.1 把成本评估做成固定工作流而不是一次性脚本成本评估应该像性能测试一样进入迭代流程。每次更换模型、调整 prompt 或改变缓存策略都重新跑一遍样本集。推荐把这个流程固化到 CI 或发布前的检查脚本中至少包含四步准备样本集从线上日志中抽样保证任务分布真实。跑批评估对每个候选模型跑同一批样本记录 token 和耗时。计算成本按统一价格配置计算单任务成本、成功率和缓存命中率。生成对比报告输出平均成本、P95 成本、失败率和缓存命中率供选型决策。把第 1 步到第 4 步固化成脚本后任何人拿到新模型都能在半小时内得到一份可对比的成本报告而不是靠口头传说。脚本本身没有太高技术门槛真正有价值的是样本集和统计口径的稳定性。6.2 发布前成本检查清单在做模型切换或大版本上线前建议按表格逐项确认检查项确认标准计费价格使用当前最新价格表确认是否含税、是否含批量折扣样本集覆盖摘要、抽取、代码生成、多轮对话等主要任务缓存命中率真实前缀分布下的命中率已统计重试率按线上超时和限流配置实测降级成本主模型失败后的平均成本已计算输出质量同一样本上人工抽检通过率达标并发配额模型的 RPM/TPM 限制满足预估峰值回滚方案模型切换支持按流量比例灰度可快速回退这份清单在模型上线前过一遍能避免大部分“上线后发现账单超预算”的情况。缺少任何一项都应该在发布说明里明确标注风险。6.3 成本对比结果的使用边界成本是选型的重要维度但不是唯一维度。单位成本只是“单次任务支出”完整的成本还包括接入开发成本、prompt 调试成本、推理延迟带来的用户体验损失、失败重试造成的人工运维成本以及模型效果差异导致的产品指标变化。因此当看到“GLM-5.3 成本仅为 FABLE 5 的八分之一”这类信息时正确的处理方式是把它当作一个待验证的假设而不是最终结论。先用本文的样本集和脚本在真实任务上复测一遍再结合成功率、延迟和效果指标做综合判断。真正有价值的不是“便宜八倍”这个数字而是这个数字在什么条件下成立、你的业务是否满足这些条件。对刚接触大模型成本评估的团队建议从 20 条样本的小评估开始把脚本跑通把缓存字段接进来再逐步扩大到完整线上分布。成本评估本身不复杂复杂的是让评估条件贴近真实生产环境只要口径统一、样本可复现、数据可追溯任何成本结论都能经得起验证。