Jev 是一个出色的决策模型。Gemini Flash-Lite 是没人谈论的模型,它快速、准确,而且几乎免费。以下是我们对两者进行选择时测量到的结果,以及为什么这一组合是所有方案中最便宜的。
Jev 是当下最被炒作的模型。TypeSafe 的决策模型不聊天,也不写作。它读取一些文本和一组选项,然后返回这些选项上的校准概率。几百毫秒,几乎不花钱。我读的每个信息流里都有关于它的帖子。
我们正好有一个适合这类模型的真实任务,已经在生产环境中运行,用的是某种远没那么时髦的东西。所以与其读关于 Jev 的文章,我们把它放到自己的数据上,把两个模型各跑了几千次,然后记分。
我喜欢 Jev。但我仍在用 Gemini Flash-Lite。原因如下。
这是我在 2026 年 10 月 7 日于 AI Tinkerers NYC 所做闪电演讲的详细版本。幻灯片在Speaker Deck上。
设置
一个在代码出错时却能通过的测试,比没有测试更糟。没有测试时,你知道自己不知道。而有了虚假的绿灯,你就会发布。
在 Major League Hacking(MLH),我们用benchspec来给 AI 编程智能体打分,这是我们为此构建的开源评测运行器。你把智能体本应完成的事情写成 markdown 文件里的自然语言检查项。benchspec 在沙箱中运行智能体并对每个检查项打分,分别在启用和未启用被测技能的情况下进行,因此两者的差异就是该技能实际教会了什么。一次运行之后,检查项看起来是这样的:
- ./.meta/index.md exists
- Exactly six files match ./.meta/templates/*.md
- The summary faithfully reflects the three key facts from the source
每个检查项通过以下两种方式之一来验证。廉价代码:这条路径是否存在,有多少文件匹配这个 glob,这个文件是否有匹配某个模式的行。免费、即时、精确。或者由LLM 裁判阅读内容以判断含义。这每次运行都要花钱。
一个分类器,即路由器,读取每个检查项并选择一个检查器(file_exists、glob_count、regex,还有几个)或交由裁判处理。
路由器的职责:输入一个检查项,输出一个检查器,或一次移交。
路由器的两种错误并不对等:
- 把代码本可以处理的东西交给裁判:你损失几分钱。
- 把代码实际上无法验证的东西交给代码:错误的结果会报告为绿灯,而且没人会发现。那个信任这个绿灯的人会据此发布。我们失去一个用户的信任。钱能赚回来。信任不能。
所以:存疑时,就推迟。
秘密
我实际运行的路由器是Gemini Flash-Lite,是 Google 三档 Gemini 中最小的一档。Pro 是前沿模型。Flash 是日常款。Flash-Lite 则是 Google 自己所说的、专为“翻译和分类这类高吞吐量、对延迟敏感的任务”而打造。
Flash-Lite 拥有与更大兄弟型号相同的百万 token 上下文窗口,响应时间远低于一秒,每百万输入 token 收费 $0.30。当前版本 3.5 于 7 月发布。它在发布说明里只占了一行,也没有主题演讲。没人炒作它,但他们应该炒。
我每月 99% 的 LLM 调用都流向 Flash-Lite。这个数字来自我自己的 AI Studio 使用页面,涵盖我所有的项目。Pro 什么都没拿到。Flash 几乎没有。而且这些调用是长输入、短输出:正是分类工作的形态。
然后 Jev 出现了。文本输入,输出的是固定选项集上的概率,按设计不生成任何文字。每百万输入 token 收费 $0.042,输出免费,耗时几百毫秒。对于路由器来说,这是完美的卖点。
第一轮:你承受不起的错误
实验很简单。我们的路由器就是一次调用,只干一件事:读取一张检查项,选择一个检查器或推迟。我们把 Flash-Lite 从这个位置换下来,把 Jev 放进去,然后让两者跑同样的检查项。每个模型都有自己的提示词,其他一切不变。
这里是八个真实案例。✓ 是安全,~ 是过度谨慎(本可由代码完成却推迟了),✗ 是危险:
Jev 做出了三次危险的(假阳性)调用。有两张检查项需要读取内容以理解含义,而 Jev 把它们送给了regex。正则表达式能找到一行source:。它无法告诉你那一行是否指向正确的操作。第三个:“任何地方都没有.DS_Store”关乎整棵树,而 Jev 选了一个只看单条路径的检查器。它在一个满是.DS_Store文件的仓库上通过了。
Flash-Lite:零假阳性。两次过度谨慎,代价是几分钱(不是信任!)。
不要犯错
Jev 按字面理解。TypeSafe 自己的文档说,它回答的是你写下的问题,而不是你想问的问题。所以我重写了问题。我本以为示例能挑大梁,因为这对聊天模型有效。并没有。真正有效的是这一句话:
快速阅读,这相当于在提示工程中写“不要犯错”。以下是为什么它不是那样,以及为什么它在 Jev 上奏效。
这是一次测试,不是恳求。“小心”没有给模型任何可执行的东西。“想象这个检查器通过了。这个检查是否仍然可能为假?”是一个它可以对每个选项运行一次的程序。拿“日志条目有一行source:命名了该移动”来说。想象regex通过了。一个source:行存在。它是否仍然可能命名错误的移动?是的。所以regex出局。拿“任何地方都没有.DS_Store”来说。想象not_file_exists在./.DS_Store上通过了。子文件夹里是否仍然可能有一个?是的。出局。每个检查器都得到一个机械的答案,而机械的答案就是路由决策。
它问的是检查器的盲点,而不是检查的形状。原始提示问的是“这能否被机械地验证?”那是一个关于检查的问题,而regex看起来是机械的。重写后的提示问的是“这个检查器看不到什么?”那是一个关于工具的问题。同样的信息,相反的方向。Jev 会精确地回答你所问的,所以它给出了不同的答案。
标准也随之改变以匹配。旧标准描述了一个检查应该是什么样子:regex适用于“一个命名文件有一行匹配既定模式”。新标准描述的是检查器机械地做什么以及看不到什么:
| 检查器 | 之前 | 之后 |
|---|---|---|
regex |
该断言说一个命名文件有一行匹配既定模式或字面前缀。 | 测试一个命名文件是否有一行匹配某个模式。它无法判断匹配的文本意味着什么或指代什么。 |
not_file_exists |
该断言说单个路径已消失或从未存在过。 | 测试一个确切路径是否不存在。它只看那个路径,别处都不看。 |
skill_invoked |
一行裸激活:技能 X 被调用了。 | 测试命名技能是否被调用。它对技能做了什么一无所知。 |
示例让它变得更糟。我把我们做过的示例塞进 Jev 的原始提示里,它危险的检查翻了一倍。示例教的是模式匹配:“一行写着 X”看起来像那些regex示例,所以regex它就是。而测试教的是失败模式。
那些必须交给评判但被送进代码的检查。Flash-Lite 在 Jev 的弱提示上也降到了零。
经过重写后,Jev 在第八项以及我们所有检查项上都归零了。零危险,零错误检查器,一次又一次运行都是如此。在一半数据上微调,在它从未见过的那一半上也是零。
这一句话修好了 Jev。它没有修好 Flash-Lite。我们写了一整批两个模型都没见过的全树检查,比如“任何子文件夹里都不存在名为 draft.md 的文件”。
两个模型在这些检查上都做出了误报。经过重写后,Jev 从 35 次误报运行降到 0。Flash-Lite 几乎没动。区别在于两者失败的方式。Jev 一次又一次地把同样的检查做错:一个盲点。Flash-Lite 大约每五次运行错一次检查,另外四次是对的:一个翻转。Jev 会字面地服从一个精确的标准,所以一个更好的标准就能修好它。Flash-Lite 会宽容一个草率的标准,但不会完全服从一个精确的标准。盲点是你去修复的提示词 bug。翻转是你要去测量的采样噪声。
更聪明的模型会掩盖一个薄弱的提示词吗?
如果提示词是最大的变量,那么一个更好的模型会让它不再重要吗?相同的措辞,Jev 的裸提示词,三个模型。
会。Flash-Lite 仍然大约每五次运行错一次。Gemini 3.8 Flash 和 Opus 5.5 从未出错。更强的模型会掩盖更弱的提示词。
它并不会让提示词不再重要。那个修好 Jev 的相同提示词改动,把两个更大的模型推向了推诿。它把 Opus 的正确答案削减了一半以上,把 3.8 Flash 的削减到了零。提示词仍然是那个单一最大的变量;模型决定你的错误有多昂贵。而这个掩盖在价格和延迟上都多出一个数量级的成本。
超越我们的数据
我们的检查来自一个项目。所以我们在Berkeley Function Calling Leaderboard的一个切片上运行了同样的实验。任务是:选出正确的函数并写出它的参数,或者说没有合适的。"没有合适的"就是那个基准测试版本的推诿。调用一个不合适的函数是危险的方向。
我们学到的第一件事和两个模型都无关。Flash-Lite 一直在答案键说没有函数的条目上调用函数,而每一次我都把它标记为错误。
在第四次或第五次失败之后,我读了那个条目。“谁赢得了 2020 年的世界大赛?”只有一个候选函数,get_champion(event, year)该函数回答了问题。参考答案说它没有。于是我检查了样本中每一个“都不匹配”的条目。有七个条目匹配。Flash-Lite 通过不同意参考答案,把这七个全都标记了出来。我们在评分时排除它们,并在仓库中列出。一个承担实际工作的廉价模型,在一个公开基准测试中发现了一个 bug。
Jev 的假阳性率是在 0.8 的概率阈值下:只有当 Jev 至少有 80% 的把握时才调用该函数。
两个模型都不是零。在其最高标签下,Jev 在约十三分之一没有任何匹配的条目上调用了一个函数。Flash-Lite 约为二十五分之一。但 Jev 有一个旋钮。把阈值提高到 0.8,它的假阳性率降到 1%,代价是推迟的频率翻倍。Flash-Lite 没有旋钮,但它会写出参数,并且十次中有九次以上完全正确。
第三张卡片,Jev + Flash-Lite,以远低于单次调用一半的价格,获得 Jev 的假阳性率和 Flash-Lite 的参数。这个组合就是最终的方案,所以本文余下部分都围绕它展开。
你不需要的那次调用
拿_Last updated检查。Jev 返回regex一个概率分布。然后停下。哪个文件?什么模式?那需要第二个模型。Flash-Lite,一次调用,返回标签、参数和理由,因为它能写。
实测,通过 OpenRouter 计费。时间是平均毫秒数;成本是每 1,000 次检查。
现在看第四行:Jev → Flash-Lite,$0.10。比单独使用 Flash-Lite 更便宜,后者是 $0.52。增加一个模型怎么会让它便宜五倍?
Jev + Flash-Lite 如何省钱
没什么巧妙的。两个乘数,都作用在 Flash-Lite 的账单上。
1. 第二次调用只在有内容可写时才运行。交给评判模型的检查没有参数。Jev 推迟它,Flash-Lite 就永远看不到它。在我们的完整集合上,这是大多数检查:不到四成的检查有内容让 Flash-Lite 去做。
2. 第二次调用使用更短的提示词。单次调用的提示词很长,还带有完整示例,因为正是这些让 Flash-Lite 在路由决策上保持安全。而且每次检查都要发出去。在这对组合里,Jev 已经做出了路由决策。第二个提示词只需要说:这是检查项,这是被选中的检查器,写出它的参数。这大约只有七分之一的 token。
An eval assertion has already been routed to a deterministic checker. Write that checker's arguments.
Assertion: {check}
Checker already chosen: {label}
Arguments needed per checker:
- file_exists / not_file_exists: "path"
- glob_count: "glob" and "count"
- regex: "path" and "pattern"
...
Reply with one JSON object holding only the arguments and nothing else.
Jev 本身每千次检查约花费三美分。可以忽略不计。
蓝色是 Flash-Lite 输入,黄色是 Flash-Lite 输出,珊瑚色是 Jev。
把各项倍数合在一起:大约一半的检查会发起一次调用,提示词只有七分之一大小,再加上几乎免费的第一跳。实测下来,每 1,000 次从 $0.52 降到 $0.10。在生产环境的混合负载下还会更低。
公开数据上也是如此。在 BFCL 上,这对组合的成本不到一次 Flash-Lite 调用的一半。第二跳只在大约一半的条目上运行,而且只读取一个函数的 schema,而不是每个候选函数的。
代价是:多一次往返,平均约四分之一秒。而且你继承的是 Jev 的路由,不是 Flash-Lite 的。在 BFCL 上,这意味着 Jev 的假阳性率和 Jev 的调节旋钮。
要点
Flash-Lite 是 Google 藏得最深的秘密。对大多数分类工作来说,它在快和便宜之间取得了恰当的平衡。它足够聪明,能容忍粗糙的提示词,也能生成内容。在 Jev 的裸提示词上,它给了我们零次危险调用,并在一次调用中写出了标签和参数。
提示词是你最大的单一变量。一句话就让 Jev 从 35 次危险运行降到 0 次,而示例反而让它更糟。更聪明的模型能缓解这个问题,但并不能消除它:同样的提示词改动仍然把 Opus 从大多作答推向了大多推诿。
私有评测能区分真实与炒作。我们不必听信任何人对 Jev 的说法,包括 TypeSafe 的。我们有一个真实产品的真实检查项。于是我们拿被吹捧的模型和我们已经在用的模型做了对比,得出了结论。真实的是:快、便宜,一旦问对问题就是零危险。不真实的是:示例有帮助、更聪明的模型让提示词变得无关紧要。没人说的是:最便宜的方案是两者一起用。
想深入了解,先从Jev 实用入门指南开始。Google 的Gemini 3.6 Flash 与 3.5 Flash-Lite 开发者指南介绍了这个模型。
Happy Hacking!










