☰
混合模型路由实战:为Agent应用节省70%成本而不损质量
2026/10/22 9:42:24 网站建设 项目流程

1. 从账单爆炸说起:为什么 Agent 不能永远只用旗舰模型

我最早开始认真考虑混合模型路由,是因为一张让我肉疼的 API 账单。

当时我维护的一个 Agent 服务,所有请求都统一接到最贵的旗舰模型上,平均一次对话要串三到五轮工具调用,遇到复杂的任务甚至要循环十几轮。一个月跑下来,整个服务的调用量其实并不大,但账单却高得吓人。最讽刺的是,我翻日志以后发现,真正需要旗舰模型深度思考的请求,可能连总量的 20% 都不到。剩下那 80% 的请求,有的只是让 Agent 查个天气、转个格式、做个简单的关键词匹配,有的只是用户随手发了句“在吗”,但每一次都让旗舰模型全功率运转了一遍。

那时候我就意识到,Agent 应用的成本问题,本质上不是“模型贵不贵”的问题,而是“该贵的时候贵、该便宜的时候便宜”的分配问题。

混合模型路由解决的就是这件事。它的核心思路很朴素:在一个 Agent 服务背后接入多个不同能力、不同价格的模型,在每次请求进来的时候,由一个路由模块自动判断“这单生意该派谁去”。能力要求低的请求,送到轻量级模型,几毫秒就返回,成本几乎可以忽略;真正复杂的推理任务,再交给旗舰模型,保证输出质量。整个过程中用户无感知,但你的成本结构会完全不一样。

这套机制特别适合两类团队。一类是已经把 Agent 跑起来、开始被成本困扰的工程团队,他们需要在不牺牲体验的前提下把账单压下来。另一类是正在做 Agent 选型、还在“一个模型打天下”和“多个模型混着用”之间犹豫的同学。这篇文章我会把我自己实现这套路由机制的全过程,包括判断逻辑、工程细节、踩坑记录和上线后的真实数据,完整拆开来讲。

2. 路由判据设计:如何让系统决定“这单该花多少钱”

2.1 第一层判断:任务意图与类型识别

路由要做的第一件事,不是选模型,而是先搞清楚用户到底想干什么。没有这一层,后面所有复杂度判断都是空中楼阁。

我见过不少团队做路由,第一版就是简单地拿 prompt 长度做判断:prompt 短就走便宜模型,prompt 长就走贵模型。这个逻辑不能说完全没用,但非常容易被击穿。有些推理任务,prompt 本身很短,比如“帮我证明一下这个数列是发散的”,就十几个字,但需要很强的推理能力;反过来,有些任务 prompt 很长,比如“把这封 4000 字的中文邮件翻译成英文”,字数很多,但翻译这种活根本不需要旗舰模型参与。

所以我的做法是,路由的第一层先做一个意图分类。实现方式有三种,我按性价比排序:

第一种是用一个小模型做分类器。把用户原始输入(注意,不是整个系统 prompt,只是用户这次说的话)丢给一个便宜的文本分类模型,让它输出预定义的任务类型标签。这个方案的优点是非常便宜,而且分类结果稳定可解释,后面接规则也好接。

第二种是用 embedding 相似度匹配。预先定义好几十个典型任务类型的示例文本,生成 embedding 存起来,然后每次请求进来,计算用户输入和这几个示例的向量相似度,取最高的那个作为意图标签。这套方案的好处是没有额外模型调用延迟,匹配速度极快。

第三种是让大模型自己给任务分类。就是额外加一次轻量模型的调用,让它在输出正文之前,先输出一个 task_type 字段。优点是灵活,缺点是增加了链路延迟,而且你要解析模型输出、处理可能不合 JSON 格式的情况。

我用的是第二种方案,因为当时我们的 Agent 场景任务类型非常集中,无非就是“信息查询”“文本处理”“代码生成”“数学推理”“闲聊”这么几种,用 embedding 匹配已经足够稳。而且我发现一个技巧:不要只用一个示例代表一个类型,最好为每个类型准备 5 到 10 个不同的问法变体,因为同一类任务的表达差异非常大,示例太少了向量匹配会飘。

