☰
Agent模型选型先定路由规则:把智能路由下沉到Runtime
2026/10/13 8:30:38 网站建设 项目流程

1. 别急着选 Agent 模型,先想清楚你家路由规则长什么样

做 AI Agent 项目的人,十有八九都会在同一个地方卡住:模型选型。

打开模型榜单一看,各家分数咬得很紧,今天 A 家领先 0.5 分,明天 B 家又在某个榜单上反超。于是团队里开始争论,有人想上闭源大模型求稳,有人想用开源模型省钱,还有人觉得干脆兼容十几个厂商,消费者想用哪个用哪个。

我见过不少项目,就是在这样反复摇摆里耗掉了大半个月,最后做出来的 Agent 连一次像样的任务调度都跑不通。

问题出在哪?很多人把模型选型当成一个纯模型问题,但实际上,对大部分业务场景来说,模型能力的差距,远没有你想的那么大。真正决定 Agent 能不能稳定、可靠、可维护的,是模型背后的路由规则,也就是决定“哪个请求应该交给哪个模型去处理”的那套逻辑。而且这套逻辑不该散落在业务代码里,它应该被写进 Runtime,成为 Agent 基础设施的一部分。

这篇文章想聊的,就是这件事:为什么路由规则是 Agent 选型的前置条件,以及如何把路由规则真正落到 Runtime 里。我会结合自己做过的几个实际项目,把思路、代码、踩坑记录都放出来,给正在做 Agent 基础设施选型的朋友一个参考。

2. 为什么选模型之前,先得解决路由问题

2.1 模型选型的本质困境:单一模型扛不住所有场景

先讲一个很现实的困境。你在做一个企业知识库问答 Agent,需求清单大概长这样:

  • 员工问“报销流程是什么”,需要快速、准确的检索回答,成本要低,响应要快。
  • 法务同事上传一份几十页的合同,需要理解复杂条款,做风险分析,这个场景上下文非常长,逻辑要求高。
  • 客服那边收到用户投诉,需要判断用户情绪,决定是安抚还是升级人工处理。

这三个场景的需求是完全不同的。第一个场景,要求低延迟、低成本,模型不用太聪明,能把检索到的内容组织成通顺回答就行。第二个场景,要求长上下文、强推理,哪怕贵一点慢一点也没关系。第三个场景,要求指令跟随能力强、输出格式稳定,最好能直接从回复里抽结构化标签。

如果你试图用一个模型扛下所有场景,结局通常只有两种:要么用强模型处理简单任务,钱烧得飞快,延迟还高;要么用弱模型处理复杂任务,结果质量惨不忍睹,Agent 频繁出错来回重试。

这两种结局我都见过,而且都挺惨烈的。

2.2 路由规则的本质:把请求分成该花的钱和不该花的钱

路由规则解决的就是这个问题。它的本质,是根据请求特征,把不同复杂度、不同要求的任务,分发给不同能力层级的模型处理。用便宜模型解决大部分简单问题,把预算集中在少量真正需要强模型的任务上。

听起来很简单对吧?但实际做起来,会遇到两个麻烦。

第一个麻烦是,路由规则藏在业务代码里。业务同学在写某个功能时,顺手写了个 if-then,判断关键词,决定走哪个模型。刚开始还挺好用,但随着业务迭代,规则越堆越多,散落在各个模块里,没人说得清现在到底有哪些路由规则,它们之间什么关系,哪个规则优先级更高。最后这个项目就变成了一个谁也改不动的屎山。

第二个麻烦是,路由规则没有统一的观测入口。你不知道每个规则命中了多少请求,每个模型的实际负载是多少,成本超了到底是因为哪条规则,这些全是一笔糊涂账。

所以我的观点很明确:路由规则必须从业务代码里抽出来,下沉到 Runtime 层。这个话怎么理解呢?你看,业务代码每天都在变,科学家在调 prompt,后端在改接口,但 Runtime 是相对稳定的,它像操作系统一样,负责管理资源、分配任务。路由规则放进 Runtime,意味着所有请求在进入业务逻辑之前,就已经被一个统一的调度层处理了。业务代码只管自己的逻辑,模型怎么选、选哪个,那是 Runtime 的事。

