LLM路由实战:从cost-per-call到cost-per-success的成本优化指南 📅 发布时间:2026/8/29 9:19:14 👁 浏览次数: 先说一个很现实的现象大多数团队在接入大模型之后衡量成本的方式仍然停留在“每次调用多少钱”这个维度。上线一个 Agent、一个 RAG 问答机器人第一个看的是 prompt 输入多少 token、输出多少 token第二个看的是单次调用接口返回了多快、花了多少费用。这种统计方式不是不对而是不够。真正推动业务落地的成本不是“调用一次”的成本而是“成功解决一次问题”的成本。LLM 路由要做的事情就是在不同模型、不同策略之间做调度把请求分发给最合适的模型同时把成本控制在不影响业务目标的范围之内。本文想从“成本核算”的角度切入把 LLM 路由讲透讲清楚为什么 cost-per-success 才是真正值得衡量的指标并且给出可以落地的路由策略与计算模型。适合读者正在做 LLM 应用落地、想优化多模型调用成本的后端开发者学习收益理解 LLM 路由的核心逻辑与指标设计掌握 cost-per-success 的计算方式并能编码实现一套轻量路由策略内容边界重点讨论路由指标与工程实现不涉及具体厂商模型的价格对比也不会展开到分布式网关等重架构。1. 为什么 LLM 路由会成为一个问题1.1 大模型接入从“单模型”走向“多模型混用”早期 LLM 应用大多只接一个模型。需求简单时一个高能力模型处理所有请求确实省心但伴随业务扩大问题逐渐暴露高能力模型的价格普遍更高所有流量都打向最高档位成本浪费明显不同任务复杂度差异大简单分类问题和复杂多步推理问题使用同一模型本就无法发挥路由的价值不同模型在不同领域有各自优势比如有的模型擅长代码有的模型长文本理解更强单一模型并不总是最优解。当团队开始接入多个模型LLM 路由便自然成为一个工程化问题如何在请求进来时决定走哪个模型、走什么策略、什么情况下兜底。1.2 影响模型选择的维度不止价格路由决策的第一反应通常是“看价格”但真正落到业务中时需要同时考虑多个维度维度说明任务类型文本分类、信息抽取、代码生成、多轮对话等任务对模型能力要求不同输入长度长文档处理需要更大的上下文窗口成本也更高响应质量对回答准确性要求高的场景需要更高能力的模型响应时间实时交互场景对延迟敏感需要更快的小模型成本预算不同业务线有不同预算需要按比例分配流量如果只按价格路由很容易出现“省了单次调用费结果任务反复失败最终从头到尾花费更高”的尴尬局面。1.3 路由的价值不在“省钱”而在“省对地方”好路由不是单纯挑最便宜模型而是在保证成功率的前提下让每次请求尽量匹配合适档位的模型。这里就引出了本文的核心概念路由决策的指标应该从 cost-per-call 转向 cost-per-success。2. 从 cost-per-call 到 cost-per-success2.1 两个指标分别衡量什么cost-per-call 指的是单次 API 调用的费用通常等于输入 token 费用加上输出 token 费用。它简单直接容易统计适合做成本预估但并不掌握业务结果。cost-per-success 指的是每成功完成一次业务目标所花费的成本。它把成本除以成功次数把失败、重试、兜底开销都纳入分母能够真实反映“拿到一个有效结果”到底花了多少钱。举个例子方案 A每次调用成本 0.01 美元调用 100 次成功 90 次方案 B每次调用成本 0.05 美元调用 100 次成功 99 次。只看单次调用成本A 更便宜。但计算 cost-per-success方案 A100 × 0.01 ÷ 90 ≈ 0.0111 美元/成功方案 B100 × 0.05 ÷ 99 ≈ 0.0505 美元/成功。单看数字A 仍然占优。但如果方案 A 失败后需要人工介入修复或者失败结果会导致下游流程重跑总成本就会远远超出 API 调用费。这种“隐藏成本”恰恰是 cost-per-call 无法体现的。2.2 为什么 LLM 路由要关注 cost-per-success模型调用的失败是常态。失败类型包括输出格式不符合要求比如期望 JSON 返回了普通文本被内容安全策略拦截返回空结果或错误码模型上下文长度超限导致请求被截断请求超时或服务不可用模型生成结果与事实不符被业务规则校验拦截。这些情况下本次调用已经计费但没有产生有效结果。后续要么重新调用、要么升级模型、要么人工处理都会继续产生成本。所以LLM 路由如果把 cost-per-call 当成唯一指标很可能为了降低单价把请求全部推向低成本模型最后失败率上升整体成本反而更高。而基于 cost-per-success 做路由可以让调度器更聪明地把“难题”分配给“强模型”把“简单题”保留给“轻量模型”在成本与成功率之间形成平衡。2.3 cost-per-success 的通用计算式在不考虑人工成本时可以这样计算cost_per_success 总调用成本 / 成功次数如果考虑重试和兜底策略则需要把相关成本纳入分子总成本 原始调用成本 重试调用成本 兜底调用成本 人工修正成本 cost_per_success 总成本 / 成功次数其中“成功次数”不是“调用次数”而是业务侧定义的有效结果次数。比如一个问答机器人用户得到有效答复并进入满意度确认流程才算一次成功如果用户反复追问“你没回答我的问题”就不能算成功。3. LLM 路由的常见策略与实现思路3.1 基于规则的静态路由最基础的路由方式是把请求特征映射到固定模型。一个典型场景短文本分类请求走小模型长文本阅读理解走大模型。伪代码如下def route_by_rule(request): input_text request[input] if len(input_text) 3000: return high_capability_model elif code in request.get(task_type, ): return code_model else: return light_model静态路由的优势是简单、可控、易排查缺点是规则需要人工维护无法自适应模型效果变化。3.2 基于评分的动态路由动态路由会为每个模型打分综合质量、价格、延迟、可用率等因素后选择综合分最高的模型。一个简易思路def score_model(model_name, request, success_stats): quality_score success_stats.get(model_name, {}).get(quality, 0.5) price_score 1.0 / (1.0 cost_per_call(model_name, request)) latency_score 1.0 / (1.0 latency_ms(model_name, request)) final_score 0.5 * quality_score 0.3 * price_score 0.2 * latency_score return final_score动态路由依赖数据反馈适合已经有流量积累、希望持续迭代的团队。3.3 基于信号反馈的路由这是把成功/失败信号纳入路由决策的方式也是 cost-per-success 指标落地的关键。当一个请求被某个模型处理后采集如下信号是否成功返回合法结果业务侧规则校验是否通过是否需要重试是否需要降级或升级模型。信号积累到一定数量后路由层会动态调整策略某个模型在“代码解释”任务上的成功率下降即使它单价便宜也会降低优先级。4. 完整实战基于 cost-per-success 的轻量路由实现下面用 Python 写一个可运行的最小示例演示 cost-per-success 统计和路由选择的联动。4.1 项目结构llm_router/ ├── router.py # 路由入口 ├── cost_tracker.py # 成本与成功率统计 ├── models.py # 模型配置 └── main.py # 演示入口4.2 模型配置# models.py MODEL_CONFIG { light_model: { price_per_1k_input: 0.001, price_per_1k_output: 0.002, capabilities: [classification, extraction], }, high_capability_model: { price_per_1k_input: 0.01, price_per_1k_output: 0.03, capabilities: [reasoning, complex_generation], }, }这里不绑定具体厂商只作为成本计算示例。4.3 成本与成功率统计模块# cost_tracker.py class CostTracker: def __init__(self): self.records [] def record_call(self, model_name, input_tokens, output_tokens, success): input_cost input_tokens / 1000 * MODEL_CONFIG[model_name][price_per_1k_input] output_cost output_tokens / 1000 * MODEL_CONFIG[model_name][price_per_1k_output] self.records.append({ model: model_name, input_tokens: input_tokens, output_tokens: output_tokens, cost: input_cost output_cost, success: success, }) def cost_per_success(self, model_nameNone): records self.records if model_name: records [r for r in records if r[model] model_name] total_cost sum(r[cost] for r in records) success_count sum(1 for r in records if r[success]) if success_count 0: return float(inf) return total_cost / success_count def success_rate(self, model_nameNone): records self.records if model_name: records [r for r in records if r[model] model_name] if not records: return 0.0 return sum(1 for r in records if r[success]) / len(records)注意这里的MODEL_CONFIG需要从models.py导入实际开发中可调整为依赖注入避免循环引用。4.4 路由模块# router.py import random class Router: def __init__(self, tracker): self.tracker tracker def route(self, request): # 示例路由结合任务类型和成本成功率 task_type request.get(task_type, general) if task_type in (complex_reasoning, long_doc): return high_capability_model # 对一般任务观察近期的 cost_per_success light_cps self.tracker.cost_per_success(light_model) high_cps self.tracker.cost_per_success(high_capability_model) if light_cps high_cps: return light_model else: return high_capability_model这个路由逻辑很简单复杂任务直接走强模型一般任务比较两个模型的成功成本选择成功成本更低的模型。真实项目中当然还要考虑延迟、并发、模型可用率等因素示例只用来展示思路。4.5 模拟调用流程# main.py from models import MODEL_CONFIG from cost_tracker import CostTracker from router import Router def fake_llm_call(model_name, input_tokens, output_tokens): 模拟一次模型调用返回是否成功。 # 简单模拟强模型成功率更高轻量模型有概率失败 if model_name high_capability_model: success random.random() 0.98 else: success random.random() 0.85 return success def main(): tracker CostTracker() router Router(tracker) requests [ {task_type: classification, input_tokens: 200, output_tokens: 50}, {task_type: complex_reasoning, input_tokens: 1500, output_tokens: 300}, {task_type: classification, input_tokens: 180, output_tokens: 40}, {task_type: extraction, input_tokens: 500, output_tokens: 120}, {task_type: complex_reasoning, input_tokens: 2000, output_tokens: 400}, ] for i, req in enumerate(requests, 1): model_name router.route(req) input_tokens req[input_tokens] output_tokens req[output_tokens] success fake_llm_call(model_name, input_tokens, output_tokens) tracker.record_call(model_name, input_tokens, output_tokens, success) print(f请求 {i}: 路由到 {model_name}, 成功{success}) print(\n 统计结果 ) for model_name in MODEL_CONFIG: print(f{model_name}: 成功率{tracker.success_rate(model_name):.2%}, fcost_per_success{tracker.cost_per_success(model_name):.4f}) print(f整体 cost_per_call{(sum(r[cost] for r in tracker.records) / len(tracker.records)):.4f}) print(f整体 cost_per_success{tracker.cost_per_success():.4f}) if __name__ __main__: main()4.6 运行结果说明每次运行结果会因为随机模拟而不同但可以预期复杂任务集中走向 high_capability_model常规任务根据累计 cost_per_success动态在轻量模型和强模型之间选择最终打印的整体 cost_per_success会比简单地把所有请求都打向同一个模型更有参考价值。这里刻意不把模拟模型替换成真实 API因为成本计算和路由逻辑本身不依赖具体厂商读者可以把fake_llm_call替换成自己的模型调用 SDK。5. 在业务中真正落地 cost-per-success 要解决哪些问题5.1 定义“成功”是第一步不同业务对成功的定义差异很大。做客服问答成功可能是“用户找到解决方案”做内容审核成功可能是“正确拦截违规内容”做代码生成成功可能是“代码通过编译”。团队必须和业务侧确认什么结果算有效输出什么结果算无效浪费。只有定义清楚了统计数据才有意义。5.2 token 计量要放在统一口径下不同模型对 token 的编码方式不同成本统计需要保证输入 token 数在统一 tokenizer 下计算或者直接使用模型返回的 usage 字段输出 token 数包括补全内容不能漏计重试导致的重复计费要单独记录。建议在调用层封装统一 SDK所有请求都走同一入口把 token 使用量和费用明细写入日志。5.3 失败与兜底成本要单独记录cost-per-success 的分子不能只包含“成功请求的成本”还要包含失败请求以及后续重试、升级、人工处理的成本。只有在记录层面把失败请求也标记出来才能准确计算。推荐至少记录以下字段字段类型说明request_idstring请求唯一 IDmodel_namestring实际调用模型input_tokensint输入 token 数output_tokensint输出 token 数costfloat本次调用费用successbool是否成功retry_countint重试次数final_statusstring成功/失败/降级business_tagstring业务线标签5.4 路由决策需要一定量的历史数据如果系统刚上线数据很少不建议直接依赖 cost_per_success 做动态路由。前期可以先用静态规则路由积累足够样本后再逐步开启动态策略。起步阶段建议设置“探索流量”比例比如 5% 的请求随机分配模型用来补充统计样本。6. 常见问题与排查思路在实际开发和对接路由系统时容易遇到下面几类问题。问题现象常见原因解决思路cost_per_success 一直显示无穷大成功次数为 0检查成功判定条件确认模型返回是否被业务侧校验拦截路由始终选择同一个模型某模型成功成本被计算为 0检查成本记录是否漏记确认 token 统计是否准确重试后成本无限上涨重试逻辑没有最大次数限制设置最大重试次数超限后走兜底流程某个模型成功率骤降模型版本更新、业务提示词变化按版本记录 prompt 和模型参数对比异常区间成本统计与账单不一致漏掉缓存命中请求、漏计 hidden token统一从调用层返回的 usage 字段取数动态路由抖动样本量不足导致统计波动增加统计窗口、引入最小样本数量阈值排查工具建议优先做一张“路由决策日志表”把每次请求的输入特征、路由选择、模型返回、是否重试、最终结果完整记录这样发现问题时可以回溯。7. 最佳实践与工程建议7.1 对成本做分层治理不要只算单次模型调用成本。推荐把成本拆成四层模型调用层token 费用网关与中间件层代理转发、日志存储、缓存成本服务资源层部署 LLM 应用所消耗的 CPU/GPU/内存人工兜底层失败后由人力介入产生的隐性成本。cost-per-success 的“成本”应该尽量覆盖这些层至少要在设计时留下扩展位。7.2 让路由具备可观测性每次路由决定都要有日志输出并且能看到路由依据。简单做法是在返回模型名时打印或记录 scoredef route(self, request): # 省略业务判断 decision { model: selected_model, reason: cost_per_success_compare, light_cps: light_cps, high_cps: high_cps, } logger.info(routing decision: %s, decision) return selected_model没有可观测性的动态路由在线上出问题时很难排查。7.3 设置安全边界与降级策略路由层绝不能因为追求 cost-per-success 而绕过安全校验。以下情况必须保留强模型或人工审核涉及敏感数据的请求结果会直接影响用户重大利益的场景模型输出需要法律、医疗等专业审核的内容。同时要设计降级链最强模型不可用时可以降级到次强模型关键服务可以保留一个规则模板用于兜底避免完全不可用。7.4 不要频繁修改路由策略路由策略的调整会影响统计口径。如果频繁修改历史数据和新数据的可比性会变差。建议策略变更使用版本号管理数据统计按策略版本区分小流量验证通过后再全量。7.5 从成本指标到质量指标的联动cost-per-success 不是唯一指标它要与满意度、结果准确率、响应延迟联动观察。建议建立一套多指标看板业务成功率 成本成本成功率cost per success 平均延迟 模型分布这样既能控制成本也能看清路由对用户体验的影响。8. 如何从零搭建一套路由系统如果不想直接引入开源框架可以先从最小闭环开始。8.1 最小闭环包含的模块请求接入层统一接收业务请求包含任务类型、输入内容、上下文长度、业务标签路由决策模块根据规则或统计结果返回目标模型模型适配层封装不同模型的 SDK统一返回格式与 token 统计日志与统计模块记录每次调用的完整链路后台配置模块维护模型列表、价格、可用状态。8.2 落地的第一阶段先接两个模型一个强模型、一个轻量模型静态规则路由例如长文本和复杂推理走强模型把所有调用日志存起来计算不同模型在各任务类型下的 cost-per-success。第一阶段的目标不是“优化成本”而是“看清成本结构”。8.3 落地的第二阶段在历史数据基础上启用动态路由设置最小样本量阈值例如某个任务类型下某模型累计调用不超过 20 次时不参与动态选择逐步把 5% 流量切到动态路由观察 cost-per-success 是否相比静态路由下降。8.4 落地的第三阶段接入多模型、多策略增加兜底策略与重试策略建立分钟级的成本监控把路由调优与业务指标如用户满意度关联起来。9. 总结LLM 路由不是一个单纯的“请求分发器”它本质上是一个“成本与质量的调解器”。很多人关注 cost-per-call因为它直观、好统计但真正决定业务收益的是 cost-per-success。我们只有把失败、重试、兜底、人工修正都考虑进来才能衡量一个路由策略到底值不值。建议正在做 LLM 应用的同学在自己的调用链路上增加成本成功率的统计先跑两周看看不同任务、不同模型下的真实成本。你可能会发现之前以为“省钱”的轻量模型在某个任务上的成功成本反而更高。接下来可以继续往三个方向深入学习更多路由策略与开源 LLM 网关的架构设计调研 RAG 场景中检索质量与模型选择如何共同影响 cost-per-success了解 Agent 编排框架下多步调用之间的成本如何分摊到最终成功结果的成本里。路由这件事越早用数据驱动后期优化成本就越低。希望这篇文章能帮你把成本指标设计得更合理也让每一次模型调用都花得更值。