AI agent 的 Assistant Auto LLM Routing 规划的思考

目的 :为 SkillLite Assistant 设计一套类似「Auto」体验的本地优先 LLM 路由能力。
范围 :先讨论 聊天/Agent/UI 路由层 ,不讨论云端计费池、统一托管 API、训练型策略系统。
当前状态:本地场景路由 + 非流式场景的失败自动切换(MVP-A)已落地;启发式复杂度路由、健康度评分、流式聊天的中途切换尚未实现。


1. 设计目标

这套能力不是为了"看起来聪明",而是为了解决三个实际问题:

  1. 主链路不要轻易挂掉
    • 主对话、Agent、多工具执行时,模型或 provider 出错不能直接让用户卡死。
  2. 辅助链路尽量省钱
    • followup、后台状态、轻量摘要/命名等场景不必默认使用最贵模型。
  3. 决策可解释、可回退
    • 用户与开发者都应能回答:这次为什么选这个模型?失败后切到了哪里?

2. 价值取向

第一原则:稳定优先于便宜

若只能二选一,优先级应为:

  1. 尽量别挂
  2. 再去省钱

原因:

  • 用户对"完全失败"的负面感受,通常强于"这次多花了一点 token"。
  • 对复杂 Agent 任务使用过弱模型,看似省钱,常会引发更多重试与上下文膨胀,最终反而更贵。

第二原则:主链路保守,边缘链路优化

  • 核心链路 :主对话、工具调用、代码修改、长上下文任务
    • 优先用更稳定的主模型,并提供 fallback。
  • 边缘链路 :猜你想问、轻量状态同步、标题/摘要、简单分类
    • 优先使用更便宜/更快的小模型。

第三原则:先规则,再启发式,再智能分类

演进顺序建议:

  1. 固定规则
  2. 失败自动切换
  3. 输入复杂度启发式
  4. 小模型分类 / 健康度打分

不要一开始就做成黑盒决策引擎。


2.1 我的判断过程

这份设计不是从"如何做得最聪明"出发,而是从"如何先解决最痛的问题"出发。

我是怎么判断优先级的

我对 LLM 路由问题的判断顺序是:

  1. 先看失败成本
    • 主对话、Agent、多工具执行,一旦失败,用户体验会直接断裂。
  2. 再看省钱空间
    • followup、标题、轻量摘要、后台状态这类任务,即使质量略降,也通常能接受。
  3. 最后才看自动化程度
    • "更智能"不一定比"更稳定"更有价值。

因此我的结论不是"先做最智能的 Auto",而是:

  • 先做不会让系统挂掉的自动兜底
  • 再把低风险任务迁到更便宜模型
  • 最后才逐步增加真正的智能决策

为什么不是一上来就做 Cursor 式 Auto

因为 SkillLite 当前更接近:

  • 本地优先
  • BYOK(用户自己配 key)
  • 多 provider 但未统一托管

这种形态下,如果一开始就追求完全自动分流,会同时引入:

  • 配置复杂度
  • 运行时状态复杂度
  • 失败回退复杂度
  • 可解释性复杂度
  • UI 理解成本

这会让"一个增强功能"迅速膨胀成"一个路由子系统"。


2.2 复杂度判断:会增加,但可以分层控制

结论先说:

这个设计一定会增加项目复杂度,但前两阶段的复杂度增加是可控且值得的;

真正危险的是过早引入"智能分流黑盒"。

复杂度会增加在哪里

维度 现在 增加 Auto 后
配置 主模型 / 场景到 profile 主模型、fallback、cooldown、tier、启发式
运行时 直接选定后请求 先决策,再请求,失败后切换,再记录原因
UI 用户知道自己选了什么 需要解释"为什么这次系统换了模型"
测试 请求是否成功 还要覆盖 fallback、cooldown、回退、提示

为什么前两阶段仍然值得做

Phase 1 的复杂度:中等,但收益最大
  • 新增内容:
    • fallback 列表
    • 错误分类
    • cooldown
    • 切换记录
  • 带来的收益:
    • 主链路失败率下降
    • "别挂"能力显著增强

这是典型的 复杂度上升,但价值足够覆盖成本 的阶段。