2.3 Runtime 路由和网关路由的边界

很多人这里会问:我把路由规则放进 Runtime,跟现在市面上那些 AI 网关(Gateway)有什么区别?网关不也能做模型路由吗?

这里要区分一下。网关做的是模型路由的最后一跳,它解决的是:已经决定要用模型 A 了,那么具体是调用模型 A 的哪个供应商、哪个版本、哪个 region,如果挂了怎么办,要不要重试。这层路由是偏运维的,它的价值在于稳定性和容灾。

而 Runtime 层的路由,解决的是更前置的问题:这个请求该用什么模型,该走什么策略,该分配多少预算。它更接近业务语义,需要感知任务类型、用户画像、内容主题、上下文长度这些信息。网关是低层的“怎么调”,Runtime 是上层的“调哪个”。

简单说,Runtime 路由是大脑,AI 网关是手脚。做 Agent 基础设施,两层都要有,但很多团队只做了网关层,把大脑的逻辑随意丢在业务代码里,这是本末倒置的。

3. 路由规则到底怎么写:三个核心维度和一套配置模型

3.1 第一维度:按任务类型路由

任务类型是最直观的路由维度。你需要先梳理业务里到底有哪些类型的任务,然后为每种任务类型定义默认的模型策略。

我习惯的做法,是在项目启动时就拉一个任务清单,表头长这样:

任务类型能力要求典型模型级别延迟要求成本敏感度
意图识别低,只需分类L1(轻量模型)极快高
信息抽取中,需结构化输出L2快中
摘要生成中高,需长文本理解L2 / L3中中
推理分析高,需多步推理L3(旗舰模型)可容忍低
代码生成高,需准确语法L3中低
创意文案中,需风格控制L2中中

这个表的价值不在于它本身有多完备,而在于它逼着团队在写第一行业务代码之前,先想清楚自己到底有哪些任务。等这个表梳理完了,路由规则的骨架其实已经出来了。

实际项目里,任务类型通常不是直接写死的,而是从一个请求解析出来的。请求进来之后,Runtime 先看它的语义特征,比如意图识别的结果、内容类型标签、目标接口等,然后映射到任务类型上。这段映射逻辑会用一个单独的模块维护,后续新增任务类型时,只需要扩展这个表,不用动业务代码。

3.2 第二维度:按代价预算路由

成本因素很多人会忽略,但等你账单出来的时候,眼泪都是咸的。

我的建议是把成本写进路由规则里,用预算控制模型选择。具体做法是这样:为每个请求或每个用户会话定义一个最大可接受成本,Runtime 在路由时,优先选用满足质量和延迟要求的最便宜模型。

举个例子。假设你的业务里存在这样的配置:普通用户请求的模型成本预算是 0.001 美元/次,VIP 用户是 0.01 美元/次。那么同样一个摘要任务,普通用户可能被路由到 L2 模型,VIP 用户因为预算更充足,可以路由到 L3 模型拿更好的效果。

这里还有一个很关键的点:成本预算不应该是静态的。我实际上会在 Runtime 里做动态调整,比如某个用户某次请求内容特别复杂,系统会自动申请追加预算,从备用池里扣,而不是死板地拒绝升级模型。这个机制有点像云厂商的按量计费和包年包月并存,灵活度要够。

3.3 第三维度:按质量与延迟动态路由

任务类型和成本预算解决的是“静态该选谁”,但实际线上环境是动态的。同样的模型,今天响应快,明天可能因为负载高就变慢;同样的任务,用户在北京和在纽约,感知到的延迟完全不同。

所以路由规则里要做动态维度,主要看两个信号:当前模型池的健康状态和请求的实时延迟容忍度。

这个动态决策逻辑我建议放在 Runtime 里用独立模块实现,它会周期性检测各模型供应商的响应时间、错误率、队列长度,然后更新每个模型的路由权重。比如某个模型供应商当前错误率超过 5%,Runtime 会自动降低它在新请求中的分配比例,直到它恢复健康。

这里需要说明的是,动态路由并非要做得像自适应机器学习那样复杂——不需要一个在线训练的模型来决策。用简单的熔断、加权、健康检查机制组合,就已经能解决绝大多数问题了。过度设计是这类系统最常见的坑。

