面对市面上从几十亿参数的小模型到上千亿参数的旗舰模型,一个自然的问题是:所有请求都该用最强模型吗? 答案是显而易见的否定——但更关键的问题是:如何系统化地决定“这个请求该用哪个模型”?模型路由(Model Routing)把这个问题从“人拍脑袋”变成“可编程的调度决策”:按任务复杂度、质量要求、成本预算、延迟约束动态选择模型,复杂任务上大模型,简单任务给小模型,不确定时级联兜底,失败时降级切换。本指南系统覆盖任务分级、大小模型路由、级联调度、失败降级与性价比选型矩阵,给出可落地的分层调度体系。
一、为什么要分层调度
1.1 单一模型的隐性浪费
用一个模型处理所有请求,会造成质量过剩与质量不足并存:
| 请求类型 | 用旗舰模型 | 用小模型 |
|---|---|---|
| 简单分类 | 质量过剩(浪费 10-30 倍成本) | 合适 |
| 日常问答 | 略浪费 | 基本够用 |
| 复杂推理 | 合适 | 质量不足(答错) |
| 代码生成 | 需要较强 | 通常不足 |
ℹ️ 核心洞察:不同请求对“模型能力”的需求差异极大。分层调度的目标是让每个请求恰好匹配满足它需求的“最便宜模型”,而不是一律给最强的。
1.2 路由决策的三个输入
路由决策需要同时考虑三个维度:
| 维度 | 输入 | 问题 |
|---|---|---|
| 任务复杂度 | 分类器 / 规则 / 用户画像 | 这个请求有多难? |
| 质量要求 | 业务 KPI、风险等级 | 答错成本有多高? |
| 成本约束 | 预算、调用量 | 这笔调用预算多少? |
1.3 分层调度收益量化
# routing_benefit.py — 分层调度的收益估算
def routing_benefit(requests, prices, router) -> dict:
"""对比"全用旗舰模型"与"路由分层"的总成本。"""
all_expensive = sum(
estimate_cost(r, "flagship", prices) for r in requests)
routed = sum(
estimate_cost(r, router(r), prices) for r in requests)
return {
"before": all_expensive,
"after": routed,
"saving_pct": (1 - routed / all_expensive) * 100,
}
二、任务分级:路由的前提
2.1 任务复杂度分级
路由的第一步是判断“这个任务属于哪个级别”。常用三级分级:
| 级别 | 特征 | 示例 | 适合模型 |
|---|---|---|---|
| 简单 | 结构清晰、答案确定 | 实体抽取、分类、格式转换 | 小模型 |
| 中等 | 需要一定理解与组织 | 摘要、日常问答 | 中模型 |
| 复杂 | 多步推理、长上下文、严谨性 | 代码、数学、法律分析 | 旗舰模型 |
2.2 分级方法谱系
| 方法 | 原理 | 成本 | 准确度 | 适用 |
|---|---|---|---|---|
| 规则分级 | 关键词 / 长度 / 功能白名单 | 零 | 中 | 任务特征稳定 |
| 小模型分类 | 用便宜模型判复杂度 | 低 | 中高 | 生产常用 |
| 置信度分级 | 小模型输出+置信度阈值 | 低 | 高 | 有小模型可用 |
| 延迟分级 | 小模型先试,不行再升 | 中 | 高 | 级联场景 |
# classify.py — 小模型复杂度分类器
def classify_complexity(query: str, small_model) -> str:
"""用便宜小模型判断复杂度,自带置信度。"""
resp = small_model.classify(
f"判断该任务复杂度(simple/medium/complex):{query}")
return resp["label"], resp.get("confidence", 0.5)
def route_by_confidence(query, small_model) -> str:
"""置信度高直接定级;置信度低保守升级。"""
label, conf = classify_complexity(query, small_model)
if conf >= 0.8:
return MODEL_MAP[label]
return MODEL_MAP["medium"] # 不确定时取中间值
2.3 特征驱动的静态路由
很多场景不依赖模型判断,用请求特征就能路由:
def static_route(query: str, feature: str, user_tier: str) -> str:
"""规则路由:按功能、用户等级、请求来源分流。"""
if feature == "entity_extraction":
return "gpt-4o-mini" # 简单功能固定小模型
if feature == "code_generation":
return "gpt-4o" # 复杂功能固定旗舰
if user_tier == "free":
return "gpt-4o-mini" # 免费用户用小模型
return "gpt-4o"
三、大小模型路由的工程实现
3.1 路由中心:统一抽象层
路由不是一个散落的 if-else,而是一个统一的模型网关:
# model_gateway.py — 统一模型网关
class ModelGateway:
def __init__(self):
self.routes = {
"small": SmallModelClient(),
"medium": MediumModelClient(),
"flagship": FlagshipModelClient(),
}
self.router = self._build_router()
def complete(self, messages, ctx: dict = None) -> dict:
ctx = ctx or {}
model = self.router.route(messages, ctx) # 动态选型
try:
resp = self.routes[model].complete(messages)
return {"model": model, "response": resp, "ok": True}
except ModelError:
return self._fallback(messages, model) # 降级
3.2 路由上下文的组装
路由决策需要看到足够上下文,但路由器本身要轻量:
def build_routing_context(messages, ctx: dict) -> dict:
"""组装路由器需要的轻量特征,避免把全部上下文喂给路由器。"""
last = messages[-1]
return {
"feature": ctx.get("feature"),
"user_tier": ctx.get("user_tier"),
"input_tokens": estimate_tokens(messages),
"has_tools": bool(ctx.get("tools")),
"query_text": last.get("content", "")[:500], # 截断
"requires_json": ctx.get("response_format") == "json",
}
3.3 路由映射表
模型映射用可配置的表,方便灰度调整:
ROUTING_TABLE = {
"simple": {
"default": "gpt-4o-mini",
"requires_json": "gpt-4o-mini",
"requires_tools": "gpt-4o", # 工具调用更强
},
"medium": {
"default": "gpt-4o-mini",
"requires_tools": "gpt-4o",
},
"complex": {
"default": "gpt-4o",
},
}
def apply_routing_table(label: str, ctx: dict) -> str:
"""按复杂度标签 + 上下文特征查表。"""
rules = ROUTING_TABLE[label]
if ctx.get("requires_tools"):
return rules.get("requires_tools", rules["default"])
if ctx.get("requires_json"):
return rules.get("requires_json", rules["default"])
return rules["default"]
四、级联调度:先便宜后兜底
4.1 级联的核心思想
不确定请求该用哪个模型时,先试便宜模型,质量不达标再升级——小模型生成后经质量判断(Judge 或规则),达标直接返回(省),不达标则升级大模型重新生成(保质量):
# cascade.py — 级联调用
async def cascade(query: str, judge_fn) -> tuple[str, str]:
"""先小模型,质量不足升大模型。"""
answer = await small_model.answer(query)
if judge_fn(query, answer)["total"] >= 4:
return answer, "small"
answer = await flagship_model.answer(query)
return answer, "flagship"
4.2 级联的触发条件
不是所有请求都值得级联。级联的触发要有质量信号:
| 触发方式 | 信号 | 成本 | 适用 |
|---|---|---|---|
| 规则触发 | 命中困难关键词、超长请求 | 零 | 明确复杂 |
| 置信度触发 | 小模型低置信度 | 低 | 有小模型可用 |
| Judge 触发 | 生成后 Judge 不达标 | 中(多一次判断) | 质量敏感 |
| 用户反馈触发 | 用户点“不满意”再重做 | 中 | 交互场景 |
4.3 级联的成本模型
级联不是“免费兜底”——它多花了小模型的调用与 Judge 的判断。要算清期望成本:
def cascade_expected_cost(rate_small_ok, cost_small, cost_judge, cost_large) -> float:
"""级联的期望成本 = 小模型通过的部分 + 升级的部分。"""
cost_when_small = cost_small + cost_judge
cost_when_large = cost_small + cost_judge + cost_large
return rate_small_ok * cost_when_small + (1 - rate_small_ok) * cost_when_large
def plain_cost(cost_large):
"""不级联:一律旗舰。"""
return cost_large
| 小模型达标率 | 级联期望成本 | 相比全旗舰 | 结论 |
|---|---|---|---|
| 90% | 0.9 × 小 + 0.1 × 大 | 省 40-60% | 值得 |
| 50% | 0.5 × 小 + 0.5 × 大 | 省约 20% | 边际 |
| 20% | 几乎全升级 | 基本不省 | 不适合级联 |
五、失败降级:路由的健壮性
5.1 降级场景
模型服务不可能永远可用。路由系统要定义失败时怎么办:
| 失败类型 | 场景 | 降级动作 |
|---|---|---|
| 大模型限流 | 配额耗尽 | 降级到中模型 |
| 大模型超时 | 慢 | 小模型快速响应 |
| 大模型不可用 | 服务故障 | 切换到备用厂商 |
| 质量异常 | 返回空/乱码 | 重试一次或换模型 |
5.2 降级链实现
# fallback.py — 降级链
FALLBACK_CHAIN = {
"flagship": ["medium", "small"], # 旗舰失败 → 中 → 小
"medium": ["small", "flagship"], # 中失败 → 小 → 旗舰
"small": ["medium", "flagship"],
}
async def complete_with_fallback(messages, preferred, chain=None):
"""按降级链逐级尝试。"""
chain = chain or FALLBACK_CHAIN.get(preferred, [])
for model in [preferred] + chain:
try:
resp = await clients[model].complete(messages)
return {"model": model, "response": resp}
except (TimeoutError, RateLimitError):
log_fallback(preferred, model) # 记录降级
continue
raise AllModelsUnavailable()
5.3 降级的质量与监控
降级不能无感知——降级意味着质量或成本可能变化,要记录并监控:
def track_degradation(entry: dict):
"""记录降级事件:触发原因、降级路径、影响。"""
push_metric("model_gateway.fallback", 1,
{"from": entry["preferred"], "to": entry["used"],
"reason": entry["reason"]})
if entry["preferred"] != entry["used"]:
push_metric("model_gateway.degradation_rate",
entry["used"] != entry["preferred"])
| 指标 | 含义 | 告警 |
|---|---|---|
| 降级率 | 发生降级的请求占比 | > 1% 告警 |
| 降级方向 | 升还是降 | 频繁升级=路由不准 |
| 降级后质量 | 采样评估降级输出 | 显著下降=链路不稳 |
六、性价比选型矩阵
6.1 选型的多维度评估
给一个业务选模型,不是“谁强选谁”,而是画性价比矩阵:
| 模型 | 能力 | 价格 | 延迟 | 上下文 | 适用 |
|---|---|---|---|---|---|
| 小模型 A | 基础问答 | 极低 | 快 | 中 | 分类、抽取 |
| 中模型 B | 均衡 | 低 | 中 | 中 | 日常对话、摘要 |
| 旗舰 C | 强推理 | 高 | 中 | 大 | 代码、复杂推理 |
| 开源 D | 可定制 | 硬件成本 | 中 | 中 | 自托管、私有 |
6.2 成本-质量-延迟三维评分
# selection_matrix.py — 模型性价比评分
def model_score(model, weights: dict) -> float:
"""按业务权重给模型打分:质量×w1 + 成本×w2 + 延迟×w3。"""
return (
weights["quality"] * model["quality"] / 10
- weights["cost"] * model["cost_per_k"]
- weights["latency"] * model["p95_ms"] / 1000
)
def select_best_model(candidates, weights):
"""按加权得分选最优模型。"""
return max(candidates, key=lambda m: model_score(m, weights))
选型不是一次定终身,要用A/B 对比在真实流量上验证:同一批查询分别跑两个候选模型,对比质量(Judge 平均分)与成本(总 token 费用),再决定是否切换。
一句话:选型矩阵的价值不是“找到最好的模型”,而是“在给定的业务权重下找到最合适的模型”——质量、成本、延迟三个坐标,业务不同权重不同。
七、路由系统的可观测性与自学习
7.1 路由质量的指标
路由系统本身要回答“路由对吗”:
| 指标 | 含义 | 健康值 |
|---|---|---|
| 路由准确率 | 定级是否与人工一致 | > 90% |
| 成本节省率 | 相比全旗舰省多少 | 30-70% |
| 质量保持率 | 路由后质量是否持平 | 下降 < 3% |
| 误路由率 | 简单题送旗舰 / 难题送小模型 | < 5% |
| 升级率 | 级联中触发升级的占比 | 10-30% |
def route_accuracy(golden, router, labels) -> float:
"""路由定级与人工标注的准确率。"""
correct = sum(router(g["query"]) == labels[g["id"]]
for g in golden.cases)
return correct / len(golden.cases)
7.2 路由策略的自学习
路由表可以随数据自动调整。简单可行的是分级阈值调优:
def tune_threshold(cases, router, judge_fn, search_space=(0.5, 0.95)):
"""网格搜索最优置信度阈值:权衡成本与质量。"""
best = None
for threshold in [round(x, 2) for x in frange(*search_space)]:
cost, quality = simulate(router, threshold, cases, judge_fn)
score = quality - 0.5 * cost # 业务自定义权衡
if best is None or score > best["score"]:
best = {"threshold": threshold, "score": score,
"cost": cost, "quality": quality}
return best
7.3 路由实验的灰度发布
路由变更(映射表、阈值、级联策略)要有灰度机制:
def canary_route(query, canary_pct=0.05):
"""按百分比放流量到新路由策略,对比新旧质量。"""
if hash(query) % 100 < canary_pct * 100:
return new_router.route(query), "canary"
return old_router.route(query), "control"
八、实战:一个客服系统的分层调度
8.1 场景设计
客服 Agent 请求构成(每日 10 万条):
45% 简单(FAQ、订单状态、意图识别)
35% 中等(操作指引、多轮对话)
20% 复杂(纠纷处理、政策解释、工具调用)
目标:成本降 50% 以上,质量不降
8.2 分层调度实现
# support_router.py — 客服分层调度
class SupportRouter:
def __init__(self, small, medium, flagship):
self.small = small
self.medium = medium
self.flagship = flagship
def route(self, query: str, ctx: dict) -> str:
feature = ctx.get("feature")
if feature in {"intent", "slot_fill", "order_status"}:
return "small" # 简单功能
if feature in {"guide", "multi_turn"}:
return "medium"
if feature in {"dispute", "tool_call"}:
return "flagship" # 复杂走旗舰
# 默认:用分类器动态分级 + 级联兜底
label = classify_complexity(query, self.small)[0]
return {"simple": "small", "medium": "medium",
"complex": "flagship"}[label]
8.3 上线验证清单
- 路由准确率:抽样 200 条人工标注对比 ≥ 90%
- 质量:路由前后评测集分数下降 ≤ 3%
- 成本:周账单对比,节省率 ≥ 40%
- 降级率:< 1%,且降级后质量采样正常
- 延迟:P95 不超预算(路由本身开销 < 50ms)
总结:分层调度的四步体系
| 步骤 | 职责 | 关键手段 |
|---|---|---|
| 任务分级 | 判断请求难度 | 规则 / 小模型分类 / 置信度 |
| 路由决策 | 选定模型 | 映射表 / 动态路由 |
| 级联兜底 | 质量保障 | 先便宜后升级 |
| 失败降级 | 健壮性 | 降级链 / 备用厂商 |
模型路由与选型的本质,是把“用哪个模型”从一次性的拍脑袋,变成持续的、可度量、可自学习的调度决策。它用任务分级识别需求的差异,用映射表把级别翻译成具体模型,用级联在不确定时兜底质量,用降级链守住可用性,最后用可观测性回答“路由对吗、省了多少、质量降没降”。当模型生态越来越丰富,这套分层调度的能力,就是你在“最强的模型”与“最便宜够用的模型”之间持续寻找最优解的工程杠杆。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。