1. 为什么按调用次数计费会把你带进沟里
做模型选型这件事,我踩过最贵的一个坑,不是选错了模型,而是选错了评测口径。
早些年团队要接一个文本结构化抽取的任务,候选方案有三四个,价格从每百万 token 几毛钱到几十块钱不等。当时我们做了一件看起来很合理的事:拿一批测试样本跑一遍,统计每个模型的平均单次调用成本,然后挑最便宜的那个。结果上线两周后,账单比预期高了将近四倍,而那个"最便宜"的模型,最后被我们连夜换掉了。
问题出在哪?出在单次调用成本这个指标本身是失真的。便宜的那个模型,输出格式经常跑偏,需要重试;遇到长文本会截断,需要二次补调;偶尔还会把结构化字段拼错,得走一遍校验再回炉。这些重试、补调、校验的成本,全都没算进"单次调用成本"里。真正该看的指标,是每完成一个成功任务的总成本,也就是 cost-per-success。
这个词直译过来就是"单次成功成本",但它的内涵比字面意思要深。它不是简单地把总花费除以调用次数,而是把所有为了达成一个合格结果而付出的调用、重试、人工干预、下游返工全部折算进去,最后落到"一个真正可用的产出"上。换句话说,它衡量的是"办成一件事要花多少钱",而不是"敲一次门要花多少钱"。
这篇内容适合谁看?如果你正在做模型选型、API 采购决策、或者负责一条依赖外部模型的业务链路,并且被"单价便宜但总账很贵"这件事困扰过,那这篇就是写给你的。我会把 cost-per-success 这套评测方法从指标定义、样本设计、埋点采集、到最终决策的完整链路拆开讲,中间穿插我自己踩过的坑和几个反直觉的结论。不需要你有很深的算法背景,只要你会写点脚本、能读懂账单,就能照着做。
先说一个反直觉的结论打底:在多数真实业务里,单价最贵的模型往往不是总成本最高的,单价最便宜的模型也往往不是总成本最低的。决定成败的是"一次做对的概率",而这个概率和单价之间,几乎没有线性关系。
2. 把 cost-per-success 拆成可计算的四个分量
要算这个指标,先得把它拆开。我习惯把它拆成四个分量,每个分量都能单独采集、单独归因,最后合成一个总账。这样拆的好处是,当结果异常时你能立刻定位是哪个环节在漏钱,而不是对着一个总数干瞪眼。
2.1 基础调用成本:不只是 token 单价乘以用量
基础调用成本是最直观的一块,就是模型 API 本身的计费。但这里有个细节很多人会忽略:输入 token 和输出 token 的单价通常不一样,而且缓存命中、批量调用、阶梯定价都会影响最终单价。
举个具体的算法。假设某模型输入 1 元/百万 token,输出 3 元/百万 token。一个任务平均输入 800 token、输出 200 token,那么单次基础成本是:
输入:800 / 1,000,000 * 1 = 0.0008 元 输出:200 / 1,000,000 * 3 = 0.0006 元 单次合计:0.0014 元看起来便宜得可以忽略。但如果这个任务一天要跑 50 万次,一天就是 700 元,一个月两万多。所以基础成本必须按真实业务量去放大看,不能只看单次。
这里有个实操建议:把输入和输出分开记账。因为很多优化手段(比如精简 prompt、压缩上下文)只影响输入侧,而输出侧的成本往往由任务本身决定,压不动。分开记你才知道优化空间在哪。
2.2 重试与补调成本:被低估的隐形大头
重试成本是 cost-per-success 里最容易被漏掉、也最容易失控的一块。
什么叫重试?模型返回了结果,但结果不合格——格式错了、字段缺了、逻辑矛盾了、或者干脆拒答了——你需要再调一次。每一次重试都是一次完整的调用成本,而且往往因为加了纠错提示,输入 token 还更长了。
我实测过一个抽取任务,某模型的首轮合格率只有 62%,意味着 38% 的请求要重试。重试之后还有一部分不合格,需要第三次。算下来平均每个成功任务要调用 1.9 次。也就是说,它的实际成本是标称单价的 1.9 倍,这个倍数在选型时如果没算进去,结论会完全反过来。
补调是重试的近亲,但场景不同。补调通常发生在"结果基本可用但有局部缺失"的时候,比如长文档被截断了,你只需要补跑被截断的那一段。补调的单价可能更低(因为输入更短),但它的触发逻辑更复杂,采集时容易漏。
提示:重试和补调一定要在代码里打上明确的标记位,比如
attempt_index和call_reason,否则事后你根本分不清哪些调用是首轮、哪些是补救。我见过太多团队因为没打这个标记,最后只能拍脑袋估个系数,评测结果自然不可信。
2.3 人工干预成本:最贵也最该被量化的一块
人工干预成本是 cost-per-success 里最"肉疼"的部分,因为它直接对应人力。
什么算人工干预?结果需要人工复核、修正、重新标注、或者因为错误触发了客诉处理,这些都算。它的计量单位不是 token,而是分钟。你需要估算每个不合格结果平均消耗多少人工时间,再乘以人力时薪。
这里有个换算技巧:把人工时间折算成"等效调用成本"。比如某岗位时薪 60 元,处理一个不合格结果平均花 3 分钟,那就是 3 元。对比一下基础调用成本才 0.0014 元,你会发现人工干预成本是基础成本的几千倍。这就是为什么"便宜模型 + 大量人工兜底"这条路,在规模化之后几乎必然亏本。
我在做评测时有个硬性要求:任何需要人工介入的环节,都必须记录介入原因和耗时。哪怕只是粗略记录,也比完全不算强。因为一旦你把人工成本摆到台面上,很多"看起来便宜"的方案会瞬间现原形。
2.4 下游返工成本:错误传导的连锁账
下游返工成本是最隐蔽的一块。模型输出的错误,往往不会当场暴露,而是传导到下游环节才炸出来。
比如一个分类任务,模型把某条数据分错了类,下游的推荐系统基于错误分类做了推荐,用户点了不满意,这个损失怎么算?再比如一个信息抽取任务,抽错了一个金额字段,下游的对账系统跑出异常,需要人工排查,这个排查时间怎么算?
严格来说,下游返工成本很难精确归因,但你可以用一个传导系数来近似。做法是:统计历史上因为模型输出错误导致的下游异常比例,乘以处理这些异常的平均成本。这个系数不需要很精确,有个量级上的估计就足够影响决策了。
把四个分量加起来,cost-per-success 的完整公式是:
cost-per-success = (基础调用成本 + 重试补调成本 + 人工干预成本 + 下游返工成本) / 成功任务数注意分母是成功任务数,不是总调用数。这个细节决定了整个指标的性质:它惩罚失败,奖励一次做对。
3. 评测样本怎么设计,才能让结论站得住
指标定义清楚了,接下来是样本。样本设计是 cost-per-success 评测里最容易被敷衍、也最影响结论可信度的环节。我见过太多团队随便抽几十条数据跑一遍就下结论,结果上线后完全不是那么回事。
3.1 样本要覆盖"难度分层",不能只抽简单样本
最典型的错误是:测试集里全是规整、干净、短小的样本,模型跑起来个个满分,于是得出"这个便宜模型完全够用"的结论。上线后遇到脏数据、长文本、边界情况,合格率断崖式下跌。
正确的做法是按难度分层抽样。我一般分三层:
- 简单层:格式规整、长度适中、语义清晰的样本,占 40% 左右。这层用来测"基本盘",如果连这层都跑不好,直接淘汰。
- 中等层:有一定噪声、长度偏长、或者需要一定推理的样本,占 40% 左右。这层是真实业务的主力,最能拉开模型差距。
- 困难层:脏数据、超长文本、边界情况、对抗性输入,占 20% 左右。这层用来测"下限",决定你需要多少人工兜底。
分层之后,每一层单独算 cost-per-success,最后按真实业务的难度分布加权。这样得出的结论才贴近实际。
3.2 样本量要够,但更重要的是"重复跑"
样本量的问题很直接:太少,结论不稳定;太多,评测成本高。我的经验是,每个模型在每个难度层至少跑 200 条,这样合格率的置信区间才收得够窄。如果预算紧张,宁可减少候选模型数量,也不要压缩样本量。
但比样本量更重要的是重复跑。同一个样本,同一个模型,跑两次结果可能不一样,因为模型输出有随机性。如果你只跑一次就下结论,等于在赌运气。
我的做法是:关键样本重复跑 3 次,取平均合格率和平均成本。这样能过滤掉随机波动,也能顺便测出模型的稳定性。稳定性差的模型,即使平均合格率不错,实际成本也会因为波动而偏高。
3.3 成功判定标准必须提前定死,不能事后找补
这是最容易被忽视、也最容易引发争议的一点:什么叫"成功"?
如果判定标准模糊,评测就会变成"看结果顺眼就算成功",不同人判出来的合格率能差出一倍。所以判定标准必须在评测开始前就写死,最好能自动化判定。
我的建议是分两级判定:
- 硬性判定:格式、字段完整性、数值范围、枚举值合法性等,这些可以用脚本自动校验,不通过直接判失败。
- 软性判定:语义正确性、逻辑一致性等,这些需要人工或用一个更强的模型来判。如果用人判,要提前写好判定细则,并且让两个人独立判、交叉核对。
注意:软性判定如果用人来做,一定要控制好判定者的一致性。我吃过这个亏——两个标注员对"语义正确"的理解不一致,导致同一批数据判出来的合格率差了 15 个百分点,整个评测白做。后来我们改成先一起判 50 条对齐标准,再各自独立判,一致性才上来。
3.4 一个可直接套用的样本设计模板
把上面的原则落成一张表,大概是这个结构:
| 难度层 | 占比 | 样本特征 | 每模型样本量 | 重复次数 | 判定方式 |
|---|---|---|---|---|---|
| 简单 | 40% | 规整、短、清晰 | 200 | 1 | 自动校验 |
| 中等 | 40% | 有噪声、偏长 | 200 | 3 | 自动 + 人工抽检 |
| 困难 | 20% | 脏数据、超长、边界 | 200 | 3 | 自动 + 人工全检 |
按这个模板跑下来,一个模型的评测成本大概在几百到几千次调用之间,对于选型决策来说完全可接受。而且因为分层清晰,你还能看出每个模型"强在哪、弱在哪",而不只是一个笼统的总分。
4. 埋点与数据采集:让每一次调用都能被追溯
指标和样本都定了,接下来是工程实现。cost-per-success 的评测能不能落地,八成取决于埋点做得好不好。埋点做得好,数据自己会说话;埋点做得糙,你只能靠猜。
4.1 每次调用必须记录的字段清单
我整理了一份最小字段集,缺一个都会影响后续分析:
| 字段名 | 含义 | 为什么需要 |
|---|---|---|
| task_id | 任务唯一标识 | 把同一任务的所有调用串起来 |
| model_name | 模型标识 | 区分不同候选模型 |
| attempt_index | 第几次尝试 | 区分首轮和重试 |
| call_reason | 调用原因 | 首轮/重试/补调/纠错 |
| input_tokens | 输入 token 数 | 算基础成本 |
| output_tokens | 输出 token 数 | 算基础成本 |
| latency_ms | 耗时 | 辅助判断稳定性 |
| raw_output | 原始输出 | 事后复核判定 |
| judge_result | 判定结果 | 成功/失败/部分成功 |
| judge_reason | 判定原因 | 归因失败类型 |
| human_minutes | 人工耗时 | 算人工成本 |
| downstream_flag | 下游是否异常 | 算返工成本 |
这份清单看起来繁琐,但真正实现起来就是在一个统一的调用封装函数里加几行日志。关键是所有调用都必须走同一个封装,不能有的地方直接调 SDK、有的地方走封装,否则数据一定不全。
4.2 用统一封装函数兜住所有调用
我一般会写一个call_model的封装函数,所有对模型的调用都从这里走。它的职责包括:记录调用前后的时间戳、统计 token、捕获异常、写入日志、返回统一格式的结果。
伪代码大概长这样:
def call_model(task_id, model_name, prompt, attempt_index, call_reason): start = now() try: resp = model_client.invoke(prompt) raw = resp.text in_tok = resp.usage.input_tokens out_tok = resp.usage.output_tokens status = "ok" except Exception as e: raw = "" in_tok = out_tok = 0 status = "error" latency = now() - start log_call({ "task_id": task_id, "model_name": model_name, "attempt_index": attempt_index, "call_reason": call_reason, "input_tokens": in_tok, "output_tokens": out_tok, "latency_ms": latency, "raw_output": raw, "status": status, }) return raw这个封装的价值在于:它把"记账"这件事从业务逻辑里剥离出来了。业务代码只管调,记账自动完成。这样即使业务逻辑改来改去,账也不会乱。
4.3 判定环节的自动化与人工兜底
判定环节我建议尽量自动化。硬性判定(格式、字段、范围)用脚本跑,几秒钟就能出结果。软性判定如果预算允许,可以用一个更强的模型来判,但要注意判定模型本身也有偏差,最好和人工判定做一次对齐校准。
人工判定只用在两个地方:一是软性判定的抽检,二是自动判定拿不准的边界情况。这样能把人工成本压到最低,同时保证判定质量。
这里有个小技巧:把判定结果和原始输出一起存下来。这样事后如果对某个判定有疑问,可以直接翻出原始输出复核,不用重新跑一遍。我在做归因分析时,这个习惯帮我省了大量时间。
4.4 成本归因的常见陷阱
埋点做完了,数据也采了,但归因时还是有几个坑要避开。
第一个坑是把失败调用的成本摊到成功任务上时算错分母。正确做法是:总成本除以成功任务数,而不是除以总调用数。前者才是 cost-per-success,后者只是平均调用成本。
第二个坑是忽略缓存和批量的影响。有些平台对重复输入有缓存折扣,有些支持批量调用降价。如果你的调用模式能吃到这些折扣,实际成本会比标称低不少。评测时要按真实调用模式来,不能按标称单价算。
第三个坑是人工成本按"平均"算,而不是按"分布"算。人工干预的耗时往往不是均匀分布的,少数困难样本可能吃掉大部分人工时间。所以除了平均值,还要看 P90、P95 这些分位数,才能看出尾部风险。
5. 一个完整的评测实例:三个候选模型的真实账本
理论讲完了,来看一个我实际做过的评测。为了不涉及具体业务,我把场景抽象成一个"合同关键信息抽取"任务:从一段合同文本里抽出甲方、乙方、金额、签署日期四个字段,输出 JSON。
5.1 候选模型与评测设置
三个候选模型,按标称单价从低到高记为 A、B、C。评测设置如下:
- 样本:600 条,按 4:4:2 分简单、中等、困难三层
- 每模型每层 200 条,中等和困难层重复跑 3 次
- 判定:格式和字段完整性自动校验,语义正确性人工抽检 20%
- 人工成本按 60 元/小时折算
5.2 原始数据与成本计算
跑完之后,核心数据如下(数字做了脱敏处理,但比例关系真实):
| 指标 | 模型 A | 模型 B | 模型 C |
|---|---|---|---|
| 标称单价(元/百万 token) | 0.5 | 3 | 15 |
| 首轮合格率 | 58% | 81% | 93% |
| 平均调用次数/成功任务 | 2.1 | 1.3 | 1.05 |
| 基础调用成本(元/成功任务) | 0.003 | 0.012 | 0.045 |
| 重试补调成本(元/成功任务) | 0.003 | 0.004 | 0.002 |
| 人工干预成本(元/成功任务) | 1.8 | 0.5 | 0.12 |
| 下游返工成本(元/成功任务) | 0.6 | 0.15 | 0.03 |
| cost-per-success(元) | 2.41 | 0.67 | 0.20 |
看到这个结果,第一反应可能是"贵有贵的道理"。但真正有意思的是拆开看。
5.3 拆解:钱到底花在哪了
模型 A 的标称单价只有模型 C 的三十分之一,但它的 cost-per-success 是模型 C 的十二倍。差距全在人工干预和下游返工上。
模型 A 的首轮合格率只有 58%,意味着将近一半的任务要人工兜底。按每个不合格结果平均 3 分钟人工算,光人工成本就 1.8 元。再加上下游因为字段抽错导致的返工,又是 0.6 元。这两项加起来 2.4 元,占了总成本的 99% 以上。基础调用成本那 0.003 元,几乎可以忽略不计。
模型 B 是个"中庸"选择,各项都不突出但也不差。它的 cost-per-success 是 0.67 元,比 A 好很多,但比 C 还是贵了三倍多。
模型 C 的标称单价最高,但首轮合格率 93%,人工干预极少,下游返工也少。算下来 cost-per-success 只有 0.20 元,反而是最便宜的。
这个结果印证了开头那个反直觉结论:在人工成本占主导的场景里,单价高的模型往往总成本更低。
5.4 分难度层看,结论会不会翻转
只看总账还不够,还要分层看。分层数据如下:
| 难度层 | 模型 A 合格率 | 模型 B 合格率 | 模型 C 合格率 |
|---|---|---|---|
| 简单 | 82% | 95% | 99% |
| 中等 | 55% | 80% | 93% |
| 困难 | 31% | 62% | 84% |
在简单层,三个模型差距不大,A 也能到 82%。如果业务里全是简单样本,A 或许能用。但真实业务里中等和困难样本占大头,A 在这两层直接崩了。
模型 B 在困难层只有 62%,意味着三分之一的困难样本要人工兜底。如果业务对困难样本的容忍度低,B 也不够用。
模型 C 在困难层还有 84%,是唯一一个在三个层都站得住的。
这个分层视角的价值在于:它告诉你每个模型的"能力边界"在哪。如果你的业务难度分布偏简单,可以选便宜一档的;如果偏困难,就得选贵的。一刀切地看总账,会丢掉这个信息。
5.5 稳定性:被忽略的第五个分量
还有一个维度没进上面的表,但很重要:稳定性。
模型 A 在重复跑时,同一批样本的合格率波动能达到正负 8 个百分点。模型 C 的波动只有正负 2 个百分点。这意味着 A 的成本是不可预测的——今天可能 2 元,明天可能 3 元。而 C 的成本相对稳定,便于做预算。
对于要长期跑的业务,稳定性本身就是一种价值。我在选型时会把稳定性作为一票否决项:波动太大的模型,即使平均成本低,也不选,因为它的尾部风险太高。
6. 从评测结果到选型决策:别只看一个数
算出 cost-per-success 之后,怎么用它做决策?这里有几个我总结的原则。
6.1 先设成本红线,再在红线内选质量
不要一上来就比谁便宜。正确的顺序是:先根据业务预算和人力情况,定一条 cost-per-success 的红线,然后在红线以内的模型里,选质量最好的那个。
比如上面的例子里,如果红线是 1 元/成功任务,那 A 直接出局,B 和 C 都在红线内,选 C。如果红线是 0.5 元,那只有 C 能选。
这条红线的意义在于,它把"成本"和"质量"这两个维度解耦了。你不需要在两者之间做模糊的权衡,而是先用成本筛掉不可行的,再用质量选最优的。
6.2 混合路由:不是所有任务都值得用最贵的模型
一个进阶玩法是混合路由:简单任务走便宜模型,困难任务走贵模型。
具体做法是:先用一个轻量的分类器(或者简单的规则)判断任务难度,简单任务路由到 A,困难任务路由到 C。这样整体成本能压下来,同时保证困难任务的质量。
我实测过一个混合方案:70% 的任务走便宜模型,30% 走贵模型,整体 cost-per-success 比全走贵模型低了约 35%,而质量只下降了不到 2 个百分点。这个性价比相当可观。
但混合路由有个前提:难度判断本身要准。如果判断错了,把困难任务路由给了便宜模型,反而会增加人工兜底成本。所以难度分类器本身也要评测,不能想当然。
6.3 定期复评:模型和价格都在变
cost-per-success 不是一次算完就一劳永逸的。模型在迭代,价格在调整,你的业务分布也在变。我建议每季度复评一次,至少把主力模型重新跑一遍。
复评时重点看三个变化:一是模型本身有没有升级(合格率可能变),二是价格有没有调整(基础成本可能变),三是业务难度分布有没有漂移(加权方式可能变)。这三个里任何一个变了,原来的结论都可能不成立。
提示:复评不需要每次都跑全量样本。可以维护一个"核心样本集",几百条,覆盖三个难度层,每次复评都跑这个集。这样成本可控,结论也有连续性。
6.4 把评测结果沉淀成可复用的资产
最后一点,也是我觉得最有价值的:把评测的样本、脚本、判定逻辑沉淀下来,变成团队的固定资产。
下次再选型,不用从零开始,直接复用这套框架,换个候选模型就能跑。我现在的做法是维护一个评测仓库,里面包括:分层样本集、调用封装、判定脚本、成本计算脚本、结果可视化。新模型接进来,半天就能出一份完整的 cost-per-success 报告。
这套资产的价值,随着你做的选型次数增加而指数级上升。第一次搭可能要花两三天,之后每次用都是几小时的事。
7. 几个我踩过的坑和对应的解法
最后分享几个实操中踩过的坑,都是真金白银换来的。
坑一:用测试环境的调用量估算成本。测试环境往往调用量小、样本简单,算出来的成本严重偏低。解法是:用生产环境的真实流量做影子评测,或者至少用生产流量的采样。
坑二:忽略 token 计费的边界。有些平台对超长输入有额外计费,有些对输出长度有限制。评测时要按真实输入长度分布来,不能只用平均长度。解法是:统计输入长度的 P50、P90、P99,按分布算成本。
坑三:人工成本按统一时薪算。不同环节的人工成本不一样,复核和返工的时薪可能差一倍。解法是:分环节记录人工耗时和对应时薪,不要用一个平均值糊弄。
坑四:判定标准在执行中悄悄漂移。评测跑了一周,判定员的尺度可能变了。解法是:定期用固定样本做校准,发现漂移及时纠正。
坑五:只看平均值,不看分布。平均 cost-per-success 低,不代表每个任务都低。解法是:同时看 P90、P95,关注尾部风险。
这些坑的共同点是:它们都不会在评测报告里直接暴露,但会在上线后以账单的形式找上门。把 cost-per-success 这套方法做扎实,本质上是在用评测阶段的一点额外投入,换上线后的大笔成本节省。这笔账,怎么算都划算。