3.4 把这些规则变成一份可读的配置

规则理清楚了,下一步是让它们可配置、可维护。我强烈建议不要把这些规则硬编码在代码里,而是用一个配置文件或配置中心统一管理。

这是我实际项目里用过的配置结构,你可以参考:

route_rules: default_policy: task_types: - name: "intent_classify" model_tier: "L1" max_cost: 0.0003 timeout_ms: 800 - name: "summary" model_tier: "L2" max_cost: 0.003 timeout_ms: 5000 - name: "reasoning" model_tier: "L3" max_cost: 0.02 timeout_ms: 30000 user_segment_overrides: - segment: "vip" task_types: - name: "summary" model_tier: "L3" max_cost: 0.01

看到这个结构,你会发现它实际上就是一个路由决策表:默认策略 + 用户段覆盖。每一行都是一个完整的路由决策:什么任务、用什么档次的模型、最多花多少钱、最多等多久。这让团队里的任何人都能看懂路由规则,也方便后续调整。

4. 把路由规则写进 Runtime:一个轻量但完整的实现方案

4.1 Runtime 层需要具备的三个基础能力

要把路由规则真正下沉到 Runtime,关键是先明确 Runtime 要提供什么能力。我把它归纳为三个:请求解析、规则匹配、执行与观测。

请求解析,指的是在请求进入业务逻辑之前,Runtime 先对请求做预处理,提取任务类型、用户段、内容长度、关键词等路由所需的信息。这里有个原则要把握——Runtime 不需要理解业务的具体细节,它只需要拿到路由所需的特征字段就够了。

规则匹配,指的是根据解析出的特征,在路由决策表里找到对应的模型配置。这个模块要快,最好能做到毫秒级,否则路由本身会成为性能瓶颈。

执行与观测,指的是拿到模型配置后,Runtime 负责调用模型、处理重试、上报日志和指标。日志里至少要包含:命中的路由规则、模型的供应商和版本、响应时间、Token 消耗、成本估算。有了这些数据,你才能回答“这个模型到底值不值”这类问题。

4.2 一个可运行的决策引擎示例

下面这段代码,我简化自一个生产项目的核心模块。它不是最完善的,但完全可以直接跑起来,给你一个 Runtime 路由决策引擎的落地雏形。