Phase 2 的复杂度:低到中等,收益稳定
  • 新增内容:
    • 一些固定低风险场景的成本分层
  • 带来的收益:
    • 成本下降
    • 几乎不影响主链路稳定性

这一步本质上是 静态分层优化,比智能决策要简单很多。

哪一步开始危险

真正复杂度快速爆炸的是:

  • 动态健康度评分
  • 小模型预分类
  • 多因子综合打分
  • 看实时输入语义做模型选择

因为从这一层开始,系统会从"路由规则"演进成"路由引擎"。


3. 不做什么

第一阶段明确 不做

  • 不做云端托管路由。
  • 不做跨用户统一策略下发。
  • 不做"完全自动理解所有自然语言意图再选模型"。
  • 不做复杂打分系统(成功率、延迟、成本、健康度同时建模)。
  • 不做隐藏式静默切换到陌生 provider 而不留痕。

这类能力后面可演进,但不应成为 MVP 前提。


4. 路由对象与术语

4.1 已有基础

  • llmProfiles: 用户保存的一组模型配置(provider + model + apiBase + apiKey
  • 当前已有:固定场景到已保存 profile 的本地映射

4.2 建议新增概念

  • Route Tier :按用途抽象出的路由层级,而不是直接写死某模型名
    • 例如:cheap / balanced / strong / vision
  • Primary Profile:某条链路的首选 profile
  • Fallback Profiles:主 profile 失败后的候补链
  • Routing Decision:一次请求最终选出的 profile 与原因
  • Provider Cooldown:某条失败链路在一段时间内不优先再试

4.2.1 两层结构:先配置绑定,再运行时决策

这套设计最容易讲清楚的方式,不是直接说"Auto 会帮你选模型",而是把它拆成两层:

第一层:设置里的配置绑定

这一层解决的是:

不同任务类型,原则上应该使用哪类模型 / 哪条 profile。

也就是说,用户先在设置里把绑定关系配好,例如:

  • 主对话 / Agent -> balanced 或某条稳定 profile
  • followup -> cheap 或某条便宜 profile
  • 图片任务 -> vision 或某条支持多模态的 profile
  • 自进化分析 -> strong 或某条更强的 reasoning profile

这一层的本质是:

  • 场景 -> profile
  • 场景 -> tier -> profile

所以从产品表达上说,这套系统的底座首先是 设置驱动的绑定表

第二层:运行时规则决策

这一层解决的是:

当前请求来了之后,应该怎么使用前面已经绑定好的配置。

运行时系统做的事情不是"随便猜一个模型",而是:

  1. 判断当前属于哪个场景
  2. 读取设置里为该场景绑定的 profile 或 tier
  3. 按规则尝试主模型
  4. 若失败,则进入 fallback
  5. 若命中 cooldown,则跳过坏线路
  6. 最终记录本次实际选择与原因

所以"自动"的含义其实是:

  • 绑定关系由设置给出
  • 执行策略由运行时规则负责

为什么这两层划分很重要

因为它能把系统复杂度控制住:

  • 没有第一层,系统会变成不可控黑盒
  • 没有第二层,系统又只是静态映射,谈不上 Auto

换句话说:

这不是一个纯配置系统,也不是一个纯智能系统,

而是一个"设置绑定 + 运行时规则"的混合设计。

一句最直白的话

如果要用最短的话解释这套方案,可以直接写成:

不同任务可以绑定不同模型,而自动路由做的事情,是在运行时按规则使用这些绑定,并在失败时自动切到备用模型。


4.3 系统视角:从请求到路由决策

下面这张图描述的是我建议的 本地优先 Auto 路由 的基本流程。
主对话 / Agent
followup / lifePulse / 轻任务








用户发起请求
调用场景是什么?
读取该场景主配置
读取该场景默认档位
构建候选链: primary + fallbacks
是否有 cooldown 中的 provider?
跳过冷却中的候选
直接尝试首个候选
请求成功?
记录 Routing Decision 并返回结果
错误是否可 fallback?
保留错误并返回
切到下一个候选
还有候选吗?

这个流程的重点不在"聪明",而在:

  • 先有一条稳定主链
  • 失败后能往后切
  • 切换有据可查

4.4 两阶段路线为什么适合作为文章主线

如果要写成文章,我建议把核心叙事写成下面这个结构:

  1. 先承认现实
    • SkillLite 不是 Cursor 那种托管 Auto,也不是 OmO 那种完整路由编排系统。
  2. 先解决最痛的问题
    • 最痛的是"请求直接挂掉",不是"少花 5% token"。
  3. 把成本优化放到低风险任务
    • 这样既能省钱,又不把主链路质量压坏。
  4. 刻意延后真正的黑盒智能
    • 因为那一层最贵、最难测、最难解释。

这会让文章更有说服力,因为它讲的是 为什么这样演进,而不只是"我想加一个 Auto 功能"。


5. 推荐演进路线

Phase 0:当前能力(已具备)

目标 :让用户手动把不同固定场景映射到不同 profile。
特点 :简单、透明、成本低。
限制

  • 不看输入内容
  • 不看 API 实时可用性
  • 不会自动 fallback

这是一个合适的起点,但不是"Auto"。

Phase 1:可靠性优先的自动 fallback(MVP-A 已落地)

实现状态:followup / lifePulse / 自进化状态与触发等 非流式 invoke 已接入;流式 skilllite_chat_stream 当前仅尊重场景映射,不在流中途切换。详见 tasks/TASK-2026-038-llm-scenario-fallback

目标

让关键请求在主模型失败时 自动切换到备用模型,先解决"别挂"问题。

建议能力

  • 每个关键场景支持:
    • primary_profile_id
    • fallback_profile_ids[]
  • 触发 fallback 的条件:
    • HTTP 429
    • HTTP 5xx
    • 连接错误 / provider 不可达
    • 请求超时
  • cooldown_seconds
    • 一条坏链路短时间内不要反复重试
  • 可选 UI 提示:
    • "主模型不可用,已切换到备用模型"

为什么先做这个

  • 最贴近用户价值
  • 不需要复杂分类系统
  • 可以在现有 llmProfiles 与场景映射之上增量实现

Phase 1 流程图


否: 429 / 5xx / timeout / network


主对话请求
选择 primary profile
成功?
返回结果
写失败记录
切到 fallback profile
成功?
返回结果 + 标记已降级
继续尝试下一个 fallback 或报错

Phase 2:低风险场景省钱

目标

在不影响主交互体验的情况下,系统性地降低辅助链路成本。

推荐做法

  • followup 固定走 cheap
  • lifePulse / 后台轻状态同步固定走 cheap
  • 简单摘要、标题、轻量结构化生成优先走 cheapbalanced
  • 主对话 / Agent 默认走 balancedstrong

核心原则

省钱应优先发生在:

  • 失败代价低
  • 对推理深度要求低
  • 可容忍偶发质量波动

的场景,而不是直接压主对话。

Phase 2 流程图

低风险: followup / title / status
中风险: 普通主对话
高风险: 复杂 Agent / 长上下文
收到请求
场景风险等级
走 cheap tier
走 balanced tier
走 strong tier
若失败可升到 balanced
若失败切同档位或升 strong
若失败切同能力不同 provider

Phase 3:输入复杂度启发式路由

目标

让系统不只看"调用点",还初步看"这次请求像不像复杂任务"。

可用的轻量启发式

  • 是否带图片
  • 输入长度 / 上下文长度
  • 是否进入 Agent/tool-heavy 流程
  • 是否包含代码修改请求
  • 是否请求分析架构、排障、长文总结

路由方向

  • 简单请求 -> cheap
  • 中等请求 -> balanced
  • 复杂请求 -> strong
  • 视觉请求 -> vision

注意

这一步依然应以 规则 为主,而不是先加二次 LLM 分类。

规则虽然"笨",但足够可解释,也更容易调参。

Phase 4:受控的"类 Auto"决策

目标

开始接近 Cursor Auto / OmO 那类体验,但保持本地优先和可解释。

可能引入的能力

  • 小模型预分类:
    • 先判断请求属于 simple / normal / complex / visual / risky
  • 动态健康度:
    • 最近 N 分钟内某 provider 的失败率、超时率、降级次数
  • 决策理由枚举:
    • manual
    • scenario-default
    • complexity-upgrade
    • runtime-fallback
    • provider-cooldown-avoidance

风险

  • 每次请求前若都额外分类,会增加延迟和 token 成本
  • 黑盒程度上升,排障变难
  • 用户可能不理解"为什么这次不是我选的那个模型"

因此这一步必须在前几阶段稳定后再考虑。


6. MVP 建议(最小可行版)

如果只做一版最值得上线的能力,建议是:

MVP-A:可靠性版 Auto

只包含:

  1. 保留现有固定场景映射
  2. 为关键场景增加 fallback 列表
  3. 在常见错误下自动切换
  4. 本地记录"本次切换原因"

MVP-A 为什么是最合理的起点

因为它同时满足四个条件:

  1. 解决真实问题
    • 用户感知最强的是"挂了没有"和"能不能自动恢复"。
  2. 不需要重新定义整个配置系统
    • 仍然以现有 llmProfiles 为中心。
  3. 可以最小化 UI 学习成本
    • 用户只需要理解"主模型 + 备用模型"。
  4. 容易做成文章里的清晰故事
    • 从"固定路由"升级到"稳定优先 Auto",叙事非常自然。

MVP-A 的收益

  • 用户感知直接
  • 技术风险低
  • 与现有结构最兼容
  • 最能回答"为什么这次没挂"

MVP-A 不做

  • 不做智能分类
  • 不做健康度全局评分
  • 不做云端调度

MVP-A 代码示意

下面是一个文章里可以直接使用的 伪代码 / TypeScript 风格示意,表达的不是最终实现细节,而是设计思想:

ts 复制代码
type RouteScenario = "agent" | "followup" | "lifePulse" | "evolution";

type RouteAttemptReason =
  | "primary"
  | "runtime-fallback"
  | "cooldown-skip";

interface SavedProfileRef {
  id: string;
  provider: string;
  model: string;
  apiBase: string;
}

interface ScenarioRouteConfig {
  primaryProfileId: string;
  fallbackProfileIds: string[];
  cooldownSeconds: number;
}

interface RoutingDecision {
  scenario: RouteScenario;
  selectedProfileId: string;
  reason: RouteAttemptReason;
  triedProfileIds: string[];
}

async function runWithFallback(
  scenario: RouteScenario,
  route: ScenarioRouteConfig,
  request: unknown
): Promise<{ decision: RoutingDecision; result: unknown }> {
  const candidates = [route.primaryProfileId, ...route.fallbackProfileIds];
  const tried: string[] = [];

  for (const profileId of candidates) {
    if (isCoolingDown(profileId)) {
      continue;
    }

    tried.push(profileId);
    try {
      const result = await callLlm(profileId, request);
      return {
        decision: {
          scenario,
          selectedProfileId: profileId,
          reason: profileId === route.primaryProfileId ? "primary" : "runtime-fallback",
          triedProfileIds: tried,
        },
        result,
      };
    } catch (err) {
      if (!isRetryableLlmError(err)) {
        throw err;
      }
      markCooldown(profileId, route.cooldownSeconds);
    }
  }

  throw new Error("All route candidates failed");
}

这段代码传达的核心理念是:

  • 决策不复杂
  • fallback 条件明确
  • 错误可分类
  • 切换可追踪
  • 行为可解释

6.1 再往后走时,代码形态会怎么变化

如果继续做 Phase 3 / Phase 4,代码会从"按场景查配置"逐步变成"先做决策,再执行请求":

ts 复制代码
interface RequestSignals {
  scenario: RouteScenario;
  hasImages: boolean;
  inputChars: number;
  estimatedContextChars: number;
  toolHeavy: boolean;
}

type RouteTier = "cheap" | "balanced" | "strong" | "vision";

function pickTierBySignals(s: RequestSignals): RouteTier {
  if (s.hasImages) return "vision";
  if (s.toolHeavy) return "strong";
  if (s.estimatedContextChars > 120_000) return "strong";
  if (s.inputChars < 400 && !s.toolHeavy) return "cheap";
  return "balanced";
}

注意:这一步虽然还是规则,但已经明显比 Phase 1 更复杂;

再往后如果引入小模型预分类,就会从"规则系统"进入"半智能决策系统"。


7. 推荐的默认策略

7.1 场景默认档位

场景 默认档位 理由
主对话 / Agent balanced 保持质量与成本平衡
长上下文复杂问题 strong 降低误判、返工和重试
猜你想问 / followup cheap 容错高、结果短
Life Pulse / 轻后台任务 cheap 成本敏感,失败影响低
图片 / 多模态 vision 明确能力要求
手动触发自进化 / 审核类复杂分析 strong 更偏复杂 reasoning

7.2 fallback 原则

  • cheap 失败后可升到 balanced
  • balanced 失败后可切同档位其他 provider,或升到 strong
  • strong 失败后先切同能力不同 provider,再考虑降级
  • 不建议从 strong 直接降到明显弱很多的模型继续执行高风险 Agent 流程

8. 配置理念

建议继续走 本地优先

  • 用户保存多个 llmProfiles
  • 在 UI 中为不同档位或场景选择 profile
  • 自动策略只决定"用哪一档 / 哪一条",不代替用户保存密钥

为什么不先上云端

  • SkillLite 当前更适合 BYOK 与本地自治
  • 云端路由会引入:
    • 统一鉴权
    • 配额协调
    • 策略托管
    • 隐私边界问题

这会让问题从"产品优化"升级为"平台建设"。


8.1 一个适合文章表达的架构分层

如果要对外发布文章,我建议把架构分成这四层来讲:

第 1 层:用户配置层

  • 用户保存多个 llmProfiles
  • 为场景或档位绑定 profile

第 2 层:路由决策层

  • 根据场景、规则、错误状态选择候选链

第 3 层:执行与回退层

  • 调用 LLM
  • 识别错误
  • 自动 fallback
  • 维护 cooldown

第 4 层:可观测与解释层

  • 记录这次为什么这样选
  • 告诉用户是否发生切换

这样写比直接堆功能点更像一篇成熟的技术文章。
用户配置层
路由决策层
执行与回退层
可观测与解释层


9. 可观测性要求

如果要做自动路由,必须留痕。

至少建议记录:

  • 请求场景
  • 原始首选 profile
  • 最终实际使用 profile
  • 是否发生 fallback
  • fallback 触发原因
  • 是否命中 cooldown

UI 最少要可见什么

  • 当前启用的是手动路由还是自动路由
  • 最近一次自动切换是否发生
  • 出错时是否切到备用模型

否则用户会觉得系统在"乱跳模型"。


10. 判断是否值得继续演进的指标

在没有云端大盘的前提下,也可以先观察本地与开发期数据:

  • 主对话请求失败率是否下降
  • 429 / 超时后的恢复率是否上升
  • followup / 后台任务的平均成本是否下降
  • 用户是否更少手动切模型
  • 是否出现更多"为什么换模型"的困惑反馈

若失败恢复率提升明显,而成本也下降,再继续做启发式与分类层。


一句话总结:

SkillLite 的 Auto 路由应当是一个"本地优先、稳定优先、可解释、可渐进增强"的系统,

而不是一开始就做成一个不可见、不可调、不可回退的黑盒模型调度器。

项目地址:Github

相关推荐
pengyi8710152 小时前
私网IP映射公网基础原理,搭配代理IP远程访问入门
linux·服务器·网络
真·skysys2 小时前
On-Policy Distillation
人工智能·深度学习·机器学习
学弟2 小时前
【内涵】深度学习中的三种变量及pytorch中对应的三种tensor
人工智能·pytorch·python
AILabNotes2 小时前
014、隐私增强技术:零知识证明与混合网络在网关中的应用
网络·区块链·零知识证明
xwz小王子2 小时前
多视角视频扩散策略:一种三维时空-觉察视频动作模型
人工智能·音视频
我是无敌小恐龙2 小时前
Java SE 零基础入门Day01 超详细笔记(开发前言+环境搭建+基础语法)
java·开发语言·人工智能·opencv·spring·机器学习
Ww.xh2 小时前
规避GCJ02偏移的坐标统一方案
人工智能
深圳市九鼎创展科技2 小时前
MT8883 vs RK3588 开发板全面对比:选型与场景落地指南
大数据·linux·人工智能·嵌入式硬件·ubuntu