先贴代码。
/* 0 = 收下草稿,非零 = 退回默认路径 */
static int gate(const char *draft, const char *check_cmd)
{
int rc = run_subprocess(check_cmd); /* 跑调用方自己的测试 */
if (rc != 0)
return GATE_REJECT; /* fail closed */
return GATE_ACCEPT;
}
就这几行。没有第二次模型调用,没有打分函数,没有那个叫 evaluator 的类。检查是一段子进程,成本一次 fork,不是一轮 token。
为什么强调"外面"。大多数人一提"给模型加验证",第一反应是再叫一次模型,让它审自己刚说的话。你已经付了延迟,还要再付一遍,就为让它点个头。这叫验证的话,我宁愿不要。
闸的活是拦,不是评分。拦这个动作,越便宜越好。慢的闸等于没有闸——等它算完,草稿早用上了。
最便宜的检查,就是不需要第二次模型调用的那种。拿草稿去跑调用方自己的测试。挂了就退回大的那个。
if (gate(draft, "make test") == GATE_ACCEPT)
use(draft);
else
use(big_model(query)); /* 一退,钱就上去了 */
fail closed,拿不准就当你没过。所以 gate 必须便宜,不然你一个字都没省下。
现在满世界在吵开放权重。主张扩散的、要求发布前对强模型做强制安全测试的、提议设联邦标准机构的、签请愿要政府拿出刹车工具的——文件我都翻了,两边都自洽。但这场仗里没有一条,能告诉你本地那个七 B 下周能不能进你的流水线。
有人一句话说到点子上:权重的可得性只是一半。另一半是你能不能判断它给的答案对不对。后一半决定了那台便宜机器能不能干真实工作。
把请求都路由到本地小模型,唯一让它站得住脚的,就是后面这道闸。没有它,你只是把信任问题从供应商搬到输出上——而输出比供应商更难查。供应商你还能换,输出烂在主干里,你得靠 diff 一行行挖。
/* 第二档:跨模型一致性,多付一次便宜调用 */
if (agree(draft, cheap_critic) == 0)
...
这档贵一点,但也就"一次便宜调用"。真贵的不是这个。
真贵的是后面这个坑,我踩过类似的。
有人贴了他生产里的数据。改了一次路由行为,便宜那条路径的通过率从 4 中 3 变成 10 中 2,被当成回归,回滚。回滚配置在同一批用例上跑出来 10 中 4。三个数字,围着同一个值。
那个 flag 其实是中性的。回滚本身,成了一次没被验证过的生产变更。
问题不在 gate。gate 在自己的局部指标上一直好看——捕获率、泄漏率,挑不出毛病。问题是整体结果在悄悄下滑:你 fallback 到的那个模型,在这个具体任务上比草稿还弱。你在闸口抓得干干净净,转头把活递给一个更差的人。
gate 局部捕获率好看,整体可能已经坏了。只有把整条路径,和一个不带 gate 的基线放一起比,你才看得见。所以顺序是反的——
先确认 fallback 真的比默认路径好,再让检查尽量便宜。
反过来做,你会得到一台又快又对、整体却略微变差的机器,还很贵。
我一开始以为闸本身就是全部,把 gate 写便宜就赢了。上面那三个数把这个想法打回去了。后来我在 eval 集里永远留着一条不带 gate 的基线,就为这个。
还有一层更阴的。那次故障的根子是 primary 路径遵守了 refusal 约定,availability 路径偷偷干了别的。没有用例覆盖它。为什么没有——因为那条路径存在的全部意义,就是"和 primary 等价"。你永远不会去测一个你假设它等价的东西。
没控制臂的时候,路由上任何变化都会默认算到 gate 头上,因为当时你脑子里只有 gate。
把这些拽回政策那边。恐惧针对的是滥用:谁拿到强模型,会不会拿去干坏事。但可验证性,才是这个栈里真正碰得到滥用的那部分。一个只规定"谁有权持有权重"的框架,绕开了这件事整整一圈。
开放权重也只有在周边系统能在坏答案变成行动之前拦下它的时候,才有帮助。验证贵,或者边界模糊,开放主要就是把责任往下推。推给谁,不写在任何一份立场文件里。
说句题外话——我觉得"open"和"deployable"该分成两个词用。做研究要拿到权重来看,这个开放是必要的;谈部署,就得谈能力、来源、下游使用的可验证性。不然两边会一直绕开眼前这个东西吵。
所以给一条能落地的规则:凡是把一个更便宜的模型接进一条已经跑通的路径,先跑一遍带闸和不带闸的基线对比,别急着让检查变便宜。
闸不写在模型里。写在模型外面,一个子进程,一次 fork。
便宜的东西才拦得住人。拦不住人的安全,不是安全,是流程。