import yaml import time import random from dataclasses import dataclass, field from typing import Optional # 配置加载 class ConfigLoader: def __init__(self, path="route_rules.yaml"): with open(path, "r") as f: self.data = yaml.safe_load(f) def get_default_policy(self): return self.data["route_rules"]["default_policy"] def get_segment_override(self, segment: str): for override in self.data["route_rules"].get("user_segment_overrides", []): if override["segment"] == segment: return override return None # 请求解析结果 @dataclass class RoutingRequest: task_type: str user_segment: str payload_size: int priority: int = 0 # 模型路由结果 @dataclass class RoutingDecision: task_type: str model_tier: str max_cost: float timeout_ms: int rule_source: str = "default" # 熔断器:记录连续错误并熔断 class CircuitBreaker: def __init__(self, threshold=3, cooldown_ms=10000): self.threshold = threshold self.cooldown_ms = cooldown_ms self.fail_count = 0 self.last_fail_time = 0 def is_open(self): if self.fail_count >= self.threshold: if time.time() * 1000 - self.last_fail_time > self.cooldown_ms: self.reset() return False return True return False def record_failure(self): self.fail_count += 1 self.last_fail_time = time.time() * 1000 def record_success(self): self.fail_count = 0 def reset(self): self.fail_count = 0 # 模型执行器:模拟调用不同档次的模型 class ModelExecutor: def __init__(self): self.circuit_breakers = { "L1": CircuitBreaker(), "L2": CircuitBreaker(), "L3": CircuitBreaker(), } def invoke(self, tier: str, request: RoutingRequest) -> str: if self.circuit_breakers[tier].is_open(): raise RuntimeError(f"{tier} tier is circuit-broken") # 模拟调用耗时 latency = random.randint(200, 1500) time.sleep(latency / 1000) # 模拟错误 error_rate = {"L1": 0.02, "L2": 0.03, "L3": 0.01}[tier] if random.random() < error_rate: self.circuit_breakers[tier].record_failure() raise RuntimeError(f"{tier} tier call failed") self.circuit_breakers[tier].record_success() return f"[{tier}] result for {request.task_type}" # 路由引擎 class RoutingEngine: def __init__(self, config_path="route_rules.yaml"): loader = ConfigLoader(config_path) self.rules = loader.get_default_policy().get("task_types", []) self.segment_override_policies = {} for override in loader.data["route_rules"].get("user_segment_overrides", []): self.segment_override_policies[override["segment"]] = override.get("task_types", []) self.executor = ModelExecutor() def _get_rule_for_task(self, rules, task_type: str): for rule in rules: if rule["name"] == task_type: return rule raise KeyError(f"No rule for task type: {task_type}") def decide(self, request: RoutingRequest) -> RoutingDecision: # 先看用户段覆盖 override_rules = self.segment_override_policies.get(request.user_segment) if override_rules: try: rule = self._get_rule_for_task(override_rules, request.task_type) return RoutingDecision( task_type=request.task_type, model_tier=rule["model_tier"], max_cost=rule["max_cost"], timeout_ms=rule["timeout_ms"], rule_source="segment_override" ) except KeyError: pass # 回退到默认策略 rule = self._get_rule_for_task(self.rules, request.task_type) return RoutingDecision( task_type=request.task_type, model_tier=rule["model_tier"], max_cost=rule["max_cost"], timeout_ms=rule["timeout_ms"], rule_source="default" ) def execute(self, request: RoutingRequest): decision = self.decide(request) start = time.time() try: result = self.executor.invoke(decision.model_tier, request) latency = int((time.time() - start) * 1000) return { "request": request, "decision": decision, "result": result, "latency_ms": latency, "status": "success", } except RuntimeError as e: return { "request": request, "decision": decision, "error": str(e), "latency_ms": int((time.time() - start) * 1000), "status": "failed", }

这个决策引擎的逻辑并不复杂,核心就三步:解析请求里的任务类型和用户段;查路由表找到对应规则;按规则调用模型。但请注意,这个引擎有几个对生产非常重要的细节:

  • 用户段覆盖优先于默认策略,且支持部分覆盖。VIP 用户只有摘要任务走强模型,其他任务还是默认策略,这很灵活。
  • 熔断器加持,某个模型档位连续失败多次后,在一段时间内不再把请求打给它,避免雪崩。
  • 所有决策和执行都在同一个模块里,日志可以统一上报,不会散落到业务代码各处。

4.3 在 Runtime 里的接入方式:中间件模式最省事

有了决策引擎,接下来要解决的是怎么把它嵌入现有系统。这里我的经验是:优先采用中间件模式,而不是让业务代码显式调用路由引擎。

所谓的中间件模式,就是所有进入 Agent 的请求都会先经过 Runtime 处理,Runtime 看完路由规则后,如果有必要,再把请求转给对应的模型执行器。业务代码里完全不需要感知模型选择这回事。

我举一个实际用的例子。假设你们内部有一个 Agent 服务框架,请求进入 Controller 之前,加一层 AOP 拦截器或者中间件,里面注入路由引擎,请求来了自动解析、自动路由、自动执行。业务代码里只需要拿到最终模型返回的结果,至于这个结果是怎么选路出来的,业务不用管。

这样做有一个立竿见影的好处:当产品经理提出“这个新场景应该用另一个模型”时,你只需要更新一份配置,加一条路由规则,业务代码零改动。如果路由逻辑散落在业务代码里,光是找到所有需要改的地方,就够你加三天班了。

4.4 观测与日志:没有数据的路由规则是盲目的

说句实在话,很多团队做了路由规则,但从不看路由效果,这跟闭着眼睛开车没两样。路由规则的“路况信息”,全部来源于观测数据。

所以在落地 Runtime 路由时,我会同步把 Metrics 埋好,至少包含以下几项:

  1. 每个任务类型的请求量分布。哪个任务类型占比最高,占比趋势如何,这能帮你判断路由配置是否符合业务分布。
  2. 每个模型的 Token 消耗与成本趋势。这个不用多说,月底账单对不上账时你会感激这组数据。
  3. 每个模型的错误率和 P95/P99 延迟。动态路由和熔断策略的依据都在这。
  4. 路由规则命中分布。默认策略命中率多少,用户段覆盖命中率多少,这能反映出你的规则需不需要调整。

