模型路由器:架构设计让LLM调用成本降低94%的实战方案 AI Model Router如何用架构设计把 LLM 成本降低 94%做 AI 应用的朋友大概率都有过这样的经历月初拿到账单发现光是调用大模型的费用就烧掉了几千甚至几万块钱。真正让人头疼的是这些花费里有多少是必要的又有多少是因为架构设计不合理而浪费的——你很难一下子说清楚。很多团队在优化 LLM 成本时第一时间想到的是换更便宜的模型或者砍掉不必要的调用。但这两招都有明显的副作用模型一换回答质量下降调用一砍产品体验缩水。有没有一种更优雅的方案既能保留大模型的强能力又能让大多数请求不必付出最贵的价格答案是模型路由器Model Router。这篇文章我会从实际架构出发拆解一个 AI 模型路由器的核心设计思路、实现要点和验证方法并回答一个大家最关心的问题94% 的成本节省是怎么来的这个数字在什么前提下才有可能成立。一个问题我们为什么会多付这么多钱先把话说明白大多数 AI 应用的调用成本不是被“复杂任务”吃掉的而是被“简单任务”用贵模型处理时浪费掉的。想象一个典型的智能客服系统。用户发来的问题五花八门但大部分其实并不需要顶级大模型的深度推理能力。比如“你们公司的退货政策是什么”这类问题本质上是一次知识库检索加一次轻量总结而“我的订单在运输途中丢失了我该找谁负责赔偿流程怎么走”这类问题才需要模型做多步推理和情绪感知。在传统架构下这两类请求走的是同一条链路用的通常是同一个模型。为了保证复杂问题的效果团队不敢把模型级别降下来于是简单请求只能跟着“享受”高价模型。如果一个产品里 80% 的请求都是简单请求那么 80% 的成本都在支付“能力冗余”的费用。这不是模型定价的问题而是架构设计的问题。模型路由器的思路其实很朴素先判断请求的复杂度再根据复杂度分配不同的模型。简单请求走便宜的小模型复杂请求才调用能力更强的大模型。它相当于在应用层和模型之间加了一道智能分发层。在理想状态下如果 90% 的简单请求都分流到成本仅有大模型十分之一的轻量模型上总成本的下降幅度就会非常可观。标题里提到的 94%本质上就是这个比例在某个具体任务分布下的结果。模型路由器到底是什么模型路由器不是一个开箱即用的第三方产品而是一种架构模式。它通常由四个模块组成请求解析、路由决策、调用执行、成本与质量追踪。请求解析负责从用户的输入中提取关键信息包括任务类型、上下文长度、是否涉及多轮对话、是否需要对结果做严格的格式校验。这些信息是路由决策的输入。路由决策是核心。它根据解析结果结合你配置的策略决定这次请求应该交给哪个模型。决策的维度一般包括任务类型摘要、分类、翻译、代码生成、JSON 抽取、复杂推理不同类型对应不同模型。上下文长度长上下文请求对模型的窗口和计费方式有较大影响。有些模型的价格是按 token 区间阶梯计算的长度不同成本差异显著。质量要求用户面向的是 C 端产品还是内部工具是否允许偶尔的低质量回答延迟预算实时聊天场景对延迟敏感适合快速响应的小模型离线批处理场景可以为了质量接受更慢的大模型。调用执行模块负责统一封装各家模型的 API统一请求格式、鉴权方式和错误重试逻辑。这样上层业务不需要关心具体调的是哪家模型只需要面对同一个路由接口。成本与质量追踪模块则负责记录每一次调用的模型、token 数、费用、响应时间和质量打分。没有这个模块你根本无法回答“路由策略到底省了多少钱、质量有没有下降”这两个问题。其实很多团队在做成本优化时把注意力集中在“选哪个模型”上却忽略了更关键的一件事如何系统性地做这个选择。选一次是模型评测每次请求都自动选才是模型路由。为什么说模型路由器是成本优化的必选项我们可以把模型路由器放到一个更广的视角里看。在过去一年多里市面上开源模型和商业模型层出不穷从几十亿参数的小模型到千亿参数的旗舰模型价格差距可以高达几十倍。与此同时同样参数量级的模型在不同任务上的表现也大不相同。这就带来一个新的问题任何单一模型都无法在所有维度上同时做到“效果最好”和“成本最低”。你选择旗舰模型就是默认接受它的价格你统一使用轻量模型就要接受它在复杂任务上的能力退化。模型路由器让你不必做这种单选题而是可以根据任务特征动态配置。它对两类团队的价值尤其明显。第一类是已经有 LLM 应用在线上运行的团队。这类团队现在的痛点不是“没有模型可用”而是成本随着调用量线性增长毛利被不断蚕食。引入模型路由器可以在不改动业务逻辑的前提下直接替换掉底层调用层就能看到账单变化。第二类是正在设计 Agent 或多步推理系统的团队。Agent 场景的特点是请求量不稳定、子任务类型多样。一个 Agent 可能在一个主任务中调用模型好几次其中有些子任务很简单比如提取日期、格式化输出有些子任务很难比如规划步骤、推理因果。如果全部让大模型做一次 Agent 任务可能产生极高的成本用模型路由器做子任务级的分流效果会非常明显。当然模型路由器也不是万能的。如果你所有的请求都是复杂推理请求路由器的优化空间就很小。如果你的请求量很小一个月只有几千次调用省下来的钱可能还覆盖不了开发维护路由器的成本。这一点需要先想清楚。整体架构设计下面我们来看一个可落地的模型路由器应该怎么设计。这里我以最常见的 OpenAI 兼容 API 生态为例因为目前大多数开源模型和商业模型都提供了这种接口形式。整体架构分为四层层级模块职责接入层统一 API 网关接收业务请求做身份认证、限流和基本参数校验决策层路由器核心解析请求、计算任务复杂度、执行路由策略执行层模型客户端封装各家模型 API统一流式与非流式调用观测层日志/指标/告警记录每次调用的模型、token、费用、延迟与质量在接入层我们通常暴露一个和 OpenAI Chat Completions 兼容的接口这样上层已有的调用代码基本不需要改动只需要把 base_url 指向路由器。在决策层最核心的数据结构是路由规则。一个简单的路由规则可以表述为当满足某些条件时使用某个模型。条件可以是关键词匹配、token 长度阈值、意图分类结果也可以是多个条件的组合。在实现上我建议把路由规则做成配置化的而不是硬编码在代码里。因为模型价格和质量会变化你的任务分布也会变化配置化可以让你在模型价格调整后快速切换策略而不用重新发布服务。在观测层除了记录调用日志还需要有一个可量化的质量反馈渠道。你可以通过用户点赞点踩数据、Agent 任务完成率、回答格式通过率等间接指标来判断路由后的质量是否稳定。没有质量反馈的路由器是盲目优化的这是很多实现容易忽略的地方。环境准备与前置条件我们现在来实现一个最小可用的模型路由器。为了方便演示我会使用 Python但核心思路适用于任何语言。需要的环境Python 3.10一个可以使用 OpenAI 兼容接口的模型服务地址比如本地的 vLLM、Ollama或者云厂商的 APIopenaiPython 包pyyaml或toml用于读取配置安装依赖pip install openai pyyaml为了让示例能够直接运行我会用两种模型来演示一个能力强价格高的模型gpt-4o一个能力适中价格低的模型gpt-4o-mini。如果使用国内模型或开源模型替换模型名和 base_url 即可。在正式开始写代码之前先明确几个设计目标上层调用方不需要感知路由逻辑接口保持统一。路由策略通过配置驱动修改策略不需要改代码。每次调用都记录成本和质量原始信息方便后续做统计分析。核心代码实现5.1 路由策略配置首先定义一个 YAML 配置文件用来描述模型列表和路由规则。# 文件路径config.yaml models: - name: gpt-4o cost_per_1k_prompt: 0.005 # 每 1k prompt token 价格美元 cost_per_1k_completion: 0.015 max_tokens: 8192 capabilities: [reasoning, tool_calling, json_mode] - name: gpt-4o-mini cost_per_1k_prompt: 0.00015 cost_per_1k_completion: 0.0006 max_tokens: 16384 capabilities: [tool_calling, json_mode] routes: # 默认路由没有命中任何规则时使用 - name: default model: gpt-4o-mini # 规则命中顺序从上到下第一个命中的规则生效 - name: complex-reasoning model: gpt-4o conditions: task_type: reasoning min_tokens: 1000 - name: json-extraction model: gpt-4o-mini conditions: task_type: extraction - name: long-context model: gpt-4o conditions: max_context_tokens: 6000这里有一个设计点routes的顺序就是规则匹配的顺序第一条命中的规则生效。所以default路由要放在最前面或者放到最后面以免拦截所有请求。在这个配置中我把 default 放在最前面但它的特殊之处在于不设置conditions作为兜底。实际项目中可以根据需要调整顺序。5.2 模型客户端封装接下来是模型客户端的封装核心是屏蔽各家 API 的差异统一模型调用接口。# 文件路径model_client.py import json import time from openai import OpenAI from dataclasses import dataclass, field from typing import Optional dataclass class ModelCallResult: model: str content: str prompt_tokens: int completion_tokens: int total_tokens: int latency_ms: int cost: float 0.0 raw_response: Optional[dict] None class ModelClient: 统一模型客户端封装不同模型的调用。 def __init__(self, model_config: dict, api_key: str, base_url: str None): self.name model_config[name] self.cost_per_1k_prompt model_config.get(cost_per_1k_prompt, 0) self.cost_per_1k_completion model_config.get(cost_per_1k_completion, 0) self.max_tokens model_config.get(max_tokens, 4096) self.client OpenAI(api_keyapi_key, base_urlbase_url) def chat(self, messages: list, temperature: float 0.7, max_tokens: Optional[int] None) - ModelCallResult: 执行一次对话补全兼容非流式场景。 if max_tokens is None: max_tokens self.max_tokens start time.time() response self.client.chat.completions.create( modelself.name, messagesmessages, temperaturetemperature, max_tokensmax_tokens, ) latency_ms int((time.time() - start) * 1000) content response.choices[0].message.content or usage response.usage result ModelCallResult( modelself.name, contentcontent, prompt_tokensusage.prompt_tokens, completion_tokensusage.completion_tokens, total_tokensusage.total_tokens, latency_mslatency_ms, raw_responseresponse.model_dump(), ) result.cost self._calculate_cost(usage.prompt_tokens, usage.completion_tokens) return result def chat_stream(self, messages: list, temperature: float 0.7): 执行一次流式对话补全后续可以按需扩展。 stream self.client.chat.completions.create( modelself.name, messagesmessages, temperaturetemperature, max_tokensself.max_tokens, streamTrue, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: yield chunk.choices[0].delta.content def _calculate_cost(self, prompt_tokens: int, completion_tokens: int) - float: 按 token 用量和单价计算成本。 prompt_cost prompt_tokens / 1000 * self.cost_per_1k_prompt completion_cost completion_tokens / 1000 * self.cost_per_1k_completion return round(prompt_cost completion_cost, 6) class ModelClientFactory: 根据配置创建模型客户端实例。 staticmethod def from_config(config: dict, api_key: str, base_url: str None) - dict[str, ModelClient]: clients {} for model_config in config[models]: clients[model_config[name]] ModelClient(model_config, api_key, base_url) return clients这个封装的核心价值在于所有模型调用都返回统一的ModelCallResult其中包含了成本字段。上层业务不需要关心具体模型价格和 token 计算逻辑。5.3 路由器核心现在写路由器核心。这一层要做的事解析请求、匹配规则、调用模型、记录日志。# 文件路径router.py import json import yaml from typing import Optional from model_client import ModelClientFactory class ModelRouter: 基于规则配置的模型路由器。 def __init__(self, config_path: str, api_key: str, base_url: str None): with open(config_path, r, encodingutf-8) as f: self.config yaml.safe_load(f) self.clients ModelClientFactory.from_config(self.config, api_key, base_url) self.routes self.config[routes] self._init_rules() def _init_rules(self): 预处理路由规则把字符串条件转成可执行的结构。 available_models set(self.clients.keys()) for route in self.routes: if route[model] not in available_models: raise ValueError(f路由 {route[name]} 指定的模型 {route[model]} 不在模型列表中) def route(self, messages: list, temperature: float 0.7) - dict: 对外暴露的路由入口返回包含模型名、内容、成本的结果。 features self._extract_features(messages) model_name self._match_route(features) client self.clients[model_name] result client.chat(messages, temperaturetemperature) return { route_name: model_name, content: result.content, prompt_tokens: result.prompt_tokens, completion_tokens: result.completion_tokens, total_tokens: result.total_tokens, cost: result.cost, latency_ms: result.latency_ms, features: features, } def _extract_features(self, messages: list) - dict: 从消息中提取路由决策需要的特征。 这里做了一个简化的特征提取累加所有消息的文本长度并尝试判断任务类型。 实际项目中可以用小模型做意图分类或者接入更复杂的特征服务。 full_text .join([m.get(content, ) for m in messages if isinstance(m, dict)]) total_chars len(full_text) # 粗略换算一个中文字符约等于 1.5 个 token英文单词约等于 1.3 个 token estimated_tokens int(total_chars * 1.5) task_type general # 简单规则包含 JSON 关键词的任务判定为 extraction if any(kw in full_text.lower() for kw in [json, 抽取, extract, 格式化]): task_type extraction # 复杂推理规则包含推理、计划、分析、为什么等关键词 elif any(kw in full_text for kw in [推理, 规划, 分析, 为什么, reasoning, plan]): task_type reasoning return { estimated_tokens: estimated_tokens, task_type: task_type, message_count: len(messages), } def _match_route(self, features: dict) - str: 按顺序匹配路由规则返回第一个命中的模型名。 for route in self.routes: conditions route.get(conditions, {}) if not conditions: # 没有条件的路由作为默认路由永远可以命中 return route[model] if self._match_conditions(conditions, features): return route[model] # 理论上不会走到这里因为配置里总有 default 路由 return self.routes[-1][model] def _match_conditions(self, conditions: dict, features: dict) - bool: 判断单个规则的所有条件是否全部满足。 if task_type in conditions and features[task_type] ! conditions[task_type]: return False if min_tokens in conditions and features[estimated_tokens] conditions[min_tokens]: return False if max_context_tokens in conditions and features[estimated_tokens] conditions[max_context_tokens]: return False return True这段代码的核心逻辑在_match_route。它把“路由决策”从业务代码中剥离出来业务侧只需要传入 messages路由器自动完成模型选择。注意这里的特征提取故意做得很简单目的是先把流程跑通。在生产环境中应该用更可靠的方式判断任务复杂度比如独立的分类模型、embedding 相似度匹配、或者基于历史日志归因出来的规则。5.4 完整调用示例现在我们把以上代码串起来跑一个最小示例。# 文件路径demo.py from router import ModelRouter def main(): router ModelRouter( config_pathconfig.yaml, api_keyyour-api-key, base_urlhttps://api.openai.com/v1, # 替换为你的模型服务地址 ) # 模拟一个简单请求JSON 抽取 simple_messages [ {role: user, content: 从下面文本中抽取所有日期和金额并用 JSON 输出\n订单 A 在 3月1日 付款 200 元订单 B 在 3月5日 付款 350 元。} ] # 模拟一个复杂请求逻辑推理 complex_messages [ {role: user, content: 有五个人参加比赛A 不是第一B 比 C 快D 比 A 慢E 比 D 快但不是第一。请问谁是第一请逐步推理。} ] for label, messages in [(simple, simple_messages), (complex, complex_messages)]: result router.route(messages) print(f[{label}] 路由模型: {result[route_name]}) print(f[{label}] 成本: ${result[cost]:.6f}) print(f[{label}] token 数: {result[total_tokens]}) print(f[{label}] 延迟: {result[latency_ms]}ms) print(---) if __name__ __main__: main()运行后预期输出大致如下[simple] 路由模型: gpt-4o-mini [simple] 成本: $0.000123 [simple] token 数: 220 [simple] 延迟: 380ms --- [complex] 路由模型: gpt-4o [complex] 成本: $0.006810 [complex] token 数: 480 [complex] 延迟: 1200ms ---从输出可以看出简单请求被分到 gpt-4o-mini复杂请求被分到 gpt-4o。成本差距非常明显。运行验证与效果评估跑通代码只是第一步。模型路由器上线后最重要的问题是你的成本到底降了多少质量有没有变化。成本节省的计算公式不复杂。假设优化前的基线是全部请求都使用旗舰模型那么优化后的节省率就是节省率 (基线成本 - 路由后成本) / 基线成本 × 100%但要注意如果只统计总成本不区分任务类型和路由命中率你是看不出哪里还有优化空间的。所以我建议至少从两个维度看数据。第一个维度是路由分布。统计每种路由命中了几次、占总体调用量的比例是多少。在理想状态下default 和低成本路由应该承接大部分请求。如果 high-cost 模型的命中率超过 30%说明你的路由规则可能太保守或者你的任务本身就普遍比较复杂。第二个维度是每类任务的成本变化。把订单按任务类型分组对比路由前后的平均单次调用成本。这能帮你判断每类任务的成本节约是否合理。关于 94% 这个数字这里要做一个诚实的解释。文章标题里的 94% 来自特定任务分布下的结果。当且仅当满足以下条件时才有可能接近这个量级请求分布高度倾斜绝大多数请求的复杂度很低。低成本模型足以满足简单请求的质量要求。路由准确率高不会把复杂请求误判为简单请求。换句话说如果换一个业务场景比如你的产品本身就是帮用户做深度代码分析和重构那么 94% 是基本不可能的。所以我更建议把模型路由器当作一个“成本优化框架”来理解它给你的是一个可持续优化的机制而不是一个固定的百分比承诺。常见问题与排查方法在实现和上线模型路由器的过程中有几类问题是高频出现的这里整理成一个排查表。问题现象可能原因排查方式解决方案所有请求都走同一个模型路由规则条件设置有误default 提前拦截打印每条请求的 features 和命中的规则名检查规则顺序确认 default 路由在配置中只做兜底简单请求偶尔返回低质量结果低成本模型能力不足或 prompt 需要适配收集失败样本对比旗舰模型和低成本模型的输出为特定任务单独设计 prompt 模板必要时对低成本模型做微调路由延迟增加特征提取逻辑过重或规则匹配复杂观测特征提取耗时和路由决策耗时优化特征提取使用缓存或异步计算成本统计不准不同模型的计费规则不同或者只统计了 prompt token核对各模型的单价和计费文档独立实现每个模型的成本计算逻辑保留原始 usage 数据模型 API 限流导致失败率升高低成本模型被过度集中调用查看限流错误码和客户端重试日志在模型客户端增加限流退避和熔断机制新增模型后路由报错配置中模型名写错或 API base_url 不兼容检查配置文件和模型客户端初始化日志先单独调用新模型验证连通性再接入路由器一个值得提醒的点路由器的失败不应该成为业务不可用的原因。如果低成本模型调用失败应该允许自动降级到高成本模型重试而不是直接返回错误。这需要在执行层增加重试和降级逻辑。最佳实践与工程建议最后聊一些工程落地的建议。这些经验来自实际项目中反复踩坑后的总结比具体的代码更重要。第一把路由决策和业务解耦。不要在业务代码里写死“这个请求要用 gpt-4o”而是把决策权交给路由器业务只描述自己需要什么能力。比如“我需要提取 JSON”路由器负责决定用哪个模型来完成提取。第二路由策略要配置化并且可以热更新。模型价格变化频繁你的任务分布也会随着产品迭代而变化。如果每次调价都要重新发布服务成本太高。建议把配置文件放到配置中心支持自动刷新。第三质量反馈是必须的。单纯降成本而不看质量最终一定会出问题。你需要建立一套质量评估机制。对于离线任务可以用评估集定期回归对于在线任务可以用用户行为指标间接判断比如对话是否提前结束、回答是否被举报、任务是否完成等。质量指标要和成本指标放在同一个看板里两边一起看。第四注意安全边界和密钥管理。路由器会汇聚所有模型的 API 密钥这意味着它的安全等级应该比普通应用服务更高。建议把密钥放在密钥管理服务里运行时动态读取而不是写死在配置文件中。同时对外开放的路由接口需要做身份认证和权限控制防止被刷。第五从小流量灰度开始。不要一上来就把所有流量切到路由器。可以先从 10% 的流量开始观察成本和质量指标确认稳定后再逐步放大。如果质量出现回退可以通过配置回滚到旧的调用逻辑。第六把成本统计当成一等公民。每一次模型调用的 cost、tokens、model、route 字段都要写进日志系统。后续做成本分析、异常报警、趋势预测时这些数据就是基础。没有这些数据优化就无从谈起。模型路由只是第一步模型路由器解决的是“每次请求选什么模型”的问题但它不是 LLM 成本优化的终点。更进一步你还可以做请求缓存、语义去重、结果复用、Agent 调用链路的 token 压缩等优化。不过如果你现在的应用还在“所有请求都用同一个大模型”的阶段那么模型路由器是性价比最高、改动最小、见效最快的一个优化手段。它不要求你牺牲产品质量也不会改变用户的交互体验只是改变了请求背后的“资源分配逻辑”。这篇文中给出的代码是一个最小可运行的演示版本直接拿去做生产部署肯定不够但它已经把路由决策、模型封装、成本采集这几个关键环节都覆盖到了。你可以在这个框架之上替换掉简单的规则匹配、完善监控告警、补充质量评估把它扩展成适合自己团队的架构。后续值得深入的方向包括用强化学习或上下文 bandit 算法做模型选择、基于历史请求日志自动生成路由规则、把路由器接入到现有 Agent 框架的每个工具调用节点。这些方向共同指向一个目标让每一分模型预算都花在刀刃上。