2.2 第二层判断:复杂度信号与置信度自评

意图分类只能决定候选模型池,也就是说“这单可能是哪几类”,但它决定不了“这单到底要花多大力气去处理”。同样是代码生成任务,让 Agent 写一个“Hello World”和写一个“分布式任务调度器的幂等重试模块”,难度差距是天上地下。所以路由必须再做一层复杂度判断。

复杂度信号的来源,我总结下来有四个:

第一是用户输入本身的特征。这里我不仅看长度,还会看几个额外信号:用户段落的标点密度、是否包含数学符号或公式、是否包含“请仔细思考”“逐步推理”这类强推理暗示词、是否包含大段报错日志或堆栈信息。这些信号单独看都不够强,但组合起来能很好地表达任务的推理密度。

第二是 Agent 内部状态的上下文长度和工具调用期望。有些任务虽然用户只问了一句,但 Agent 为了回答这个问题,需要先查数据库、再调外部 API、再汇总结果,这种多工具链路的请求就应该走强模型。所以路由器不能只看用户输入,还要结合 Agent 执行计划里预计必要的工具调用数量来判断。

第三是模型置信度自评。这个方案我用得比较谨慎:让轻量级模型在处理请求以后,额外输出一个 self_eval 字段,表示它对这次回答质量的信心。如果信心低于一个阈值,就把结果作废,重新发给旗舰模型。这个机制很巧妙,等于让最便宜的模型当第一道筛子,筛不住的才往上送。但这里有个成本陷阱,后面我在踩坑部分会专门讲。

第四是我后来加上的错误反馈信号。Agent 调用外部工具返回了异常、JSON 解析失败,或者用户对上一个回答点了“不喜欢”,这些信号一旦出现,几乎就意味着“当前模型搞不定这件事”,应该立刻升级到更高级的模型。这条反馈链路让我省掉了大量无用的重复调用,因为你与其让便宜模型再试一次,不如直接升级。

2.3 路由表配置:质量与成本的显式权衡

前面这些判断做完以后,最终要落到一张路由表上。我自己的实现里,路由表是这样的一个结构:

任务类型复杂度等级路由目标备注
闲聊、问候、情感类低轻量模型几乎免费,用最便宜档位
信息查询、事实检索低轻量模型如果有引用需求可升一档
文本处理、改写摘要中中档模型结构化输出能力要好
代码生成、调试解释中高中档或旗舰看是否涉及多文件修正
数学推理、逻辑分析高推理特化模型这里有个重点,见下文
多工具调用、规划类高旗舰模型工具调用稳定性优先
任何类型出现错误信号升级当前档上位模型错误信号优先级最高

这个表格看着简单,但有两个细节值得展开。

第一个细节是“推理特化模型”这一行。当时我们对接的模型里,有一类模型价格适中但在数学、代码推理上表现特别好,另一类通用旗舰模型虽然综合能力强但价格是前者的好几倍。对于纯数学推理、逻辑证明这类任务,推理特化模型的效果和旗舰模型几乎打平,成本却低一大截。所以我的路由表在“数学推理”这一类上,会优先路由给推理特化模型,而不是直接丢给最贵的选项。

第二个细节是关于“结构化输出稳定性”的。做 Agent 的同学应该深有体会:一个模型即使推理能力很强,如果它偶尔不按预定义的 JSON 格式输出,你的下游工具解析就会崩溃,然后整条 Agent 链路就断了。所以配置路由表的时候,不能只看模型贵不贵、聪明不聪明,还要看它对 JSON 模式和 function calling 的支持是否稳定。我交过学费之后,现在的做法是在每个候选模型上,都预先跑一份包含 50 个典型请求的回归测试集,专门记录格式遵从率,再把这数据写进路由的权重因子。

3. 路由执行层的工程细节:超时、重试与优雅降级

3.1 降级链:贵的失败后该由谁接管

