SELECT
选一次,调一次。延迟 = 路由 prefill + 一次上游补全,成本 = 一次补全。默认。
引擎
Enthalpy 为池内每一张模型卡计算一个分值,减去成本项后取最大者,并保存完整的候选表。本页说明打分函数、四种路由模式,以及泛化测量的结果。
1,138 个候选时也是这个数:W 和 P 的形状里没有「模型数」这一维。
四条真实查询在各成本权重下的完整候选表,录自当前 checkpoint。
同一条查询,成本权重由 0 调至 1 后所选模型发生变化。原始分不变,成本项改变,最终排序随之改变。
查询:「一行重命名」。claude-opus-4-6 的原始分最高(+15.132),预估成本也最高($0.006240)。γ 由 0 调至 1 后,其成本项为 -0.485,kimi-k2.5 为 -0.048;最终分 +14.678 高于 +14.647,差值 0.0307。
概率一列在两个 γ 下取值相同:softmax 作用于原始 logit,不受成本项影响。改变选择结果的是一个显式的、可调回零的价格项。
这不是为落地页导出的特例。网关把每一次路由都写进 route_decisions 表,无条件地:请求里的 explain 只决定候选表进不进响应体,不决定它存不存。所以客户能在控制台里打开自己发过的任意一条请求,看到的就是这张表。
打分在本地完成,不产生上游调用。一次本地前向推理决定模型归属,被选中的模型在此之后才收到请求。
模型池可配置,不是重训一个 head。
query 过一次 backbone 得到 h(q) ∈ R^1024;模型卡的描述符段落过同一个 backbone,再经 P 投影并归一化,得到 e_j ∈ R^128。打分是一个双线性内积:
s_j = (h(q)ᵀ W) · e_j
关键在于 W 和 P 的形状里没有「模型数」这一维。模型是输入,不是参数。
| 张量 | 形状 | 参数 |
|---|---|---|
P | 1024 × 128 | 131,072 |
W | 1024 × 128 | 131,072 |
b_acc | scalar | 1 |
w_acc | 1024 | 1,024 |
| 合计 | 263,169 |
checkpoint 里另有 8 组、共 8,192 个浮点数,是这 8 段描述符的 backbone 输出缓存。它们是输入的缓存,不是权重 —— 把它们算进参数量,等于承认 head 会随模型数增长,而那正是这个架构要否掉的说法。
增删一个模型只改描述符文本和它的缓存向量;W、P、w_acc、b_acc 一个字节都不动。池里 8 个模型时 head 是 263,169 个参数,池里上千个候选时还是这个数。
在 1,138 个候选、12 个分组上按 12.5% 留出 142 个候选,head 从未在它们上面训练过。结果:
| 切片 | 路由器 | 最强单模型 | oracle | 随机 |
|---|---|---|---|---|
val | 0.7571 | 0.7667 | 0.9758 | 0.4865 |
test | 0.7516 | 0.7637 | 0.9749 | 0.4915 |
holdout | 0.5702 | 0.6596 | 0.8668 | 0.4863 |
留出切片上 beats_best_single 是 false,gap_closed 是 -0.4313 —— 负数,意思是它比最强单模型还差。仍然为真的只有 beats_random(true)。所以:打分函数上换池不用重训,但换池之后的质量,今天没有证据,且已有的证据是反向的。上面那些正面数字,全部来自同一个池内。
同一个端点,四条路径。前两种的区别是「要不要给自己的答案打分」,后两种的区别是「谁来决定调几次」。
选一次,调一次。延迟 = 路由 prefill + 一次上游补全,成本 = 一次补全。默认。
从较低成本的候选开始,逐级向上,最多 max_attempts 次。最后一次尝试的结果始终返回;未达到阈值时,notes 中记录该情况。
planner 出一张图,executor 按深度分波并行跑。每一步只看得到原始问题 + 它的 access 列表点名的那几个更早的输出 —— 看不到别的子任务,看不到计划本身,也看不到同一波里兄弟步骤的输出。
access 只能向后指,所以上面这张图里每一条边都朝左。一张所有边同向的 DAG,本身就是那条隔离契约的证明。违反它的计划会被拒绝执行,而不是执行了再说。
请求里的 model 写了池里的真实 id,路由器整个跳过。鉴权、准入、计费、决策日志照旧。这是迁移期该用的那一档:先把一个端点切过来,继续钉着,直到你愿意相信路由。
只有 SELECT 和 PINNED 是从上游真流式的 —— 另外两种在答案存在之前不知道哪个模型会产出它,所以它们在拿到答案之后回放成 SSE,并在 notes 里说明帧的时序不是上游的时序。先流式吐出 escalate 的第 0 步再把它收回去,比等一会儿更糟。
同一类工作里,Fugu 做了三个相反的选择。下面每一条都写清楚了证据在哪,以及证据到哪儿为止。
head 的形状不随池的大小变化:263,169 个参数,在 8 个候选时是这个数,在 1,138 个候选时还是这个数。加一个模型要写的是一段描述文字,不是重训一个分类头。
这一条是可核对的机械事实。它不等于「换了池质量也一样」—— 后面那句被测过,142 个从未训练过的候选上没成(上面第 3 节印着三条切片的原始数字)。把两句话分开说,是因为它们的证据强度差了一个数量级。
γ 直接进排序:adjusted = logit − γ·log1p(est_cost / c_ref)。不是先选完再看贵不贵,也不是一个 0–1 的比例旋钮 —— 它的单位是 logit,所以它和模型的把握度在同一个量纲上较劲,谁赢是可以算出来的。
证据是第 4 节那条前沿曲线,包括出厂默认值为什么是 0:γ=0.3 时胜势的置信区间跨过了零。
route_decisions 是无条件写入的。请求里的 explain 只决定这次响应体里带不带候选表,不决定存不存 —— 所以任何一个历史请求,事后都能在控制台里打开完整的候选分数、每一次 attempt(含失败的和被 accept 头拒掉的)、以及精确到 1e-8 美元的计费金额。
Fugu 的决策是 not exposed by design。决策记录是无条件写入的。
4ROUTER / 系统研究
理解算子、KV 状态与调度怎样共同决定一条推理路径。
在 Resources 中阅读系统笔记 ↗接入平台,或讨论模型、性能与部署方案。