不生成 token 的判决模型开始抢抽取的活:把置信度变成一道运营门槛
十月中旬这半个月,一个不太起眼的品类连着出了三家新品:10 月 15 日 Liquid AI 放出多模态判决模型 d1-ultra,价格写在官网首页——每百万输入 token $0.042;10 月 18 日阿塞拜疆团队 HAL-X 把 THX-01 挂上 Hugging Face,Apache 2.0,322M 参数、单次前向约 10 毫秒,工单分类的平均 ECE 压到 0.003;更早挂出的 laya 把文档上限做到 8192 token,还顺手把「长文档时正确率会掉」写进了自己的模型卡。
它们都不生成一个 token。你给它一个 state(邮件、工单、文档、JSON 记录、agent 轨迹)和几个带类型的问题,它在一次前向里把每个问题的概率分布还给你。
本站在月初写过 TypeSafe 的 Jev 被黑盒复刻那段故事(a445,10 月 2 日)——那时候能问的还只有「选项 / 是或否 / 打分」三种类型,想抽个数字得在服务层用一堆区间桶去模拟。这次要讲的是两件事的变化:问题类型被扩到了抽取和引用,以及「校准」第一次成了可以拿来定价的产品特性。
一、判决模型在做什么
Liquid 的官方定义最干净:判决模型回答「关于某个情形的问题」,给的是每个可能答案的概率,而不是文字。d1-ultra 支持三种问题类型(noul 是或否、choice 多选一、score 量表位置),能同时读文本和图片,一次前向出结果。
THX-01 的接口更宽,六种:
| 类型 | 返回 | 关键点 |
|---|---|---|
choice | N 个选项各一个概率 | 选项数 >24 时走两轮锦标赛 |
noul | P(yes) | 是或否 |
score | 有序等级 + 分布 | 情感/紧急度这类 |
number | 文档里写着的数字,没有则 null | 原生能力,不做任何换算 |
excerpt | 原文片段 + 字符偏移 | 从文档里切出来,编不出新字 |
cite: true | 支撑答案的原文部分 + 概率 | 任意问题都能挂 |
官方给的最小调用长这样(模型卡原文,Apache 2.0,首次运行从仓库下载 322M 权重):
pip install thx01 # 本地推理库
pip install "thx01[server]" # 需要 REST 服务时
THX01_MODEL=doofz/THX-01 THX01_API_KEY=your-key python -m thx01.server --port 8095
import thx01
agent = thx01.load("doofz/THX-01") # 有 GPU 就走 GPU
doc = ("Northwind Corp. reported Q3 2026 results. Revenue for the quarter was $48.3 million, up 14%. "
"Net income was $6.2 million, compared with $4.1 million a year ago. "
'"We will open two offices in Baku," said CEO Laura Chen.')
result = agent.decide(doc, {
"kind": {"type": "choice", "question": "What is this document?",
"criteria": {"earnings": "earnings report", "complaint": "customer complaint",
"invoice": "invoice"}},
"revenue": {"type": "number", "question": "What was the revenue, in US dollars?", "cite": True},
"quote": {"type": "excerpt", "question": "What did the CEO say?"},
})
# kind -> earnings
# revenue -> 48300000(并回传支撑句)
# quote -> 从原文切出的片段
注意 revenue 那一问:文档里写的是 $48.3 million,返回的是 48300000——它会做量词归一化(模型卡另举了 1,2 mln → 1200000),但不做单位或币种换算。这个「归一化数字、不换算单位」的边界,是它和生成式模型最不一样的地方:它不会去猜你想问的是哪种单位,它只会告诉你原文写了什么。
这张表里最有信息量的一行是 number。THX-01 的模型卡里写得很直白:number、excerpt、引用不在 TypeSafe 的问题类型里(那边只有 choice/noul/score),服务层要实现只能拿 choice 反复模拟;而它自己把这种模拟调用也兼容了(用区间桶、文档分块来问),所以旧的服务层不用改。
效果差距写在自家表里:数字查找 69.6% → 93.4%,用区间桶模拟 0.0% → 95.1%,原文抽取 F1 从 20.1% 到 84.1%,引用命中 12.7% → 94.0%。那个 0.0% 是全表最值得看的一格:把一个需要「读数字」的问题硬塞给只会选桶的分类模型,旧方案不是差,是完全做不了——模型只能选一个桶,不会做区间到具体值的推理。
顺带说一句不好听但必须说的:同一个数字查找任务上,TypeSafe Jev 1.13 做到 96.6%,比 THX-01 的 93.4% 高。这是模型卡自己写出来的对照,不是我从别处挖的。厂商愿意在自家优势表里塞一行自己输的对手分数,这种账本的可信度比全绿的表格高。
二、为什么「准」不够:校准才是这个品类的核心竞争力
一个模型 98% 的准确率,听起来可以全自动了。但如果它在答错的那 2% 上给出的概率是 0.97,你没有任何办法在下游把它们拦下来——因为它的「自信」和「正确」不挂钩。
校准(calibration)说的就是这件事:把样本按置信度分箱,每箱的平均置信度应该等于该箱的实际准确率。量化指标是 ECE(Expected Calibration Error),越小越好。THX-01 在工单任务上的 ECE 是 0.003,Kev-4B 在同一任务是 0.202——后者意味着它在你最需要它说真话的时候,系统性高估自己二十个百分点。
Liquid 在博客里把这件事说成了产品卖点:「大多数判决模型对自己的回答过度自信,所以我们专门训练 d1-ultra 给出更可靠的银率」,并声称在 112 项评测上胜过自家 d1 与 Jev 1.14,且比所有其它判决模型在 Artificial Analysis Decision Index 上高至少 8 分。
关于那个 8 分:这是厂商引用第三方榜单的说法,我没法在本机复核——AA 的榜单页是前端渲染的,抓 HTML 检索不到不等于没上榜。要引用就按「厂商引用第三方榜单,未经本机复核」来标。
校准能被训练出来,靠的是把奖励函数设计成严格合适评分规则(strictly proper scoring rule)。THX-01 的模型卡把公式摊开了:
R(q, t) = Σ_k t_k·log q_k + 0.5·(Σ_k t_k·q_k)/‖q‖₂ − 1[ordinal]·RPS(q, t)
对数是 log score、第二项是 spherical score、最后一项是按序问题的 ranked probability score。严格合适的含义是:这个奖励的期望只有在报出真实分布时才最大。也就是说,如果模型心里觉得「是」的概率是 0.7,报 0.9 会让长期收益下降——它没有说谎的动机。这不是靠提示词求它「请诚实」,是靠目标函数让它诚实。
再叠两个细节,整套逻辑才完整:没有合适选项时给均匀分布(于是模型报「我不确定」,而不是自信地瞎选一个),有序量表上给近似命中部分分。训练是 8 个阶段的 RLCD,累计约 210 万条判决,最后两个阶段专门练抽取和引用。
三、纸面数字,以及哪些能核、哪些不能
以下是模型卡自报(第三方无法独立复核,我按原表抄)。工单分类,15 个类目、4 个测试集、2843 张阿塞拜疆语/俄语/英语/土耳其语工单:
| 模型 | Clean | Corrupted | Messy | Independent | 平均 | ECE | 延迟 |
|---|---|---|---|---|---|---|---|
| THX-01 | 99.2 | 97.7 | 98.8 | 97.9 | 98.4 | 0.003 | ~10 ms |
| Claude Sonnet 5.5 † | 98.5 | 98.0 | 98.5 | 99.0 | 98.5 | – | 1.5 s |
| Wahoo 1.5 | 99.8 | 96.3 | 98.0 | 97.2 | 97.8 | – | 145 ms |
| GPT-6-Luna | 98.6 | 97.4 | 96.7 | 98.2 | 97.7 | – | 1.9 s |
| TypeSafe Jev 1.13 | 99.2 | 95.0 | 96.2 | 99.0 | 97.4 | 0.007 | 331 ms |
| Kev-4B | 96.1 | 88.4 | 90.8 | 95.8 | 92.8 | 0.202 | 830 ms |
读之前先看脚注,这张表的可比性是有洞的:† 号那行只跑了每个测试集 200 条的抽样;ECE 只在 Independent 集上算,而且大模型根本不返回概率,所以它们的 ECE 栏是「–」。也就是说「THX-01 准确率和 Sonnet 5.5 打平、延迟快 150 倍」这句话成立的前提是:你只信 200 条的子集,并且承认大模型给不出概率。后者才是重点——选这个品类,本来就是冲着概率去的。
速度表也是自报:单问约 9 ms,同一个 state 连问三个约 10 ms,150 个请求批处理约 156 ms,峰值约 3000 判决/秒。laya 那侧的数字是约 33 ms/次,而且它自己在卡里承认:4000 token 的输入在 Apple GPU 上要约 1.7 秒,速度跟着真实长度走。
厂商自己承认的边界,往往比成绩更有用。 THX-01 的 Limitations 写了三条:77 类意图的 Banking77 只到 65.9%、六类情绪(DAIR Emotion)只到 44.6%、number/excerpt 只负责「找出文档里写着的字」,不做算术、不做单位换算、不做同义改写——问公里而文档写英里,答案就是 null。laya 的卡里则写着:长文档 20 题里能对 16–18 题,再长就变成 8–17 题,「请在你自己的数据上验证长文档准确率」。
这两句比任何 SOTA 声明都值钱:它告诉你这个品类的输入上限是按问题算的——THX-01 是每个问题最多 1024 token(问题、选项、state 全算进去),超了直接截断。长文档得先切,切法直接决定准确率。
四、成本结构:值得单独算一遍
d1-ultra 的定价是只算输入 token,没有输出 token——因为它不生成。官网把计价细节写全了:图片按 32×32 像素的 patch = 1.5 token 计,一张 1024×1024 的图 = 1536 token。
关键的一句在后面:每个问题各按一次 prompt 计费,包含它自己的文字和所有图片。
这条把单价结构改了形。一张 1024×1024 的图挂 5 个问题:
图片:1536 token/问 × 5 问 = 7680 token
文字:假设 state 200 token + 每问 20 token → 200×5 + 20×5 = 1100 token
合计 ≈ 8780 输入 token ≈ 0.00037 美元/次请求
换成生成式模型问同样五件事,通常是五轮生成,或者一轮生成里塞五个问题(然后你还要解析它输出的 JSON,还得处理它偶尔编一个字段)。判决模型这侧的好处不是单价,是没有解析层、没有幻觉面:excerpt 是从文档里切出来的,number 没找到就返回 null,结构本身堵住了「编一个看起来合理的答案」。
但「每问重复计费」这件事也意味着:把问题拆得越碎越贵。3 个问题合 1 个粗问题(比如「这封邮件属于哪个部门且紧急度如何」)省 token,代价是准确率下降——这个权衡得在你自己的数据上量,别照抄别人的问题清单。
五、自己算校准:一套能直接跑的脚本
厂商的 ECE 是厂商算的。你要上线选择性自动化,必须在自己数据上算一遍——因为做运营决策的不是准确率,是「覆盖率 × 自动处理部分的准确率」这条曲线。
下面这份 calib.py 只用标准库,把四个最常用的度量和一条门槛策略都实现好了。输入是一行一条的 JSONL,{"p": 0.83, "y": 1},p 是模型给的「答对」概率、y 是实际对错。没数据就直接跑,它会造一批**「真实概率 q、模型报 sigmoid(2.2·logit(q))」的过度自信预测**给你看方法怎么用:
import json, math, random, sys
from collections import defaultdict
def brier(pairs): # 越小越好;一律报 0.5 相当于 0.25
return sum((p - y) ** 2 for p, y in pairs) / len(pairs)
def ece(pairs, bins=10): # 等宽分箱,返回 ECE 与可靠性表
buckets = defaultdict(list)
for p, y in pairs:
buckets[min(int(p * bins), bins - 1)].append((p, y))
n, out, table = len(pairs), 0.0, []
for idx in range(bins):
rows = buckets.get(idx, [])
if not rows: continue
conf = sum(p for p, _ in rows) / len(rows)
acc = sum(y for _, y in rows) / len(rows)
out += len(rows) / n * abs(conf - acc)
table.append((idx / bins, (idx + 1) / bins, len(rows), conf, acc, conf - acc))
return out, table
def mce(pairs, bins=10): # 最坏那一箱的偏差,比 ECE 更能暴露尾部风险
return max(abs(g) for *_, g in ece(pairs, bins)[1])
def _logit(p):
p = min(max(p, 1e-6), 1 - 1e-6)
return math.log(p / (1 - p))
def fit_scale(pairs): # p' = sigmoid(a·logit(p)),网格搜 a 最小化 NLL
best, best_a = None, 1.0
for i in range(281):
a = 0.2 + 2.8 * i / 280
cur = -sum((y * math.log(max(1/(1+math.exp(-a*_logit(p))), 1e-12))
+ (1-y) * math.log(max(1-1/(1+math.exp(-a*_logit(p))), 1e-12)))
for p, y in pairs) / len(pairs)
if best is None or cur < best: best, best_a = cur, a
return best_a, best
def apply_scale(pairs, a):
return [(1 / (1 + math.exp(-a * _logit(p))), y) for p, y in pairs]
def coverage_table(pairs, thresholds=(0.5, 0.7, 0.8, 0.9, 0.95)):
for t in thresholds:
kept = [(p, y) for p, y in pairs if max(p, 1 - p) >= t]
if kept:
print(f" p>={t:.2f} 覆盖 {len(kept)/len(pairs)*100:5.1f}% "
f"准确率 {sum(y for _, y in kept)/len(kept)*100:6.2f}%")
if __name__ == "__main__":
data = [(float(json.loads(l)["p"]), int(json.loads(l)["y"]))
for l in open(sys.argv[1], encoding="utf-8") if l.strip()] if len(sys.argv) > 1 else None
if data is None: # 演示数据:真实的过度自信
rng, pairs = random.Random(7), []
for _ in range(4000):
q = min(max(rng.choice((0.55, 0.7, 0.85, 0.95, 0.99)) + rng.gauss(0, 0.06), 0.5), 0.999)
y = 1 if rng.random() < q else 0
pairs.append((min(max(1/(1+math.exp(-2.2*_logit(q))), 0.001), 0.999), y))
data = pairs
err, table = ece(data)
print(f"样本 {len(data)} 准确率 {sum(y for _, y in data)/len(data):.4f}")
print(f"Brier {brier(data):.4f} ECE {err:.4f} MCE {mce(data):.4f}")
print(" 区间 样本 平均置信 实际准确 差")
for lo, hi, cnt, conf, acc, gap in table:
print(f" {lo:.1f}-{hi:.1f} {cnt:6d} {conf:8.4f} {acc:8.4f} {gap:+.4f}")
a, _ = fit_scale(data)
cal = apply_scale(data, a)
print(f"缩放 a={a:.3f}(<1 即过度自信)→ ECE {ece(cal)[0]:.4f} Brier {brier(cal):.4f}")
print("自动处理门槛:"); coverage_table(cal)
本机实跑输出(Python 3.13,纯标准库,几秒钟):
样本 4000 准确率 0.8157
Brier 0.1336 ECE 0.0755 MCE 0.1540
区间 样本 平均置信 实际准确 差
0.5-0.6 395 0.5307 0.5646 -0.0338
0.6-0.7 260 0.6503 0.5808 +0.0696
0.7-0.8 296 0.7521 0.6250 +0.1271
0.8-0.9 473 0.8538 0.6998 +0.1540
0.9-1.0 2576 0.9833 0.9212 +0.0621
缩放 a=0.540(<1 即过度自信)→ ECE 0.0183 Brier 0.1254
自动处理门槛:
p>=0.50 覆盖 100.0% 准确率 81.58%
p>=0.70 覆盖 73.4% 准确率 89.51%
p>=0.80 覆盖 60.4% 准确率 93.13%
p>=0.90 覆盖 45.0% 准确率 96.78%
p>=0.95 覆盖 32.4% 准确率 97.99%
三条结论直接可用:
一、可靠性表才是诊断书。 ECE 0.0755 是个孤立数字,但按箱摊开就看到病根在 0.8–0.9 那一箱:模型说 85%,实际只有 70%,偏了 15.4 个点。要改进模型就往这一档加数据,不需要看别的。
二、一个系数就能修掉大部分偏差。 拟合出的 a = 0.540(不到 1,正是过度自信的方向),缩放后 ECE 从 0.0755 降到 0.0183、Brier 从 0.1336 降到 0.1254。注意这是在验证集上拟合的系数——上线前必须另留一段数据复核,否则只是把偏差背到了测试集上。
三、门槛把概率变成运营策略。 不设门槛时自动处理 100%、准确率 81.58%;设 p≥0.9 变成覆盖 45%、准确率 96.78%;设 p≥0.95 是覆盖 32%、准确率 98%。用同一份脚本在验证集上定阈值(我在演示数据上把「准确率 ≥99%」的最大覆盖率算出来是 25.5%,阈值落在 0.970),剩下的转人工——这样你就能对业务方说出那句真正有用的话:「自动处理四分之一,错一个都算事故;其余进人工队列」。
对比一下 THX-01 自报的那条运营曲线:约四分之三的工单零错误自动处理,覆盖 90% 时准确率仍 ≥99.5%。厂商的数字是厂商数据上的,你的数字得你自己跑。方法在,结论自己算。
六、四个坑
- ECE 怎么分箱会变数。 等宽分箱在样本少时会被尾部几个点带跑,等频分箱更稳;两套都算一遍,差得远就说明样本不够。另外 ECE 是加权平均,会被大箱掩盖——所以一定要把可靠性表和 MCE 一起看。
- 别用平均分掩盖单个任务。 THX-01 的 39 个多语言任务平均准确率从 58.3% 抬到 84.2%、平均 ECE 从 0.204 降到 0.066,但逐任务看:Banking77 只有 65.9%、DAIR Emotion 只有 44.6%(ECE 0.284,全表最差)、SIB-200 的哈萨克语 ECE 0.158。平均分好看,不代表你要用的那个任务能用。
- 输入上限是按问题算的,不是按文档算的。 THX-01 每问 1024 token 全包(问题+选项+state),超出截断;
number不做单位/币种换算,问法不对就是null;THX-01 支持 18 种后训练语言、经编码器覆盖 100+ 语言,但非它后训练过的语言别指望同样水准。 - 阈值必须配漂移监控。 上半年的数据定下的门槛,到了下半年工单分布一变就作废。把「每日自动处理覆盖率 + 自动部分的准确率 + ECE」三项打进监控,掉出区间就回退到全人工——这比调模型参数便宜得多。
总结
这个品类的价值不在「比大模型准」,而在它把不确定性变成了一个接口字段。生成式模型给你一段文字,你得靠解析和事后校验去猜它有多确定;判决模型直接给你概率,你就可以写一行 if p > 0.97: 自动处理。
半个月三家新品里,THX-01 是最值得拿来读的一份:322M 参数、Apache 2.0、六种问题类型、模型卡把奖励函数和 8 个训练阶段都摊开,还主动写了自己输给 Jev 的那一行。Liquid 的 d1-ultra 把这条路线推到了多模态和 API 定价($0.042/百万输入 token、按 patch 折算图片、每个问题单独计费);laya 补上了 8192 token 的长文档和 MCP 集成。
要注意的成本账是「每问重复计费」,要注意的技术账是「校准必须在自己数据上量」。真要做起来,顺序很简单:拿 500 条自己业务的历史样本跑一遍,用上面的脚本出可靠性表,拟合一个缩放系数,再用验证集定门槛。产出不是一篇评测,是一句能给业务方看的承诺。
参考与致谢
- Liquid AI《d1-ultra: Introducing the World’s Best Decision Model》(2026-10-15):https://www.liquid.ai/blog/d1-ultra
- THX-01 模型卡(HAL-X AI,Apache 2.0):https://huggingface.co/doofz/THX-01
- laya 模型卡:https://huggingface.co/convaiinnovations/laya
- 本站相关前文(a445,2026-10-02):TypeSafe Jev 的黑盒复刻与温度拟合——本文的增量在于问题类型扩到抽取/引用,以及把校准落成可跑的门槛脚本。
文中 THX-01、d1-ultra、laya 的分数、延迟与价格均为厂商官方自报,未经本机独立复核;calib.py 的输出为本机 Python 3.13 实跑结果(合成数据,可复现)。
不生成 token 的判决模型开始抢抽取的活:把置信度变成一道运营门槛
- 作者
- 摆渡
- 发布于
- 许可协议
- CC BY-NC-SA 4.0
除另有声明外,本文采用上述许可协议。转载或改编请注明作者与原文地址,并遵守许可条款。