路由不只是“选一个模型”这么简单,更关键的是“选了之后出了状况怎么办”。真实生产环境里,最贵最聪明的那个模型,恰恰可能是延迟最高、最容易超时的那个。所以我在路由层必须设计完整的降级链。

降级链的设计原则是这样:路由目标优先,但任何单独模型失败时,请求必须能顺延到下一个模型,而不是直接向用户报错。

举个例子,一次复杂代码生成任务,路由决策结果是“旗舰模型”,但它超时了。这时候降级链要把请求转给此时仍旧健康的次优模型,而不是让用户干等或者说“服务不可用”。我在实现时维护了一个多级候选列表,每一级都包含模型名称、调用优先级、健康状态三个字段。请求进来以后,从优先级最高且健康状态正常的模型开始尝试,失败就自动切换下一级。

这里有个经验值得分享:降级链不一定非要“高级降中级、中级降低级”这种单向降序。更好的做法是维护一个动态的可用模型池,并且考虑模型之间的互补关系。比如当前最快的轻量模型是 A,虽然路由目标是 B,但如果 A 当时的负载非常低且返回速度特别快,完全可以把一部分中低复杂度请求临时切给 A。这在高峰期削峰的时候特别有用,能让整个系统的吞吐量更平滑。

3.2 超时与重试的平衡:避免双重扣费

超时和重试是路由层最容易产生隐藏成本的地方,也是我自己真正踩过坑之后才彻底想明白的一个机制。

第一版实现里,我给所有模型设置了同样的超时时间,比如 60 秒。结果发现一个尴尬现象:轻量模型通常 2 到 3 秒就返回了,设置 60 秒的超时根本没用上;旗舰模型偶尔会因为推理链路过长而超过 60 秒,超时之后我又盲目地重试一次,又等 60 秒。最后用户没拿到结果,但后端已经因为两次完整调用被扣了两次费。

后来我把超时策略改成按模型分级:轻量模型超时时间 10 秒,中档模型 30 秒,旗舰模型 60 秒。同时,重试不是简单的“重试一次”,而是先把同一请求按降级链切换到另一个健康模型再试,只有当降级链全部不可用时,才会选择对原模型做一次幂等重试。而且重试之前必须确认这个请求是幂等的——如果是 Agent 内部已经执行过一次写操作的请求,盲目重试会造成重复扣费甚至重复执行事故。

另一个容易忽视的细节是“流式响应下的超时判断”。如果你使用的是流式接口,不能只等首字节,而应该监控两个指标:首 token 延迟和两次 token 之间的最大间隔。如果一个模型首 token 秒回,但之后每个 token 之间间隔超过 20 秒,说明它某个环节卡住了,这时候也应该触发降级而不是傻等。

3.3 上下文与并发:路由决策必须以真实输入为准

路由决策必须基于真实、完整的当前请求上下文,而不是基于某个固定规则硬编码。这句话听起来像废话,但我真的见过有团队把路由做成“所有请求默认走便宜模型,只有白名单里的用户走贵模型”。这种做法的问题在于,它对“当前这条请求的内容”完全无感,白名单用户问一句“你好”也会被送到旗舰模型上去,成本根本压不住。

正确的做法是,路由模块应该直接读取本次请求的组装完成之后的消息列表。我在实现里就是这么干的:Agent 的入口拿到用户输入以后,先拼接历史对话、工具调用结果、当前系统指令,形成一个完整的 request_context,再把这个 context 送给路由模块做意图和复杂度判断,最后才决定目标模型。

之所以强调这一点,是因为如果不这样做,路由永远会短视。比如用户上一轮说“帮我分析这份销售数据的异常”,Agent 已经查完数据库,这一轮说“继续”,如果不结合完整上下文,路由器看到的就是一个孤零零的“继续”,它不知道该送哪个模型。只有把完整 context 都交给路由判断,才能准确知道这一轮任务其实还在上一轮的复杂延续中,应走对应等级的模型。