我用过的比较轻量的方案,是让路由引擎在执行时把决策结果写进结构化日志,然后接入日志系统或时序数据库,再用现成的图表工具做面板。不需要一开始就上重型的可观测性平台,起步阶段简单直接反而更好维护。

5. 动态路由:让 Runtime 自己学会“见风使舵”

5.1 为什么静态路由不够用

静态路由解决的是“规则清晰、场景明确”的问题。但线上的模型市场和服务稳定性,不会一直如你所愿。

举个例子,你上线时选了个模型供应商 A 的 L3 模型做推理任务,效果很好。三个月后,供应商 A 的推理服务频繁超时,错误率居高不下;而供应商 B 的 L3 模型已经迭代了一版,在同样任务上的效果和稳定性都超过了 A。按照静态路由,你的用户会持续忍受 A 带来的糟糕体验,直到有人发现异常并手动改配置。

如果路由规则只是一份静态配置,那它就是个麻烦的“半成品”。Runtime 的价值,正是在于有能力在规则基础上叠加“动态调整”的能力。

5.2 动态路由的三个实用策略

  • 基于错误率的熔断降级:某个模型或供应商连续错误超过阈值,Runtime 自动把流量切到备用模型,等供应商恢复后再切回来。这个策略是保底手段,至少保证 Agent 不会因为某个供应商的故障而整体不可用。
  • 基于延迟的权重调节:Runtime 周期性地检测各候选模型的响应延迟,如果某个模型持续变慢,就降低它的权重,把更多请求分给更快的模型。注意这里是权重调节,不是直接把某个模型禁掉,因为慢并不代表不能用。
  • 基于成本预算的动态升级降级:这是把成本维度和动态维度结合的一个例子。当某个请求的实际消耗远低于预算时,Runtime 可以决定升级到更高质量的模型,提升用户体验;反过来,当成本快超预算时,Runtime 会优先选择更省钱的模型。

5.3 从观察者到决策者:动态路由的反馈闭环

动态路由要做到真正的闭环,核心不只是“调整”,而是“反馈”。路由引擎需要根据上一次执行的结果,更新决策参数。

比如,用户请求进来,Runtime 先选中了 L3 模型,结果执行超时。那么这个超时信息要反馈给路由引擎,让它在未来一段时间内降低该模型在此类任务上的优先级。或者某个任务连续几次在 L2 模型上产生低质量输出,路由引擎应该考虑把这类任务稳定地提升到 L3。

在实现上,我倾向于把这些反馈做成“近期滑动窗口统计”,而不是全局统计。因为线上的流量分布时刻在变,用 5 分钟或 10 分钟的滑动窗口,比用全天累计数据更能反映即时情况。

这里要特别提醒一点:动态路由要保守,不要激进。一次超时或一次低分输出,不代表模型不行。触发调整的阈值要高一些,调整的幅度要小一些,宁可多观察一轮,也不要让路由规则在几个模型之间反复横跳,那样反而会引入大量不稳定因素。

6. 模型选型的最终决策表:把路由规则作为前置约束

6.1 先定路由规则,再选模型参数

回到最开头的问题:Agent 模型怎么选?

到这一步,答案已经很清楚了。模型选型不是拍脑袋比较一堆模型跑分,而是先定义你的路由规则,明确每个任务类型对延迟、成本、质量的要求,然后反推每个档位的模型应该具备什么参数和能力。有了这套前置约束,选型就变成一个按图索骥的过程。

比如,你的规则里定义 L1 档位是意图识别专用,那么你选 L1 模型时只看意图识别的准确率、延迟和成本,而不会因为它在数学推理上分数高就去选它。同理,L3 档位如果承担推理分析任务,你选型时就侧重看多步推理、长上下文理解这类能力。

6.2 给每个档位准备两个候选模型

选型经验里还有一条比较重要的:每个档位不要只绑定一个模型,至少准备两个候选,一个主、一个备。

