OmniRoute 免费供应商排行榜的用量可靠性:24 小时真实成功率如何补上 ELO 排序的盲区
本篇指南围绕 OmniRoute 的 Free Provider Rankings(免费供应商排行榜)功能展开,重点讲解 #11546 引入的"用量可靠性"(usage reliability)维度:排行榜不再只看 Arena ELO 模型质量分,而是叠加展示每个供应商过去 24 小时实际处理的请求数与成功率。读完本文,你将理解该功能的完整数据链路——从 API 参数、call_logs 聚合 SQL,到小样本返回"破折号"的判定规则与前端展示逻辑,并能据此做出更可靠的免费供应商接入决策。
一、背景:ELO 单维排序的盲区
OmniRoute 注册了数百个供应商,其中 150 余个目录条目标记为免费/免鉴权(no-auth、免费档 OAuth 或免费档 API key)。免费供应商的模型质量差异极大,因此 OmniRoute 用 Arena AI(LMArena 风格)ELO 分对免费供应商的模型质量打分,并在仪表盘的 Free Provider Rankings 页展示排名。
但仅凭 ELO 排序存在一个致命盲区:一个对所有请求都返回错误的供应商,只要它的模型 ELO 高,依然会排在第一位。连接状态(connection state)描述的是"此刻的凭证与限流",看不到"这个供应商是否真的在成功服务流量"。
#11546 的修复思路是:用量数据(usage data)此前已经由 API 提供,但前端从未主动请求。现在排行榜页显式请求并在表格中展示每个供应商在最近 24 小时窗口内实际服务的请求量与成功率;样本太小的供应商显示破折号(—),而不是一个没有统计意义的数字。
二、数据来源:三个真实信源的 Join
排行榜的分数体系由三个真实来源计算而成(详见 Free Provider Rankings 文档):
- 免费供应商清单:
NOAUTH_PROVIDERS,加上标记hasFree的OAUTH_PROVIDERS/APIKEY_PROVIDERS条目(定义于 src/shared/constants/providers.ts)。 - 模型目录:来自 provider registry(open-sse/config/providerRegistry.ts),并合并运营者手工添加的自定义模型(src/lib/freeProviderRankings.ts 中的
mergeProviderModels,注册表条目在 ID 冲突时优先)。 - ELO 派生的任务适配分:由 Arena ELO 同步引擎(src/lib/arenaEloSync.ts)写入
model_intelligence表,source = "arena_elo"。
Join 逻辑位于 src/lib/freeProviderRankings.ts 的 computeFreeProviderRankings:对每个免费供应商的每个模型做三级模糊匹配(findMatchingIntelligence:精确匹配 → 去除尾部版本后缀如 kimi-k2.6 → kimi-k2 → 前缀匹配),然后取最高分模型作为 Top Model、全体已评分模型的均值作为 Avg Score,供应商按 Top Model 分数降序、再按平均分排序。
ELO 分数按榜单归一化到任务适配区间 [0.4, 0.98]:
taskFit = 0.4 + 0.58 * ((elo - minElo) / (maxElo - minElo))
前端把分数渲染为人类可读标签(Elite / Excellent / Very Good / Good / Average / Below Average),因为它是相对排名质量而非百分比。
三、API 层:withUsage 与 usageRange 参数
排行榜页由公开只读端点 src/app/api/free-provider-rankings/route.ts 支撑,查询参数经 Zod 校验:
GET /api/free-provider-rankings
GET /api/free-provider-rankings?category=coding&limit=20
GET /api/free-provider-rankings?configuredOnly=1&withUsage=1&usageRange=24h
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
category |
string | (无) | default、coding、review、documentation、debugging 之一;省略返回综合排名 |
limit |
number | 50 |
钳制到 1–100,非法值回退为 50 |
configuredOnly |
boolean | false |
只保留至少配置了 1 条(激活)连接的供应商 |
availableOnly |
boolean | false |
只保留至少 1 条未耗尽、未限流的连接(隐含 configuredOnly) |
withUsage |
boolean | false |
追加每个供应商在窗口内实际服务的统计(reliability.usage) |
usageRange |
string | 24h |
可选 1h、24h、7d、30d;拼写错误会被 400 拒绝而非静默改写窗口 |
两个值得注意的实现细节:
- 布尔参数宽容解析、严格拒绝:布尔参数会把
"1"/"true"/"yes"统一转换为true(路由文件中的boolParam);而usageRange采用z.enum,注释明确写道"拒绝而非静默转换:一个拼写错误不能悄悄返回与调用方要求不同的窗口"。 withUsage是纯增量、默认关闭:因为它要付出一次对call_logs的聚合查询代价,"只关心排名的调用方不必为此买单"。同时withUsage会连带加载连接快照——源码注释指出,过去单独开withUsage会静默返回没有reliability的排名,现在needsConnectionSnapshot会自动触发快照加载。
响应形如:
{
"rankings": [
{
"id": "<provider-id>",
"name": "<provider name>",
"category": "noauth | oauth | apikey",
"topModel": {
"modelId": "<registry model id>",
"modelName": "<model display name>",
"score": 0.0,
"eloRaw": 0,
"confidence": "high | medium | low"
},
"averageScore": 0.0,
"modelCount": 0,
"reliability": {
"state": "healthy | degraded | down",
"connections": [{ "testStatus": null, "rateLimitedUntil": null, "state": "healthy" }],
"usage": {
"requests": 0,
"successes": 0,
"successRate": null,
"avgLatencyMs": null,
"lastRequestAt": null,
"windowHours": 24
}
}
}
]
}
四、用量统计的 SQL 层:getProviderUsageSince
withUsage 打开后,引擎调用 src/lib/db/callLogStats.ts 中的 getProviderUsageSince(since),在 call_logs 上做单窗口聚合:
SELECT
c.provider,
COUNT(*) as requests,
SUM(CASE WHEN c.status >= 200 AND c.status < 400 THEN 1 ELSE 0 END) as successes,
ROUND(AVG(c.duration)) as avgLatencyMs,
MAX(c.timestamp) as lastRequestAt
FROM call_logs c
WHERE c.provider IS NOT NULL AND c.provider != '-'
AND c.timestamp >= @since
AND EXISTS (
SELECT 1 FROM provider_connections pc WHERE pc.provider = c.provider
)
GROUP BY c.provider
从源码结构看,这条查询是有意不直接复用同文件的 getProviderMetrics()(后者带两个关联子查询,而 call_logs 仅按 timestamp 建索引,关联扫描会主导成本):这里只保留排名真正需要的四列,单次有界 GROUP BY 即可走 idx_cl_timestamp。成功率的定义与邻居查询保持一致——2xx/3xx 计为成功;EXISTS 子句则保证已删除连接的供应商不会因历史日志残留为"幽灵节点"(对应 #10714)。
窗口时长由 usageRange 决定(复用健康矩阵 RANGE_MS),默认 24h,即 changelog 中"过去 24 小时"的由来。
五、小样本规则:为什么不足 5 次请求就显示破折号
聚合结果经 src/lib/freeProviderRankings.ts 中的 attachProviderUsage 挂到每个排名的 reliability.usage 上,其中核心判定是:
successRate: row.requests >= MIN_USAGE_REQUESTS ? row.successes / row.requests : null
常量 MIN_USAGE_REQUESTS = 5(约 L279),源码注释解释得很直白:"2 次里挂 1 次不等于'坏了一半',没人调用过的供应商也不是'0% 健康'"。样本量低于 5 时 successRate 置为 null,把"没有把握下结论"与"成功率为 0"区分开。
展示侧由纯函数 formatUsageReliability(src/lib/freeProviderRankingsUsage.ts,刻意零依赖、可被客户端组件直接引用)把用量归为三种展示形态:
| kind | 触发条件 | 展示 |
|---|---|---|
rate |
successRate !== null(窗口内 ≥ 5 次请求) |
百分比数字,如 97% |
insufficient |
successRate === null 且 requests > 0(0–4 次请求) |
破折号 — |
none |
窗口内无流量或未请求 usage | 破折号 — |
百分比还按阈值着色:good(≥ 95%,绿)、fair(≥ 80%,黄)、poor(< 80%,橙);破折号场景显示为中性色,且三种形态都有各自的 tooltip 文案(完整样本数、"请求过少"、"无流量"),即 changelog 所说"too small a sample shows a dash, not a number"。
六、前端:排行榜页如何消费用量数据
仪表盘页面位于 src/app/(dashboard)/dashboard/free-provider-rankings/page.tsx/dashboard/free-provider-rankings/page.tsx),入口为 Costs → Free Provider Rankings 或直接访问 /dashboard/free-provider-rankings。页面包含:
- Top-3 领奖台(前三名免费供应商卡片);
- 完整排名表,列为 Rank / Provider / Top Model / Score / Avg Score / Reliability / Models / Type;其中 Reliability 列就是 #11546 新增的 24 小时成功率列;
- 类别过滤按钮(All Categories / Default / Coding / Review / Documentation / Debugging)、可用性开关(Configured only / Available only)、Type 过滤与按 Type 分组排序(客户端派生,不触发重新请求)。
关键改动在请求构造处:
// Always: an ELO-only ranking describes a provider that errors on every
// call as healthy. `usageRange` matches the health matrix default.
params.set("withUsage", "1");
params.set("usageRange", "24h");
也就是说,页面现在无条件附带 withUsage=1 与 usageRange=24h——这正是 changelog 所说"用量数据早已由 API 提供,但此前从未被请求"的落点:修复不是新增数据能力,而是把已有能力接进默认视图。Reliability 列与 state(连接当前状态)互补:state 读的是连接此刻的样子,看不见"每次调用都报错"的供应商,只有调用日志能看见。
七、ELO 分数体系的支撑机制
用量维度建立在 ELO 分数体系之上,理解以下机制有助于解读页面数据(完整说明见 docs/guides/FREE_PROVIDER_RANKINGS.md):
- 数据源:Arena AI 排行榜 API 的
text与code两个榜单;text映射到default/review/documentation/debugging类别,code映射到coding。 - 置信度:按 Arena 投票数分档——
high(≥ 5,000 票)、medium(≥ 1,000)、low(< 1,000)。 - 新鲜度:条目写入
model_intelligence表后 7 天过期,停止同步的供应商会自然掉出排名而不是提供陈旧数据。 - 同步开关:同步引擎默认开启,服务启动时运行一次并周期执行,非阻塞、永不致命(上游拉取失败时排行榜展示最后一次有效数据或空态)。两个环境变量(见 ENVIRONMENT.md):
| 变量 | 默认值 | 用途 |
|---|---|---|
ARENA_ELO_SYNC_ENABLED |
true |
设为 false 可关闭出站同步 |
ARENA_ELO_SYNC_INTERVAL |
86400(24h) |
同步间隔(秒) |
- 手动运维:管理端点 src/app/api/intelligence/sync/route.ts(需管理鉴权):
GET查看同步状态、POST触发手动同步(body 可传{"dryRun": true}预览)、DELETE清除全部arena_elo条目。排行榜页为空时,手动POST或重启服务即可重新填充。
八、连接状态过滤与可靠性三态
configuredOnly / availableOnly 过滤与 reliability 标注复用同一份连接快照(零额外查询)。单条连接按健康矩阵同一词汇分类(classifyConnection):
testStatus ∈ {credits_exhausted, banned, expired}→down(终态,不会自愈);rateLimitedUntil仍在未来 →degraded(限流冷却,惰性恢复);- 其余 →
healthy。
供应商级聚合规则为:所有连接 down 则 down;任一非 healthy 则 degraded;否则 healthy。需要留意的是:availableOnly 会直接丢弃无健康连接的供应商,因此在该过滤下 state 不会出现 down——down 只在单独使用 configuredOnly 时可见。
九、测试覆盖
该功能的纯函数层均有独立单测,可在仓库中直接查看验证:
- tests/unit/freeProviderRankings-usage-display.test.ts:覆盖
formatUsageReliability的none/insufficient/rate分支与色调判定; - tests/unit/freeProviderRankings-filters.test.ts:覆盖
configuredOnly/availableOnly过滤与可靠性标注的纯函数行为。
由于过滤、标注、展示判定都被拆成"完全同步、无副作用"的纯函数(filterFreeProviderRankings、attachProviderReliability、attachProviderUsage、formatUsageReliability),测试无需数据库即可断言全部边界。
十、实战建议:如何组合 ELO 与用量可靠性选供应商
- 先看类别,再看双维度:代码任务用 Coding 过滤,通用对话用 Default/All。同一供应商在不同类别排名可能不同(其 Top Model 随榜单变化)。
- Reliability 列优先于 ELO 分数做排除:一个
97%+绿色成功率 + Elite 分数的供应商是最佳接入目标;ELO 高但成功率< 80%(橙色)的供应商说明模型质量与实际交付存在落差,应谨慎或等冷却结束后复看。 - 破折号不是坏信号,是"未知"信号:
—表示窗口内无流量或请求少于 5 次,不构成失败证据;可以先小额试用再评估。 - 连接多个 Top 供应商,交给 Auto-Combo 决策:同一份 Arena ELO 数据也驱动 Auto-Combo 评分引擎的任务适配因子(open-sse/services/autoCombo/taskFitness.ts,解析顺序
user_override → arena_elo → models_dev_tier → static table)。接入头部免费供应商后,以model: "auto"(如auto/coding)发请求即可按请求质量偏好自动路由,完整 15 因子说明见 Auto-Combo 文档。 - 接入凭证参考:
NOAUTH供应商无需凭证最快接入;OAUTH/APIKEY免费档需要简单注册但常暴露更强的模型,具体步骤见 Free Tiers Guide,完整免费目录见 FREE_TIERS.md。
小结
#11546 的核心价值在于把"模型理论上有多强"(ELO)与"实际交付是否可靠"(24 小时调用日志成功率)放到同一张表里,并以严格的样本量门槛(≥ 5 次请求才给出百分比,否则显示破折号)避免小样本误导。整条链路——Zod 参数校验(route.ts)、有界窗口聚合 SQL(callLogStats.ts)、小样本置空(freeProviderRankings.ts)、展示形态判定(freeProviderRankingsUsage.ts)——均可在仓库中按上述路径逐一核对。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00