并发控制同样不容忽视。我在路由层加了一个简单的信号量机制,限制同一时刻对旗舰模型的最大并发调用数。原因很简单:旗舰模型虽然贵,但不同时间段它也是有小并发配额的。如果某段时间所有复杂请求集中涌进来,直接把旗舰模型打满,后面的请求全部排队,延迟就会飙升。这时候路由可以做一个“降级但要告知”的策略:把一部分中等复杂度的请求提前降级到中档模型,而不是全部挤在旗舰模型门口排队。这个策略效果很明显,我用一个公式来描述决策逻辑:

估计预计排队时间 = 当前等待队列长度 × 模型单请求估计耗时。当这个值超过路由目标的延迟预算时,就尝试将任务调度给下一个候选模型。

从现象看,就是高峰期 Agent 的 p95 延迟从原来的 9 秒降到了 5 秒以内,而输出质量几乎没变化。

4. 上线一周后的真实数据:成本降了,质量没塌

4.1 成本与质量指标怎么统计才可信

很多团队做完路由之后,跟我说“成本确实降了”,但我一问他们怎么统计的,就发现统计口径有问题。有的人只看 API 账单总额,但没排除业务增长带来的调用量变化;有的人只看单次平均成本,却没统计因为路由误判导致的“二次补救调用”。

我的建议是上线前就把三个指标定死:综合成本、关键质量指标、路由命中率。

综合成本按每千次有效对话来算,而不是看账单总额,这样才能排除掉请求量波动的影响。关键质量指标要看两类:一类是用户侧的引用,比如请求成功率、用户反馈里的不满意率;另一类是 Agent 侧的,比如任务完成率、工具调用失败率、返工率。路由命中率就是统计路由决策最终执行成功的比例——这个指标能反映出路由模块本身的稳定性。

还有一个容易被忽略的坑:统计质量指标时,一定要把“因为路由升级而成功”的请求单独分桶。否则你会遇到一个诡异的情况:便宜模型先答错了,路由升级到旗舰模型答对了,最后总账上任务成功率是 100%,你会以为便宜模型也能 100% 成功。实际上便宜模型的第一轮成功率可能只有 60%,只是被升级机制救回来了。这两种数据的含义完全不同,前者说明路由兜底有效,后者才是模型本身的真实水平。

4.2 实测数据:三档模型的分配比例

我拿自己维护的那个 Agent 服务做了对比测试。基线是全部走最贵的旗舰模型,改造成混合路由之后,一共跑了七天。

分配比例大概是这样的:

  • 轻量模型承接了约 55% 的请求,主要就是闲聊、查天气、格式转换、简单文本处理这类低难度任务,单次调用成本几乎可以忽略。
  • 推理特化模型承接了约 25% 的请求,集中在数学题、逻辑推理、数据分析解释这些场景,效果跟旗舰模型几乎打平,价格却便宜了一大半。
  • 旗舰模型只承接剩下约 20% 的请求,全是最复杂的多步骤规划、长代码项目修改、高难度逻辑推理。

成本数据上的变化更直观。改造前,每千次有效对话的综合成本我记为基准值 1.0。改造后,同一业务量的综合成本降到了 0.3 左右,也就是省了大约 70%。而关键质量指标里,用户不满意率没有明显上升,Agent 任务完成率只掉了不到 1 个百分点,基本在统计波动的范围内。

值得注意的是,这个结果不是一次性白捡的。为了达到“质量不掉”,我花的代价是:成本 0.3 这个数字里,其实包含了一部分“升级补救”的调用成本。也就是说,便宜模型先答、回答置信度低、再升级到贵模型重答,这种情况虽然没有让质量崩掉,但也确实多花了冤枉钱。后面的优化重点就是压低这部分的占比。

5. 踩过的坑与后续演进

5.1 让模型自己判断,结果全走贵模型

第一个踩的坑,就是我前面提到的“置信度自评”机制。一开始我对这个机制寄予厚望,觉得让轻量模型给自己打分,既省了规则配置又灵活。结果上线以后我发现一个极其讽刺的现象:轻量模型的 self_eval 打分普遍偏高,很少觉得自己的回答不够好,所以升级率并不高,任务完成质量却肉眼可见地下降。反过来,我把阈值调严一点之后,升级率又飙上去了,几乎变成了所有请求都走旗舰模型,成本比不用路由还高。

