2026 年 9 月。这里的每个数字都来自基准测试,并且bash experiments/bench.sh --no-record在没有 API key 的情况下重新运行它们。
如果你用Jev来对文本分类,每个答案都是一次对某个供应商的网络调用,在任何负载下大约 300 ms 返回。对于批处理任务来说这没问题。但对于一个决定、行动、再决定的智能体循环,或者一个游戏 tick,或者任何先分类再行动的场景,每步 300 ms 就是全部预算。
Jevstiller 位于该调用之前,从 Jev 自己的答案中学习一个小型本地模型,并让它在 CPU 上以大约 15 ms 回答它有把握的内容。有趣的部分不是这个小模型,而是这份契约:
设定一个数字,比如 98%。Jevstiller 至少在那么多比例的请求上返回 Jev 本会返回的标签。
这篇文章讲的是要让这句话成立需要付出什么,为什么挑选置信度阈值的显而易见的方法无法让它成立,以及它的代价是什么。
它具体训练什么
一个冻结的句子编码器(bge-small,384 维,CPU 上的 ONNX Runtime)对每个请求进行嵌入。在此基础上,Jevstiller 训练一个多项逻辑回归头,一个线性层加一个 softmax,用交叉熵针对 Jev 对任务标签的完整概率分布(而非仅其最高标签)进行训练,使用全批量 Adam 并在验证切片上早停。这就是整个模型:几百 KB,在几千行数据上几秒内训练完成,每新增 2,000 条 Jev 答案就重新训练,带版本管理,并在上线前在实时流量上进行影子测试。
它旁边有两个小东西决定该头何时可以作答:一个基于训练嵌入的 k 近邻分布外评分器,以及一个路由策略,即一个置信度阈值加一个 OOD 截断值,在留出的 IID 划分上校准,并带有下文所述的界限。高于阈值且位于训练分布内的请求会在大约 15 ms 内得到该头的答案;其他所有请求都交给 Jev。代码路径见 DESIGN.md §7.3 至 §7.6。
这份契约,一句话
每个任务,在一段流量窗口内,记c本地模型作答的请求占比(覆盖率),以及e那些标签与 Jev 不同的比例。它不回答的请求会交给 Jev,按定义与 Jev 一致。因此系统与 Jev 的一致率为
A = 1 − c · e
你的目标A*给出一个预算β = 1 − A*:占比所有最终可能得到 Jev 不会给出的答案的请求。在 98% 时,即 100 个中有 2 个。路由器的任务是尽可能多地回答,同时保持c · e低于β。
这句话中有两件事被刻意省略了。它没有提及模型相对于真相的准确率:如果 Jev 错了,本地模型也会以同样的方式出错,而状态报告会在每个数字旁边说明这一点。而且它陈述的是所有请求上的一致率,而非本地模型在其选择回答的请求上的准确率。第一种表述是你可以验证的;第二种是大多数工具所报告的。
显而易见的方法,以及它为何失效
这类级联的常见做法:留出一些数据,扫描置信度阈值,保留测得的不一致率在预算内且最宽松的那个,然后上线。这是你一个下午就能写出来的东西,也是我们基准测试中采用的点估计规则。
然后它大约有一半的时间会超出预算。在五个公开任务上,每个任务二十次随机的训练/校准/测试划分,点估计规则在每个任务 20 次划分中有 6 到 12 次超出 2% 的预算,最多超出整整一个百分点:
| 任务 | 规则 | 覆盖率 | 不一致率,均值 | 最差 | 预算被突破 |
|---|---|---|---|---|---|
| Banking77(77 个意图) | 点估计 | 79.8% | 1.90% | 2.70% | 9 / 20 |
| Banking77 | 边界 | 74.9% | 1.15% | 1.55% | 0 / 20 |
| CLINC150(151 个意图) | 点估计 | 84.3% | 2.09% | 2.85% | 12 / 20 |
| CLINC150 | 边界 | 78.9% | 1.25% | 1.80% | 0 / 20 |
| AG News(4 个类别) | 点估计 | 90.3% | 2.06% | 2.65% | 11 / 20 |
| AG News | 边界 | 86.5% | 1.34% | 1.75% | 0 / 20 |
| TweetEval 情感 | 点估计 | 28.2% | 1.99% | 2.60% | 8 / 20 |
| TweetEval 情感 | 边界 | 24.0% | 1.46% | 2.10% | 1 / 20 |
| TweetEval 攻击性 | 点估计 | 36.8% | 1.81% | 2.70% | 6 / 20 |
| TweetEval 攻击性 | 边界 | 28.7% | 1.10% | 1.40% | 0 / 20 |
这不是该做法的缺陷。这就是在有限数据上选择最宽松的通过阈值所导致的结果。任何阈值下测得的不一致率都是对真实值的有噪声估计。选择最宽松的阈值,使其看起来在预算内的选择中,会优先选择一个噪声恰好指向下方的阈值。在下一批流量样本上,噪声指向别处,而那个测得 1.9% 的阈值实际交付了 2.7%。
在 100 次划分中,该界限有一次突破了预算,为 2.10%。这一次是预期之内的:该界限是一个 95% 的陈述,因此 100 次中约有 5 次可能落空。点估计根本不是一个陈述。
Jevstiller 转而采用的做法
四个决策,每一个都不大,合在一起使该契约对进入生产的每一个本地模型版本都以至少 95% 的概率成立。
1. 损失就是契约本身。对于 IID 校准集的每一行,由 Jev 标注且从未用于训练,如果本地模型会回答它并且会与 Jev 不一致,则记 1 分,否则记 0 分。该指示变量在所有行上的平均值恰好就是“所有请求上的不一致率”,即预算所限制的量。它服从二项分布,因此具有精确的置信界限,即 Clopper–Pearson,无需近似,也无需诉诸大样本。
2. 在固定网格上,从最严格的阈值开始测试,并在第一次失败处停止。该比率只会随着阈值放宽而增大,因此从最严格的候选走到最宽松的候选,逐一检查每个阈值的上界是否超出预算,并在第一个失败的阈值处停止,就能以同样的 5% 控制选错的概率,且无需因测试了多个候选而做校正。这就是固定序列检验,如Learn Then Test中所述。网格在看到任何校准行之前就已固定;校准行只用于检验。
3. 其他任何东西都不碰校准行。分布外门控会把不熟悉的输入无论置信度如何都发送给 Jev,它的截断值来自训练数据。候选阈值是一个固定网格。如果其中任何一个是在校准行上调出来的,那么界限就会在已经被用来选择其所界定对象的数据上计算,这正是点估计错误换了一副面孔。
4. 留出余量。阈值在预算的 85% 处拟合。然后,候选阈值必须在完整预算下,通过一个在标定行与它影子跟随的新流量合并后的第二次检查。该检查并非独立保证,因为标定行参与了阈值的选择,但它能捕捉到恰好卡在边界线上的阈值。
这个的第一版把四项中的两项搞错了:它把选择性比率作为上界,并乘以一个被当作精确值的估计覆盖率;它还保留了 200 个各自单独通过的阈值中最宽松的那个,而这和点估计一样是在对噪声进行选择。一次现有技术审查发现了这两个问题。修正后的规则在 Banking77 回放上没有任何代价:覆盖率从 70.6% 变为 70.7%。
第二个承诺:“Jev 当时不确定吗?”
Jev 对每个答案都返回一个置信度,其文档建议把低置信度视为“不确定”:交给人工、重新提问、走安全分支。一个只在它有把握时才作答的本地模型会悄悄破坏这一模式,因为本地答案永远不会以不确定的形式返回。在基准测试任务上,0.6 处的检查漏掉了 Jev 本会标记的 8% 到 37% 的标记。
从 0.4.0 起,一个任务可以携带一个confidence_floor:即 Jev 置信度低于该值时,你的代码就把答案视为不确定。随后,标定、影子测试和审计不仅会在本地答案的标签与 Jev 不同时将其计为分歧,还会在 Jev 本会以低于该下限的置信度作答时将其计为分歧。同样的 2% 预算覆盖两者。Jev 本会不确定的请求会交给 Jev,并返回它自己的置信度。这会带来覆盖率损失,在五个任务上、下限为 0.6 时损失 4 到 12 个百分点,而且除非你设置它,否则它是关闭的。
在第一天之后保持其成立
仅在晋升时给出一个上界是不够的,原因有二。本地模型会随着流量积累而重新训练,而每次重新训练都会再次花掉那 5%,所以在许多版本中总会有一些不达标。而且流量会变化,或者 Jev 的答案会变化。
所以固定的 2% 的全部无论本地模型怎么想,请求都会发给 Jev,同时记录本地模型的回答。一旦开始路由,这个审计切片就是生产环境中唯一无偏的视角:本地模型拒绝的请求按构造就是困难的那些,而在这些请求上衡量一致性,衡量的是路由器,而不是世界。审计给出了实时一致性在置信区间上的估计,按版本进行,并用每个请求实际得到的回答来评分。当它低于目标时,审计率就提高;当它自身的边界确认出现违规时,每个请求都重新发回 Jev,训练从该点重新开始。
在 24 小时浸泡测试中,一个替身 Jev 在第十二小时悄悄更改了每一个回答。随着审计发现问题,本地份额在四分钟内从 90% 降至 9%,并在 49 分钟内回到 90%,仅用变更后的回答进行训练,没有任何人干预。
覆盖率。该边界在有限数据上是保守的,而这正是关键:在上述每个任务上,它相比点估计规则牺牲了四到八个百分点的覆盖率。用 Jev 的完整概率分布而非其最高标签进行训练,能在多类别任务上挽回两到三个百分点。
目标是一个旋钮。在同样的五个任务上,把它从 98% 调到 95%,大致能让推文任务本地回答的比例翻倍,并把意图任务从约 70% 提升到 80% 出头。在整个范围内,从 99% 降到 90%,系统相对于数据集自身标签的准确率始终保持在 Jev 的一个百分点以内,因为本地模型与 Jev 不同的地方,它正确的频率与 Jev 差不多。这在这五个任务上成立;这不是一条定律。
覆盖率也反映 Jev 的一致性,而非任务的难度。在两个推文任务上,Jev 与人工标签的一致率仅为 64% 和 74%,因此它在类别边界附近的回答是有噪声的,而本地模型无法在 2% 的预算内复现噪声。它回答有把握的那四分之一,其余的都转发出去。
它不承诺什么
该界限假设校准行是阈值将被用于的流量的随机样本。如果流量构成发生变化,旧构成上的界限对新构成没有任何说明,而这正是审计的目的。一致不等于准确。而且契约是按请求而非按类别计算的:一个稀有类别可能承载了大部分分歧。按类别的预算是路线图上的计划。
带风险保证的选择性分类是Geifman 和 El-Yaniv,2017;固定序列检验来自Learn Then Test。从大模型答案中学习的级联出现在OCaTS(EMNLP Findings 2023)和Cache & Distil(ACL Findings 2024)中,但没有界限;BARGAIN为批处理建立了相同的带有限样本保证的一致契约;vCache(ICLR 2026)为语义缓存给出了类似的保证。Jevstiller 的贡献在于将这一切组合进一个运行中的系统:在持续再训练下保持的保证、永久审计、漂移检测,以及当教师模型变化时重启训练的 lineage。
gitclonehttps://github.com/tomerglick57/Jevstiller&&cdJevstiller&&pipinstalljevstiller
bashexperiments/reproduce.sh# Banking77 结果,来自 Jev 记录的答案,无需 key,约 10 分钟
bashexperiments/bench.sh--no-record# 全部五个任务以及阈值规则对比,一到两小时
Apache 2.0。或者把它放在你自己的 Jev 调用前面:docker run -d -p 8080:8080 -v jevstiller-data:/data ghcr.io/tomerglick57/jevstiller,指向TYPESAFE_BASE_URL它,并在几千个请求后读取状态报告。它会打印出所达到的界限,以及审计自那以来测量到的区间。