混合推理路由:端侧回退后的分流判据与降级设计

举报
上云 发表于 2026/11/13 09:33:09 2026/11/13
【摘要】 端侧小模型接入后,回退率与云成本常双双失控。本文把回退拆成质量、超时、能力三类,给出置信度校准、时延预算传递、任务能力矩阵三条可量化判据,并给出一条带超时与熔断的有限深度降级链路及对应观测口径。

端侧小模型接进链路之后,团队通常会看到两个反直觉的数字:云侧调用量没降多少,用户感知的 P95 时延反而变差了。问题很少出在模型本身——端侧能跑什么早已论证过很多轮,真正缺的是另一层设计:什么请求该留在端侧、什么该放出去,以及放出去之后怎么兜。这篇只谈这三件事的工程实现。

把「回退」拆成三类,指标才有意义

只统计一个总回退率,调优时基本无法下手。端侧回退至少有三类,触发原因、降级动作、优化手段完全不同:

类型 触发条件 典型原因 优化方向
质量回退 输出不达标 能力不足、提示词不适配 提示词 / 微调 / 阈值校准
超时回退 超出时延预算 设备降频、长输出、冷启动 预算分配、输出裁剪
能力回退 请求超出端侧能力域 长上下文、多模态、多步工具 前置准入,不该进端侧

三者混在一起统计,就会出现"回退率 30%,但不知道是该改提示词还是该改路由"的窘境。埋点必须至少带 fallback_type 和 task_type 两个维度,且 fallback_type 在决策点写死,不能事后靠规则推断——事后推断出来的归因,误差大到不能用于调参。

还有一点:能力回退本质上是准入问题,不是回退问题。把能力不匹配的请求放进端侧再回退,浪费的是设备算力和用户等待时间,而且会污染回退率指标。准入判定应该放在请求进入端侧之前,也就是网关侧的任务识别环节,用下面第四节的能力矩阵完成。

判据一:置信度要校准,阈值从目标反推

端侧置信度的来源有三种,可靠性依次下降:模型输出的 token logprob、多次采样的答案一致率、输出后校验。第一种最便宜,但不少端侧 runtime 并不暴露 logits;第三种对结构化输出几乎零成本,schema 校验、字段取值域校验、正则约束都能直接当判别器用,建议优先落地。

阈值绝不能凭感觉定。正确做法是用线上回捞样本做分桶统计,得到每个置信度区间的"端侧直接通过率"(下表为某客服问答任务的实测形态,示意):

置信度区间 样本占比 端侧通过率 建议处置
[0, 0.45) 18% 11% 直接上云
[0.45, 0.65) 27% 58% 端侧 + 后校验
[0.65, 0.85) 34% 89% 端侧直出
[0.85, 1.0] 21% 97% 端侧直出,跳过校验

阈值从业务目标反推:若要求端侧直出的合格率不低于 95%,查表可知切点大致落在 0.70,而不是全局取 0.80——取高了会把三成多高质量样本白白推到云上,这是最常见的成本泄漏点。

必须按 task_type 分开校准。同一个阈值在意图分类上合理,搬到开放问答上会把合格率压到 70% 以下。原因是输出空间大小不同:分类任务标签集固定,logprob 天然偏高;生成任务输出空间大,logprob 天然偏低。一套阈值横跨所有任务,等价于只对其中一类做了校准。

还有个容易翻车的细节:置信度与正确率在小模型上不保证单调。3B 级别的模型在"看起来很确定但答错"的样本上置信度极高,这是幻觉的典型特征。所以校准之后要做一次单调性检验,对不单调的最高分桶单独打标,用规则或判别器兜住,不能无条件信任 logprob。

判据二:时延预算往下传,路由点放在 70%

"端侧更快"是错觉。端侧省掉了 RTT,但 decode 速度受设备负载影响极大——后台跑着视频时 tok/s 掉一半很常见。所以时延判据必须用"预估完成时间 vs 剩余预算"来比较,不能用固定阈值。

前置条件是把 deadline 从入口一路传下来。网关拿到 SLA(比如首 token 800ms、整体 8s),随请求上下文下发 deadline_ms 和 elapsed_ms,路由层只做减法:

def latency_gate(ctx, est):
    remaining = ctx.deadline_ms - ctx.elapsed_ms
    dev_eta = est.device_ttft_ms + ctx.generated_tokens / est.device_tps * 1000
    cloud_eta = est.cloud_p90_ms          # 含排队,取近 5 分钟滑窗 P90
    if dev_eta <= remaining * 0.9:
        return "device"
    if cloud_eta <= remaining:
        return "cloud"
    return "degrade_truncate"             # 都赶不上,裁剪输出或返回部分结果

三个工程细节:

路由判定点放在预算的 70% 处。 太早信息不足,端侧还没真正试过;太晚切云也跑不完。经验值是在消耗掉 30% 预算时做一次判定,把剩下的 70% 留给回退路径本身。

TTFT 和总时长要分开看。 流式场景下,端侧首 token 通常 100~200ms,云侧 400~800ms,感知上端侧明显占优;但端侧整体 tok/s 可能只有云侧的五分之一,长输出会被反超。判据应该按预计输出长度分段:

预计输出长度 端侧 TTFT 端侧总时长 云侧总时长 决策倾向
< 100 token ~150ms ~0.7s ~1.2s 端侧
100~400 token ~150ms ~2.5s ~1.6s 看预算,紧则上云
> 400 token ~150ms > 5s ~2.8s 云侧(RTT 仍达标时)