主模型是常态下用的,备模型在动态路由触发熔断或降级时会顶上去。这么做不单是为了容灾,也给了团队在模型效果出现分歧时做 AB 对比的空间。运营同学可以说“备模型在新文案任务上表现更好”,那就可以在规则里临时切换一批流量过去验证,而不是改代码或者换模型上线。

6.3 用“路由演练”验证模型选型是否真的合理

最后分享一个我自己比较推崇的实践:路由演练。

具体做法是,选型完成后,不要直接上生产,先把路由规则和历史请求数据放到一个仿真环境里,跑一遍全量回放。看看各个任务被分到了哪些模型,延迟和成本是否符合预期,有没有哪个规则产生了意外情况,比如某个冷门任务把大量请求导向了 L3 导致成本爆表。

我跑过一次演练之后,发现原本以为只占 5% 流量的推理任务,在路由规则里定义过宽,把很多类似的分类任务也兜进去了,结果 L3 模型的实际流量比预期翻了三倍。如果没做演练直接上生产,下个月账单可能就要吓到财务同事了。

路由演练的工具不需要多复杂,把线上 Nginx 或网关日志回放一遍即可,核心是让路由规则的每一个分支都能被覆盖到。

7. 常见问题与排查技巧实录

7.1 “改了一条规则,怎么线上还是走老模型?”

这个问题的原因,九成是配置没生效。先检查配置中心是否真的推送到了所有 Runtime 节点,再检查是不是因为配置带缓存,需要等缓存过期或者手动 refresh。还有一个很隐蔽的坑:某些团队把用户段覆盖规则放在默认策略之前,但覆盖规则里没有新增的任务类型,导致新任务类型悄悄落回了默认策略,看起来就像“改了没生效”。

排查口诀是:先看配置版本,再看节点日志,最后看路由命中记录。三步走完,一般都能定位到问题。

7.2 “动态路由频繁切换模型,用户反馈结果不稳定”

动态路由的反馈机制要设置“死区”,也就是当候选模型的得分差异在一个可接受范围内时,保持原有选择,不触发切换。很多团队的动态路由做成了“每轮都重新选择最优”,结果在模型效果接近时来回跳转,用户体验奇差。记住,动态路由的目标是在一个窗口期内保持决策稳定,而不是每分每秒都追求最新最优。

我的做法是:候选模型得分差异超过 10% 才允许切换,低于这个阈值则沿用当前模型。这样可以过滤掉大量噪声干扰。

7.3 “路由规则越加越多,配置越来越乱,怎么梳理?”

我见过团队里路由规则膨胀到上百条,最后没人敢动。这个问题根源在于没有做好规则的“分类分层”。我建议把规则分成三层:全局策略、任务类型策略、用户段策略。新增规则时,先判断它属于哪一层,能放进已有层的就别新增独立规则。定期清理那些命中量为 0 的“僵尸规则”,这些规则往往是历史迭代残留,既增加理解成本,又可能在某个角落里干扰正常路由。

7.4 “买了贵的模型,但业务方觉得效果提升不明显,怎么办?”

这种情况太常见了。先把观测数据拉出来,对比贵模型和便宜模型在同一任务上的关键指标,比如回答准确率、用户满意度、任务重试率。如果差异确实不大,那就果断把该任务降回便宜模型。很多时候,贵的模型提升的只是跑分,不是业务指标,这时候省钱是优化而不是降级。

8. 最后分享一点小经验

我在做 Agent 路由这套东西时,最大的感受就是:模型选型这件事,别把它看成一步到位的决策,它更像一个持续迭代的过程。今天选中的模型,可能三个月后就被更好的替代了;今天定义的路由规则,可能一个季度后就要根据业务变化重新调整。

所以,与其花大量时间比较模型跑分,不如把精力花在搭好 Runtime 的路由框架上。框架稳了,换模型就像换零件一样简单;框架不稳,每次换模型都是一次伤筋动骨的重构。

如果你也在做 Agent 基础设施,建议从今天开始,把路由规则从业务代码里一件件往外挪,挪进 Runtime 里。等挪完的那天,你会发现模型选型这件事,突然就变得没那么纠结了。按我个人经验,这个改造带来的收益,比把三个顶级模型集成的收益都大,而且大得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询