OmniRoute Auto-Combo 引擎解析:基于自适应评分与自愈机制的自管理模型链
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
Auto-Combo 是 OmniRoute 网关中的"自管理模型链"能力:无需手工维护 provider 优先级列表,引擎对每个请求实时计算候选 provider/model 的加权评分并动态选择最优目标,同时叠加熔断感知、临时排除、故障注入(chaos)与 bandit 探索等机制,保证路由在故障、配额耗尽与成本波动下仍能自动收敛。读完本文,你将理解 Auto-Combo 的评分模型、Mode Pack 权重档位、自愈阈值参数、Bandit 探索与预算上限的实现细节,并掌握零配置auto/前缀路由与持久化 auto combo 两种使用方式及对应 API 的调用方法。
一、核心机制:多因子加权评分
Auto-Combo 引擎为每个请求动态挑选最优 provider/model,其基础是一个多因子评分函数。按文档给出的经典模型,评分由 6 个核心因子构成:
| 因子 | 权重 | 含义 |
|---|---|---|
| Quota(配额) | 0.20 | 剩余容量,取值 [0..1] |
| Health(健康度) | 0.25 | 熔断器状态:CLOSED=1.0,HALF_OPEN=0.5,OPEN=0.0 |
| CostInv(成本反向) | 0.20 | 成本越低得分越高 |
| LatencyInv(延迟反向) | 0.15 | p95 延迟越低得分越高 |
| TaskFit(任务匹配) | 0.10 | 模型 × 任务类型适配分 |
| Stability(稳定性) | 0.10 | 延迟/错误方差越小得分越高 |
在当前仓库的源码中,该评分模型位于 scoring.ts。可以确认几个关键实现细节:
- 权重归一化:
normalizeScoringWeights()会把用户自定义权重清洗(负数、NaN 一律置 0)后归一化为总和为 1 的分布;若全部为 0 则回退到默认权重。这保证了任何合法配置下评分公式都定义良好。 - 因子契约 [0,1]:
calculateFactors()中每个因子都经过clamp01()限界。例如quota: clamp01(candidate.quotaRemaining / 100)、costInv: clamp01(1 - costPer1MTokens / maxCost)(相对池内最大成本归一)、latencyInv: clamp01(1 - p95LatencyMs / maxLatency)、stability: clamp01(1 - latencyStdDev / maxStdDev)。注释明确说明:单个脏遥测输入(负配额、NaN)不能产生越界因子来扭曲加权分。 - 池级最大值只算一次:
computePoolMaxima()在 scoring.ts#L278-L288 中对整个候选池一次遍历求出 maxCost/maxLatency/maxStdDev。源码注释解释了原因:零配置的autocombo 可把候选池扩展到上千个 provider/model 目标,若在逐候选循环里重复计算会使 O(n) 评分退化为 O(n²),曾导致进程 OOM。 - 防 NaN 排序:
calculateScore()的最终结果经clamp01()限界,NaN 因子被映射为 0,避免 NaN 参与排序产生不确定结果(见 scoring.ts#L160-L186)。
值得指出的是,当前仓库中的评分函数已从文档的 6 因子扩展为16 因子。DEFAULT_WEIGHTS(scoring.ts#L62-L88)在保留上述 6 个核心因子的基础上,加入了tierPriority(账号档位优先级,Ultra=1.0/Pro=0.67/Standard=0.33/Free=0.0)、tierAffinity、specificityMatch、contextAffinity、sessionAvailability、connectionDensity(同 provider 多连接间的负载分散)、cacheAffinity、resetWindowAffinity、quality(路由事件质量追踪器给出的反馈信号,冷候选默认中性 0.5)与reliability(观测成功率1 - failureRate,无观测候选按 1.0 计——"没失败过"而非"中性")等信号。原文档中的 6 因子表可以理解为这套模型的"核心子集":quota/health/costInv/latencyInv/taskFit/stability在DEFAULT_WEIGHTS中分别占 0.1429/0.1605/0.1429/0.1143/0.0762/0.0476,健康度(health)与配额(quota)仍是最重的两项投票,与原文档强调的"可用性优先"取向一致。
评分与选择的主入口是scorePool():对池内每个候选计算因子 → 加权求分 → 按分数降序排序(scoring.ts#L356-L383)。
二、Mode Pack:预设权重档位
Mode Pack 是一组"整体替换默认权重"的预设档位,用于把选择偏向某一目标。文档列出的 4 个经典档位及其关键权重如下:
| Pack | 取向 | 关键权重 |
|---|---|---|
| Ship Fast | 速度优先 | latencyInv: 0.35 |
| Cost Saver | 经济优先 | costInv: 0.40 |
| Quality First | 模型质量优先 | taskFit: 0.40 |
| Offline Friendly | 可用性优先 | quota: 0.40 |
在当前仓库中,这些档位实现在 modePacks.ts 的MODE_PACKS表里,共6 个档位(4 个经典档位 +reliability-first+chaos-mode),且已对齐 16 因子结构。核心数值(节选自源码):
| 因子 | ship-fast | cost-saver | quality-first | offline-friendly | reliability-first | chaos-mode |
|---|---|---|---|---|---|---|
quota | 0.1133 | 0.1133 | 0.0752 | 0.3324 | 0.1133 | 0.0376 |
health | 0.2667 | 0.1810 | 0.1714 | 0.2667 | 0.3524 | 0.4000 |
costInv | 0.0276 | 0.3324 | 0.0276 | 0.0752 | 0.0181 | 0.0140 |
latencyInv | 0.3048 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0186 |
taskFit | 0.0952 | 0.0952 | 0.3524 | 0.0000 | 0.0952 | 0.1905 |
stability | 0.0000 | 0.0476 | 0.1429 | 0.0952 | 0.1905 | 0.1714 |
可以看到原文档的语义在源码中被忠实保留:ship-fast以 latencyInv 0.3048 + health 0.2667 主导(低延迟、健康连接);cost-saver以 costInv 0.3324 主导(最便宜 token 胜出);quality-first以 taskFit 0.3524 + stability 0.1429 主导(任务最适配且表现一致);offline-friendly以 quota 0.3324 + health 0.2667 主导(不管快慢贵贱,先保证有量可用)。每个档位权重总和均为 1.0,因此normalizeScoringWeights()在档位生效时几乎无需修正。
档位通过getModePack(name)按名取用,getModePackNames()列出全部可用档位名。引擎侧(engine.ts 的AutoComboConfig)以modePack?: string字段承载该选择,未知档位会回退到默认权重。
三、Self-Healing:临时排除、探针恢复与事故模式
自愈层由 selfHealing.ts 的SelfHealingManager实现,其常量定义与文档描述完全对应:
const DEFAULT_COOLDOWN_MS = 5 * 60 * 1000; // 5 min const MAX_COOLDOWN_MS = 30 * 60 * 1000; // 30 min const REENTRY_THRESHOLD = 0.3; const EXCLUSION_THRESHOLD = 0.2; const INCIDENT_MODE_THRESHOLD = 0.5; // >50% OPEN四个自愈行为逐一对照源码:
- 临时排除(progressive backoff):
evaluate()中,若候选得分score < 0.2则写入排除表;再次被排除时冷却时间翻倍Math.min(existing.cooldownMs * 2, MAX_COOLDOWN_MS)——即首次 5 分钟,后续 10、20、30、30…分钟封顶,与文档"excluded for 5 min (progressive backoff, max 30 min)"一致。 - 熔断器感知:
circuitBreakerState === "OPEN"的候选自动排除(理由标记为 "Circuit breaker OPEN");处于排除中且熔断器为HALF_OPEN的候选会放行并记为探针请求(probeCount++,返回isProbe: true)。 - 事故模式(Incident mode):
updateIncidentMode()统计池内熔断器状态,当OPEN占比> 50%时置位 incident mode。引擎侧(engine.ts#L293-L299)在 incident mode 下直接把探索率清零:const effectiveExplorationRate = incidentMode ? 0 : config.explorationRate;,即"禁用探索、最大化稳定"。 - 冷却恢复(probe):被排除候选重新入池走
recordProbeResult()——连续成功探针达到 3 次才完全解除排除;任一探针失败则再次把冷却翻倍并重置探针计数。重入还需满足score >= 0.3(REENTRY_THRESHOLD,比排除阈值 0.2 更严格,形成滞回,避免分数在边界附近抖动时反复进出池)。
getStatus()会输出当前排除数量、事故模式与各候选的剩余冷却毫秒数,可用于运维观测。该管理器是进程内单例(getSelfHealingManager()),排除状态保存在内存 Map 中,不落库。
四、Bandit 探索与预算上限
Bandit 探索:文档说明"5%(可配置)的请求路由到随机 provider 用于探索,事故模式下禁用"。在 engine.ts 中对应两处代码:AutoComboConfig.explorationRate默认注释即为0.05 = 5% exploratory;选择阶段:
const effectiveExplorationRate = incidentMode ? 0 : config.explorationRate; let selected: ScoredProvider; const isExploration = Math.random() < effectiveExplorationRate && candidates_.length > 1; if (isExploration) { const idx = Math.floor(Math.random() * candidates_.length); selected = candidates_[idx]; // 随机探索 } else { const rotator = getRotator(config.name); selected = rotator.pick(candidates_); // 常规打分选择 }SelectionResult.isExploration会把本次选择是否属于探索回传调用方,便于日志区分"利用"与"探索"路径。
预算上限(Budget cap):引擎还支持budgetCap(每请求最大成本,USD)。估算成本 =costPer1MTokens / 1_000_000 × estimatedInputTokens(缺省按 1000 token 估算)。若选中候选超预算:
- 先过滤出预算内的候选重新选择;
- 若全部候选都超预算,则由
budgetFallback决定策略:"cheapest"(默认,回退到全局最便宜候选,容忍超支)或"strict"(抛出BudgetExceededError,让调用方返回明确的"超预算"响应而非静默超支)。
BudgetExceededError携带budgetCap与cheapestCostUsd两个字段(engine.ts#L54-L65),便于客户端构造可读的错误提示。
五、使用方式与 API
Auto-Combo 有两种消费方式,文档中的 API 章节与仓库现状如下。
5.1 文档中的 REST 示例
# 创建 auto-combo curl -X POST http://localhost:20128/api/combos/auto \ -H "Content-Type: application/json" \ -d '{"id":"my-auto","name":"Auto Coder","candidatePool":["anthropic","google","openai"],"modePack":"ship-fast"}' # 列出 auto-combos curl http://localhost:20128/api/combos/auto需要说明的是,以当前仓库为准:route.ts 目前只实现了GET /api/combos/auto——它通过createVirtualAutoCombo()现场构建虚拟 combo,列出每个变体的名称、解析出的候选池(candidatePool)与候选数量,并要求客户端按池内窗口的 MAX 值上报context_length/max_output_tokens(而不是 0,避免客户端误判上下文上限)。POST创建则统一走常规 combo 端点POST /api/combos并设置strategy: "auto":
# 持久化 auto combo(strategy 为 auto,权重与候选池放在 config 中) curl -X POST http://localhost:20128/api/combos \ -H "Content-Type: application/json" \ -d '{"name":"Auto Coder","strategy":"auto","config":{"auto":{"candidatePool":["anthropic","google","openai"],"weights":{"quota":0.15,"health":0.3,"costInv":0.05,"latencyInv":0.35,"taskFit":0.1,"stability":0}}}}'5.2 零配置auto/前缀路由(推荐)
不需要创建任何 combo,直接在任意 OpenAI 格式客户端的model字段中写:
model: "auto" # 平衡默认 model: "auto/coding" # 编码任务取向 model: "auto/fast" # 低延迟取向 model: "auto/cheap" # 成本最优取向 model: "auto/offline" # 配额余量优先 model: "auto/smart" # 质量优先 + 更高探索率 model: "auto/lkgp" # Last-Known-Good Path model: "auto/chaos" # 故障注入(韧性测试)前缀解析在 autoPrefix.ts 中:parseAutoPrefix("auto")返回{valid: true, variant: undefined};auto/<已知变体>返回对应variant;autocoding、auto/unknown等格式非法输入返回{valid: false, error}而不会误入自动路由。处理链路为:src/sse/handlers/chat.ts检测到auto/前缀 → 调用 virtualFactory.ts 的createVirtualAutoCombo()从当前所有活跃 provider 连接构建候选池(过滤无有效凭据的连接,交叉核对 provider 注册表的模型可用性与定价)→ 在内存中生成AutoComboConfig→ 走与持久化 combo 相同的handleComboChat()引擎。虚拟 combo 每请求重建、零持久化开销,新增 provider 连接会立即扩入候选池。
引擎侧还按 combo 名称套用分层轮换偏好(TIER_PREFERENCES,见 engine.ts#L79-L85):如coding变体对高分层(top)候选的偏好权重为 0.6,fast变体为 0.3、对中间层(mid)偏好 0.5——这与各变体"质量/速度取向"的语义互相印证。
六、Task Fitness:模型 × 任务类型适配表
taskFit因子依赖 taskFitness.ts 中的适配度查找表:对30+ 个模型在 6 种任务类型(coding、review、planning、analysis、debugging、documentation)上给出 [0,1] 分数,并支持通配符模式(例如*-coder匹配到高 coding 分)。
从源码结构看,其解析是一条多层优先级链(文件头部注释明确列出):
- 用户覆盖(DB
model_intelligence,source='user_override'); - Arena ELO实时排名(
source='arena_elo',受ARENA_ELO_SYNC_ENABLED开关控制); - models.dev 能力层级(由
model_capabilities表派生,并带"厂商已退役模型一票否决"); - 静态
FITNESS_TABLE——源码注释强调该表刻意保持很小且只收录带版本号的模型 id(如o3: 0.95、gemini-2.5-pro: 0.92、deepseek-r1: 0.88、glm-5.1: 0.78),避免用无版本族的子串匹配去给厂商已退役的模型打分; - 通配符提升——在第 4 层未命中时,在 0.5 中性基线上做模式匹配加成。
表头注释还给出了一条重要语义:未命中任何层的模型落到0.5 基线,表示"没有证据",而不是"平庸模型",评分引擎对冷候选同样采用中性处理(如quality因子缺失默认 0.5),确保新 provider 既不被抬轿也不被惩罚。
七、关键文件索引
文档给出的实现文件表与当前仓库一一对应,全部位于open-sse/services/autoCombo/目录:
| 文件 | 职责 |
|---|---|
| scoring.ts | 评分函数、DEFAULT_WEIGHTS、池归一化(computePoolMaxima/scorePool) |
| taskFitness.ts | 模型 × 任务适配度查找(多层解析链 + 通配符) |
| engine.ts | 选择主逻辑、bandit 探索、预算上限、分层轮换器 |
| selfHealing.ts | 排除、探针、事故模式(SelfHealingManager) |
| modePacks.ts | 6 个权重档位(4 经典 + reliability-first + chaos-mode) |
| autoPrefix.ts | auto/前缀解析与 7 个变体枚举 |
| virtualFactory.ts | 从活跃连接构建内存AutoComboConfig |
| route.ts | GET /api/combos/auto变体发现 API |
单元测试位于 open-sse/services/autoCombo/__tests__/,覆盖评分(autoCombo.test.ts)、任务适配通配符顺序(taskFitness-pattern-order-8603.test.ts)、chaos 虚拟 combo(chaosVirtualCombo.test.ts)等。
小结
Auto-Combo 的设计可以概括为三层:评分层(16 因子加权,核心 6 因子决定"配额—健康—成本—延迟—任务—稳定"的取舍)、策略层(Mode Pack 档位 + bandit 探索 + 预算上限,允许按场景整体偏置)、保护层(临时排除渐进退避、HALF_OPEN 探针、50% OPEN 触发事故模式关闭探索)。三层全部在请求路径上以内存状态运转(虚拟 combo 零落库、排除表进程内单例),因此候选池随连接增减实时变化,路由行为随遥测(p95 延迟、错误率、配额余量、质量反馈)持续自适应。对使用者而言,最小上手动作只有一个:把客户端 model 设为auto或某个auto/<variant>,其余交给引擎。
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考