别轻易做中途接管。 端侧流式输出到一半超时,把已输出内容当 prefix 传给云侧续写,听着很美,实际有两个坑:一是两侧 tokenizer 不一致,prefix 的分布对不上,续写容易出现风格断裂和复读;二是云侧要 prefill 整个 prefix,成本比从头生成更高。务实做法是设一个"承诺点"(比如 120 token 或第一个完整句子),过了承诺点就不再接管,宁可让端侧跑完;没到承诺点则整段丢弃重来,用户看到的是稍慢但完整的答案。

判据三:任务类型用能力矩阵,硬约束优先

任务类型判定的价值是把大部分"显然不该上端侧"的请求挡在门外。实现上别写一长串 if-else,用一张可配置的能力矩阵,让算法同学能改而不用发版:

任务类型 端侧支持 附加条件
意图分类 / 槽位抽取 是 标签集固定,schema 校验通过
短摘要(< 2k token) 是 输出上限 300 token
长文摘要(> 8k token) 否 超出上下文窗口
结构化抽取 是 必须过 schema 校验,失败即回退
单步工具调用 是 仅限白名单内工具
多步工具编排 否 编排能力与错误恢复不足
代码生成 部分 单文件片段可,跨文件上下文不可
含 PII 的请求 强制 合规约束,不可上云

最后一行是重点:合规与隐私是硬约束,优先级高于置信度和时延。含 PII 的请求哪怕置信度只有 0.2 也不能上云,此时降级方向不是"换更强的模型",而是"换更保守的输出"——只返回脱敏结果,或提示用户补充信息。在决策函数里,硬约束必须放在最前面,且不能被后续打分覆盖。

合成决策、降级链路与观测

三个判据之间是短路求值的关系,并不是加权平均:

def decide(req, ctx, est) -> Decision:
    # 1. 硬约束,不可协商
    if req.has_pii and not ctx.policy.pii_to_cloud:
        return Decision("device", "pii_pin", degrade=0)

    # 2. 能力准入,不满足直接上云
    if not ctx.caps.supports(req.task_type, req.tokens):
        return Decision("cloud", "capability_miss", degrade=0)

    # 3. 时延预算
    if latency_gate(ctx, est) == "degrade_truncate":
        return Decision("cloud", "latency_pressure", degrade=1)

    # 4. 置信度(仅端侧已产出时)
    if ctx.device_output and ctx.device_output.confidence < ctx.calib.threshold(req.task_type):
        return Decision("cloud", "low_confidence", degrade=0)

    return Decision("device", "default", degrade=0)

两个约束:ctx.calib.threshold 必须支持热更新,阈值调优是常态;Decision 必须带 reason,它是后续所有观测的维度基础。

回退不是"切到云侧就结束"。云侧同样会排队、限流、故障,降级链要设计成有限深度、单调变强的序列:

层级 动作 超时上限 失败后
L0 端侧原生 预算 × 0.7 → L2
L1 端侧降级:上下文缩到窗口 70%、关 few-shot、输出上限砍半 预算 × 0.3 → L2
L2 云侧小模型 预算 × 0.5 → L3
L3 云侧大模型 预算 × 0.8 → L4
L4 语义缓存命中 / 兜底话术 / 转人工 50ms 结束

L1 值得单独做:一部分"质量回退"其实是上下文过长或 few-shot 干扰导致的,缩短后重试一次的成本远低于上云。但要设死上限——L1 只允许一次,禁止递归降级。

熔断放在两个边界上,用滑动窗口统计失败率,超阈值则打开、冷却后半开。两个必须有的熔断:云侧连续失败或 P90 突增时全量走端侧,质量降级但可用;端侧模型加载失败、内存不足或回退率连续超标时全量上云。后者尤其重要,"端侧不可用"是比"回退率高"严重得多的故障。

观测口径容易踩坑:回退率的分母是进入端侧的请求数,不是总请求数,否则能力回退会稀释掉真实回退率。另外三个指标必须盯:误回退率(回捞回退样本,端侧答案其实可接受的比例,通常 15%~30%,是纯成本浪费)、漏回退率(端侧直出但不合格的比例,是体验损失,与误回退率互为矛盾)、每会话云调用次数(比"云成本占比"更能反映路由效果)。降级层级分布也要看,L2/L3 占比抬头往往早于用户投诉。

落地建议

  1. 先埋点,再调阈值。 上线首周只做观测,按 fallback_type × task_type 打点,不要急着改路由逻辑。没有分桶数据,任何阈值调整都是盲调。
  2. 置信度按任务分桶校准,并做单调性检验。 每个任务类型单独出一条置信度-合格率曲线,阈值从目标合格率反推;最高分桶若出现"高置信度低合格率",用规则或判别器单独兜住。
  3. deadline 从网关一路传到路由层。 上下文里带 deadline_ms、elapsed_ms,路由层只做减法,判定点统一放在剩余预算 70% 处,禁止在业务代码里写本地常量超时。
  4. 降级链定死深度和超时。 每级有独立超时和明确失败去向,L1 仅一次且禁止递归;跨端、跨云两个边界都加熔断,端侧整体不可用时必须有一条"全量上云"通道。
  5. 建立每周回放机制。 抽样回捞、离线用当前端侧模型重跑、画出置信度-合格率曲线并按成本目标定阈值,把误回退率压到 10% 以下,再谈扩大端侧覆盖率。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。