后来我仔细想了想,问题出在“自我评估这件事本身也需要能力”。一个模型如果只能考 60 分,它可能完全判断不出自己哪里错了——你让它评估自己的输出质量,它永远觉得“尽善尽美”。所以现在的置信度自评机制只保留了一个变体:用轻量模型生成,但用一个独立的中档模型做答案评估。评估模型的两段式判断:先看答案是否回答了用户问题,再看答案里是否存在明显的逻辑硬伤。这样引入了一层“外部审核”,比自我感觉靠谱得多。

这个坑给我的启发是:路由机制的决策来源,尽量不要用“被评估者自己”做唯一裁判,否则系统会趋向于保守,最后滑向成本最高的那条默认路线。

5.2 路由只在调用入口做,拦截了质量救赎的机会

第二个坑比较隐蔽:我一开始把路由模块放在了 Agent 的最外层,也就是所有请求进来先路由,路由完直接发给某个模型,然后整条链路就固定了。结果遇到一种情况:一个请求被路由到了轻量模型,轻量模型答着答着,我突然发现它已经连续两次工具调用失败。但按当时的架构,路由决策已经做完了,没有机制能中途把这条请求“再升级”到更高能力模型。

这就是我所说的“静态路由”和“动态路由”的差别。静态路由只管入口一次,后面就撒手不管。动态路由则会监听 Agent 链路的中间信号:某一步解析失败、某次工具返回异常、某轮回答置信度极低。一旦出现这类信号,路由模块会重新介入,把一个原本正在执行的轻量模型链路升级成中档或旗舰模型链路。

加了这个机制之后,我服务的整体任务完成率又往上提了两三个百分点。这个提升来自于那些本来会被浪费掉的中途失败请求——它们不再需要用户重新发起一次全新对话,而是在同一次对话里完成了模型升级。说实话,这个功能做完之后,我再看原本那种“入口一次路由定终身”的设计,觉得简直是在给质量埋雷。

5.3 下一步:反馈闭环与动态路由

目前这套混合路由机制已经稳定运行了一段时间,但我很清楚它还有明显的改进空间。最优先要做的,是让路由决策拥有反馈闭环。

原理很简单,但实现上有点复杂。现在 V1 版的路由表是静态的,任务类型分档和模型映射关系都是提前配置好的。某天如果我换了个新的轻量模型,或者某个旗舰模型的效果下降了,路由表不会自动感知,需要人工去调整。我想要的是:每次请求结束以后,把实际的任务类型、路由到哪个模型、任务完成率、回答质量评分、成本这些数据,全部汇总进一个统计池。每周自动跑一次数据回顾,对比同一个任务类型下不同模型的综合表现,然后自动生成“该在路由表里增删哪些模型”的建议。

更进一步说,我有考虑把路由策略从一个静态决策函数,改造成基于强化学习的调优方案。也就是说,路由决策不再是“根据当前输入特征选一个最优点”,而是在多个可选模型之间做试探和分配,让系统自己学出来“哪些请求该走哪些模型、什么比例最优”。具体的方案还在试。

从我个人体会来说,混合模型路由这个方向,并不是一个做完就一劳永逸的功能,而是会随着模型市场变化持续演进的基础设施。模型一直在更新,价格一直在变动,能力格局也在洗牌。只要这些变量在变,路由策略就需要持续优化。真正让它稳定发挥价值的,不是某个聪明的算法,而是整个机制的设计足够健壮——能观察、能反馈、能调整,让“质量”和“成本”这两头始终处在动态平衡的状态。

最后再分享一个小技巧:如果你也想给自己的 Agent 做路由,别急着在一开始就追求复杂模型。先用最朴素的规则表把 80% 的低难度请求分走,把成本先降下来,再慢慢在剩下的 20% 里打磨判断精度。路由是均衡的艺术,没必要一上来就用最重的方案解决一个其实很轻的问题。

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

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

立即咨询