
用 Decision Model 做单次前向推理的分类决策:从请求构造到概率阈值
用 Decision Model 做单次前向推理的分类决策:从请求构造到概率阈值
Decision Model 把分类、路由、Yes/No 判定和分级评分统一成「一次前向推理返回概率分布」的任务。本教程讲清三类问题(Choice / Noul / Score)的请求结构、概率与期望值的读法、四种概率读出方式的差异,以及选型时必须核对的许可证、语言支持与校准问题。
业务系统里有一大类任务其实不需要模型「写」任何东西:把工单分到既定类别、决定 agent 调用哪个工具、判断某个条件是否成立、给候选打一个有序等级。用聊天型模型做这些事,每次都要走完整的解码循环,输出格式还可能跑偏;更麻烦的是,想用概率阈值在「自动处理」和「转人工」之间切换时,从生成文本里拿不到可信的概率。Decision Model 就是针对这类场景设计的模型:给它一段 state(输入)和若干带类型的 question,它在一次前向推理中返回每个选项的概率。本教程按请求结构、概率读法、模型差异、选型核对四条线展开,帮你把这类模型接进自己的判定流程。
准备工作
动手之前,先把下面几件事确认清楚。Decision Model 生态目前的特点是模型多、命名杂、字段不统一,准备工作做得越细,后面踩坑越少。
明确你要的是「选择」而不是「生成」
先判断任务是否落在下面四类里:
- 把咨询、案件、工单归入既定类别
- 决定 agent 调用哪个工具、哪个模型
- 判断「是否满足某条件」的 Yes/No 问题
- 给候选做有序的等级评定
如果任务需要输出解释性文字、需要多轮追问、需要自由格式的长答案,那它不适合 Decision Model。这类模型刻意不输出自由文本;需要理由时,常规做法是另接一个聊天模型,让它基于判定结果去写说明。
确认访问方式
公开的 Decision Model 大致分三种获取形态,准备工作的内容完全不同:
- 开放权重:可以从模型仓库下载权重自行部署,部分还公开了训练代码或 recipe。
- 仅权重与推理代码:能跑,但训练 pipeline 不公开,无法用自己的标签重训。
- 托管 API:只能通过服务方接口调用,权重不公开,也无法本地部署。
如果你的场景涉及敏感业务文本,第三种形态通常直接出局。先定形态,再谈选型。
核对许可证与训练数据条件
这是最容易在 PoC 之后才被发现的问题。确认清单:
- 模型权重本身是商用许可、非商用许可,还是尚未确定。
- 即使权重是宽松许可(例如 Apache-2.0),部分模型的卡片仍会注明「训练数据另有许可,模型许可不替代数据许可」。商用前要一并核对数据侧条件。
- 托管型服务按各家服务条款执行,与开放权重模型的许可不是一回事。
许可证、可用地区、价格与配额都会变动,一律以官网与模型卡当前 revision 为准。
确认语言支持是否被明确写出
「backbone 是多语言模型」和「这个 Decision Model 在你的语言上可用」是两件事。选型时要区分三种情况:
- 卡片明确写了支持你的语言;
- 卡片声称多语言,但没有给出该语言单语的精度;
- 卡片只写英语,或干脆没有语言说明。
第三种情况不能靠推断补齐,只能用自己的评估数据验证。
准备自己的标注样本
Decision Model 输出的概率是模型输出,不是正确率的保证。要设阈值,就必须有一批带标签的真实样本,用来做温度重拟合和阈值校准。样本量、类别分布按你的业务实际来定,但这一步不能省。
操作步骤
第一步:把任务拆成带类型的 question
目前多数模型共用三种问题类型:
| 类型 | 用途 | 返回内容 |
|---|---|---|
| Choice | 从多个选项中选一个 | 每个选项的概率 |
| Noul | Yes/No 判定 | P(true) |
| Score | 从有序等级(评分量表)中选一档 | 各档概率与期望值 |
一个请求里可以同时放多个 question,一次推理全部返回。这既是性能优势,也意味着你要在拆题时想清楚:哪些问题彼此独立,哪些问题需要放在一起看。
第二步:构造 state 与 questions
state 可以是纯文本,也可以是 JSON。questions 是一个以问题名为键的对象,每个问题带 type、instructions 和 criteria。下面是一个把三类问题合并到一个请求里的示例,state 是一段客服来信:
{
"state": "上周收到的商品有破损,希望退款,请尽快处理。",
"questions": {
"is_refund_request": {
"type": "noul",
"instructions": "这条咨询是否在要求退款?",
"criteria": {
"true": "明确要求退款",
"false": "没有要求退款"
}
},
"department": {
"type": "choice",
"instructions": "应转交哪个部门?请选一个。",
"criteria": [
{ "id": "billing", "description": "账单与付款相关咨询" },
{ "id": "returns", "description": "退货、退款、换货相关咨询" },
{ "id": "technical", "description": "产品用法与故障相关咨询" }
]
},
"urgency": {
"type": "score",
"instructions": "请选择处理紧急度。",
"criteria": [
{ "id": "low", "description": "不急", "value": 0.0 },
{ "id": "normal", "description": "常规处理即可", "value": 0.3 },
{ "id": "high", "description": "需要尽快处理", "value": 0.7 },
{ "id": "critical", "description": "需要立即处理", "value": 1.0 }
]
}
}
}
几个构造要点:
- Noul 的 criteria 用 true / false 两个键,分别写清成立与不成立的含义。判定标准写得越具体,概率越有意义。
- Choice 与 Score 的 criteria 是数组,每项带 id 与 description;Score 的每项还要带一个数值 value。
- instructions 是给模型的自然语言说明,不是注释,要写清楚「选一个」「判断是否」这类动作。
- 字段名在不同模型之间并不统一。上面的结构只是说明用,接入具体模型时必须回到它的模型卡核对入参格式。
第三步:读取返回的概率分布
返回体按问题名组织,每个问题给出该类型的判定结果和完整概率分布:
{
"answers": {
"is_refund_request": {
"noul": true,
"probabilities": { "false": 0.04, "true": 0.96 }
},
"department": {
"choice": "returns",
"probabilities": { "billing": 0.02, "returns": 0.97, "technical": 0.01 }
},
"urgency": {
"score": 2.48,
"expected_value": 0.834,
"numeric_values": [0.0, 0.3, 0.7, 1.0],
"probabilities": { "low": 0.02, "normal": 0.08, "high": 0.30, "critical": 0.60 }
}
}
}
读法要分清三个层次:
- Noul 的 probabilities 是 true 与 false 两个概率。
- Choice 的 probabilities 是每个选项各自的概率。
- Score 的 probabilities 是每一档的概率;score 是「以 0 起始的档位索引」的期望值,即用各档概率加权平均索引;expected_value 则是各档所赋 value 的期望值。
以上面的数字为例,score = 0×0.02 + 1×0.08 + 2×0.30 + 3×0.60 = 2.48;expected_value = 0.3×0.08 + 0.7×0.30 + 1.0×0.60 = 0.834。注意 score 与 expected_value 是两套刻度,前者基于档位序号,后者基于你自定义的 value,不要混用。
示例中的数值仅用于说明计算方式,不是任何模型的真实输出。
第四步:用概率设自动处理与转人工的阈值
这是 Decision Model 相对生成式方案最直接的收益。典型写法是:取该问题概率最高的选项,若其概率低于阈值,就不自动处理,转人工复核。例如「department 的最大概率低于 0.9 就转人工」。
但阈值不能拍脑袋定。设阈值之前必须完成两件事:
- 用你自己的标注样本,检查概率与正确率是否对齐;
- 按模型卡的建议做温度重拟合,再重新评估。
多个模型卡都明确提醒:概率是模型输出,不是正确率保证,且会随 state、instructions、criteria 的写法变化。有的模型卡还特别说明,其置信区间只在留出数据上验证过,实际流量上要先测再用阈值。
第五步:理解概率是从哪里读出来的
「一次前向推理出概率」是所有这类模型的共同点,但概率的读出位置不同,直接决定了模型在候选数量、上下文长度、能否复用 embedding 上的表现。已公开的做法大致分四类。
A. 读选项代码的 logit
把选项编码成 A、B、C 之类的字符,取最终 token 位置的 logit,只在这些选项字符上做归一化。有论文报告,即使不做额外训练,仅从指令微调过的 LLM 的最终 token logit 里读选项字母,就能接近专用模型的水平;再叠加 LoRA 与 decision head 微调还能继续提升。也有模型卡描述为:把每个问题格式化后,限定只生成一个选项代码 token,再把它转成概率。这类实现通常对选项数量有上限,超过上限就分组处理。
B. 读每个选项的标记位置
给每个选项安排一个专用标记位置(例如各自的 mask token),在该位置上取分,再在问题内的选项之间做 softmax。因为答案空间是每次请求时定义的,新增选项不需要重新训练。也有实现用「选项结束 token 与问题最终 token 比对」的 pointer head 打分,并把 state 的计算结果缓存起来,使多个问题互不影响。还有把 state 与多个问题拼成一条序列、在每个问题的选项后放标记来分别读出的做法,以及用一个小的 transformer head 从 backbone 最终隐状态一次性给所有问题的所有选项打分。
C. 共享前缀 + 每个候选单独分支
用 encoder 把指令和 state 编码成共享前缀,只编码一次;每个候选只 attend 这个前缀和自己的 token,候选之间互不可见;对每个候选做 mean pooling 后,用按决策类型区分的 head 打分,再在候选间做 softmax。这类结构在候选很多时优势明显,因为前缀只算一次。
D. 比较 embedding
在冻结的 LLM embedding 之上,分别训练「把 state 映射过去」和「把 action 映射过去」的 head,用双向 InfoNCE 损失训练。因为 state 与 action 分开编码,候选的 embedding 可以复用。有模型卡报告在约一千个候选的条件下比对照模型快一个数量级。代价是概率只在给定候选集合内有相对意义,换一批候选,数值就不可比。
一般性的取舍是:候选多、需要复用 embedding 时,C 与 D 更合适;上下文长、需要处理图像等复杂输入时,以 decoder LLM 为 backbone 的 A 与 B 更合适。这只是从各模型卡描述推出的判断,具体差距要在自己的数据上测。
第六步:按结构选模型族
公开模型可以粗分为 encoder 系(小模型为主)与 decoder 系(带小型 head)。选型时看四个维度:读出方式、backbone、校准处理、许可证与语言。
encoder 系体积小、推理便宜,适合高并发、低延迟的分类与路由。已公开的规格里,规模从千万级参数到数亿参数不等,backbone 有 reranker 类模型、多语言 encoder、单语 ModernBERT 等。语言支持差异很大:有的明确写英语,有的明确写某单一语言,有的声称多语言但未给出单语精度。
decoder 系 + 小型 head规模从不到 1B 到 27B 不等,backbone 多为 Qwen 系或 Gemma 系,常见做法是 LoRA 加专用 readout head,也有全层微调后合并 adapter 的。这类模型在长上下文和复杂输入上更有余地,部分版本已支持图像与视频输入。代价是部署成本高,且部分大尺寸版本采用非商用许可。
embedding 系与托管型适合排序与路由,候选 embedding 可复用;托管型则省去部署,但权重不公开、无法重训,且受服务方条款约束。
无论选哪一族,都要回到模型卡确认三件事:入参字段名、选项数量上限、以及是否要求特定的推理框架构建。
一个完整示例
下面把前面的步骤串成一个可执行的最小流程。示例用伪代码表示调用,具体 SDK 与字段名按你所选模型的卡片替换。
1. 定义问题模板
questions = {
"is_refund_request": {
"type": "noul",
"instructions": "这条咨询是否在要求退款?",
"criteria": {"true": "明确要求退款", "false": "没有要求退款"}
},
"department": {
"type": "choice",
"instructions": "应转交哪个部门?请选一个。",
"criteria": [
{"id": "billing", "description": "账单与付款相关咨询"},
{"id": "returns", "description": "退货、退款、换货相关咨询"},
{"id": "technical", "description": "产品用法与故障相关咨询"}
]
},
"urgency": {
"type": "score",
"instructions": "请选择处理紧急度。",
"criteria": [
{"id": "low", "description": "不急", "value": 0.0},
{"id": "normal", "description": "常规处理即可", "value": 0.3},
{"id": "high", "description": "需要尽快处理", "value": 0.7},
{"id": "critical", "description": "需要立即处理", "value": 1.0}
]
}
}
2. 发起请求并解析
state = "上周收到的商品有破损,希望退款,请尽快处理。"
resp = model.decide(state=state, questions=questions)
answers = resp["answers"]
# Noul:取 true 的概率
p_refund = answers["is_refund_request"]["probabilities"]["true"]
# Choice:取最大概率选项及其概率
dept_probs = answers["department"]["probabilities"]
dept = max(dept_probs, key=dept_probs.get)
p_dept = dept_probs[dept]
# Score:取期望值与各档概率
urgency_ev = answers["urgency"]["expected_value"]
urgency_probs = answers["urgency"]["probabilities"]
3. 按阈值分流
THRESHOLD = 0.9 # 先用占位值,校准后再定
if p_dept < THRESHOLD:
route = "human_review"
else:
route = dept
if p_refund >= THRESHOLD:
tag = "refund"
else:
tag = "other"
# 紧急度用期望值做排序键,而不是用单一档位
priority = urgency_ev
4. 校准阈值
把上面流程跑在你的标注样本上,收集每个样本的最大概率与是否正确,然后:
- 按模型卡建议的温度参数做重拟合(有的模型自带默认温度,仍建议用自己的数据重拟);
- 重算概率后,画出「最大概率区间」与「实际正确率」的对应关系;
- 选一个正确率满足业务要求的区间下界作为阈值,而不是直接沿用示例里的 0.9。
如果发现高概率区间并不比中概率区间更准,说明该模型在你的数据上校准不佳,此时要么换模型,要么放弃阈值分流、改为全量人工复核关键类别。
5. 上线后持续监控
把每次请求的最大概率、最终路由结果、以及人工复核的纠正记录一起落库。校准会随流量分布漂移,阈值需要定期重估。
注意事项
概率不等于正确率
多个模型卡都明确写了这一点:概率是模型输出,会随 state、instructions、criteria 的写法变化,不构成正确率保证。有的模型卡还指出,其置信度只在留出数据上验证过,实际流量上要先测再用阈值。因此「温度重拟合 + 自己数据上重评估」是使用前提,不是可选项。
Score 类型普遍偏弱
有模型卡明确指出,Score 是三类问题中最弱的一类,并测到了位置偏置;也有模型卡报告在四档以上的 Score 中存在位置偏置。这意味着档位顺序、档位数量都可能影响结果。使用 Score 时建议:档位数不要过多;对档位顺序做打乱测试;用期望值而非单一最高档做下游排序。
Noul 可能低估 true
有模型卡报告 Noul 在某些情况下会低报 true。如果你的业务对漏判更敏感(例如合规判定),阈值要相应下调,并用召回率而非准确率来评估。
选项数量存在上限
采用「读选项代码 logit」方式的模型通常有固定的选项槽位,超过上限会分组处理。分组会改变概率的归一化范围,跨组比较概率时要格外小心。
多问题合并处理有副作用
有的模型把多个问题放进一条序列处理,问题之间会互相 attend。模型卡对此的说明是:把相关的方面放在一起有利,把彼此独立的候选放在一起则不利。如果你的多个问题逻辑上互不相关,考虑拆成多次请求。
输入长度限制
部分小型 encoder 模型使用局部注意力窗口,输入过长时精度会下降。长文本场景要么截断,要么换用支持长上下文的 decoder 系模型。
特殊推理模式可能报错
有的模型版本被设计为「不思考」的即时模式,对它请求推理模式会直接报错。接入前确认该版本支持哪些调用模式。
访问条件可能受限
个别模型的仓库需要令牌才能访问,或权重公开情况不明确。选型时把「能否稳定获取权重」作为硬性条件之一。
评估集不能直接当采用依据
公开的评估集适合做一次初筛,但它们通常不是你的业务分类任务本身。另外要注意训练数据与评估数据的重叠:有的模型在训练中包含了某些公开评估集的训练分片,在这些集合上的分数会偏高。用公开集筛掉明显不合适的模型,最终采用与否仍取决于你自己标注数据上的表现。
分数刻度不是正确率
部分公开基准的分数是经过基准值调整的 0 到 100 分,不是正确率;Score 类任务按期望值计分,即使最高概率选项正确,分数也可能变化。跨模型比较时先确认分数口径一致。
易变信息以官网为准
模型规模、许可证、可用地区、价格与配额更新很快。本文涉及的规格仅用于说明选型维度,实际接入前请核对模型卡当前 revision 与服务方的最新条款。