置信度
每个 Choice 和 Score 答案都带有 probabilities:选项(Choice)或档位(Score)上的概率分布。分布的形状说明了模型有多确定:集中在一个结果上,就是有把握;摊开在几个结果上,就是拿不准。
confidence 把这个形状压缩成 0 到 1 之间的一个数,方便你直接设阈值,不用自己算。Noul 答案没有 confidence,原因见 下文。
置信度由概率算出
confidence 不是模型额外"报告"的一个数,而是在返回之前,根据答案已有的 probabilities 算出来的统计量。公式是公开且固定的:
| 类型 | 公式 | 含义 |
|---|---|---|
| Choice | (n × p_max − 1) / (n − 1) | n 为选项数,p_max 为最高概率。均匀分布时为 0,全部集中在一个选项时为 1 |
| Score | p_max | 概率最高那一档的概率 |
算一个例子
一个有 4 个选项的 Choice,返回:
{"probabilities": {"物流": 0.70, "售后": 0.20, "账务": 0.10, "其他": 0.00}}最高概率 0.70,于是:
confidence = (4 × 0.70 − 1) / (4 − 1) = 1.80 / 3 = 0.60为什么不直接用 0.70?因为 0.70 在不同选项数下意义不同:两个选项时,随便猜也有 0.50,0.70 只比瞎猜好一点(置信度 0.40);二十个选项时,瞎猜只有 0.05,0.70 已经相当集中(置信度约 0.68)。Choice 的公式把"比均匀分布集中了多少"换算到 0–1,使不同大小的选项集可以用同一套阈值。
Score 的置信度就是最高档的概率。例如三档分布 0.00 / 0.55 / 0.45,置信度为 0.55;score = 0 × 0.00 + 1 × 0.55 + 2 × 0.45 = 1.45。
这是一个好用的默认值,但不是唯一选择
confidence 适合大多数场景,但你并不被这个定义锁死:完整的 probabilities 就在响应里。比如你更关心"前两名的差距"(margin),或者想用熵来衡量不确定性,都可以在代码里自己算:
probs = sorted(resp.answers["route"].probabilities.values(), reverse=True)
margin = probs[0] - probs[1] # 第一名比第二名高出多少对于 Choice,置信度低通常意味着没有哪个选项明显胜出:可能是选项之间有重叠,也可能是输入本来就属于多个选项。对于 Score,置信度低通常意味着档位定义有歧义、这个问题同时在衡量几件事,或者 state 里信息不够。
Noul 为什么没有置信度
Noul 的分布只有"是"和"否"两个结果,noul 这一个数已经完整描述了它:noul = 0.93 就意味着"否"的概率是 0.07。把握程度就体现在离 0.5 有多远:
- 0.95 或 0.05:非常确定;
- 0.70 或 0.30:倾向明显,但不确定;
- 0.5 附近:模型认为两种可能差不多。
所以对 Noul,你不需要单独的置信度,而是直接用两个阈值划出三个区间,见 Noul 是非题。如果非要一个 0–1 的"确定度",可以用 abs(noul - 0.5) * 2。
"我不知道"是有用的信号
一个系统,无论是人还是机器,如果无法诚实地表达不确定,就不值得信任。
置信度给了模型一个内建的方式来说"这个我拿不准"。你的代码因此可以针对不同的确定程度采取不同的行为,这是构建可靠系统的基础。与之相比,让一个生成式大模型"输出 JSON 并附上 0–1 的把握分数",它给出的数字往往只是一段看起来合理的文字,和实际对错没有稳定关系。
三个区间,三种行为
一个实用的起点是把置信度分成三段,每段对应一种系统行为:
| 区间 | 行为 |
|---|---|
| 高 | 自动执行。模型判断清晰,无需人工介入。 |
| 中 | 谨慎执行。答案合理但不确定:请用户确认、标记待复核,或者先补充信息再行动。 |
| 低 | 不执行。转人工、请用户澄清,或者交给另一个系统(比如一个推理更强但更慢的大模型)。 |
区间边界画在哪里,取决于风险。这就是 置信度门控路由 模式。
阈值随风险变化
置信度阈值不是一个全局数字。同一个系统里,不同动作应该按"做错的后果"设不同的门槛。
下面是一个手机银行 App 的智能助手。用户的一句话可能是查余额、确认一笔转账,或者冻结银行卡:
from xiangxin import Choice, XiangxinClient
client = XiangxinClient()
UNSURE = 0.5 # 低于它:模型拿不准,不猜
CONFIRM_FREE = 0.9 # 高风险操作免确认所需的置信度
def handle(user_message: str, account_id: str) -> None:
resp = client.system_one(
state=user_message,
questions={
"action": Choice(
instructions="用户想做什么?",
criteria={
"check_balance": "查询账户余额或近期交易",
"approve_transfer": "确认或授权一笔待处理的转账",
"freeze_card": "挂失或冻结银行卡",
"support": "咨询问题或寻求帮助",
},
),
},
)
action = resp.answers["action"]
if action.confidence < UNSURE:
# 模型确实拿不准,不要猜
route_to_human(user_message)
elif action.choice == "check_balance":
# 低风险:只读操作,展示错了也能挽回
show_balance(account_id)
elif action.choice == "freeze_card":
# 冻结宁可错冻:漏掉一个真实挂失的代价更高
freeze_card(account_id)
notify_user(account_id, "已为您临时冻结银行卡,如非本人操作可一键解冻。")
elif action.choice == "approve_transfer":
if action.confidence >= CONFIRM_FREE:
# 高风险、高置信:仍然走一次确认,但流程更短
confirm_then_execute(account_id)
else:
# 高风险、中等置信:先让用户明确确认
ask_user_to_confirm(account_id)
else:
route_to_support_bot(user_message)0.5 的下限拦住了模型明确表示没把握的输入。在此之上,动钱的操作比只读操作要求更高的置信度才能减少确认步骤;而冻结卡这种"做了可以撤销、不做可能损失更大"的操作,门槛反而可以放低。风险偏好由你的代码表达,模型只负责如实报告它有多确定。
电商场景同理:自动给差评用户发 5 元无门槛券,阈值可以低一些;自动为订单全额退款,阈值应该高得多,或者干脆永远走人工复核。
校准说的是"一群预测"
象信一号经过训练,使它的概率是校准的:在所有它给出约 0.8 的答案里,大约 80% 是对的。几点需要正确理解:
- 校准是对一批预测的统计性质,不保证任何单个答案正确。0.95 的答案仍然可能错。
- 校准依赖数据分布。 在我们的评测集上校准良好,不代表在你的业务数据上同样好:领域术语、行文风格、中英混杂程度都会影响。
- 所以阈值必须用你自己的数据来定。 做法很简单:拿一两百条已知正确答案的样本,跑一遍,按置信度分桶统计准确率,找到"准确率达到业务要求"的那条线。
from collections import defaultdict
# samples: [(state, 正确答案), ...],resps: 对应的响应
buckets = defaultdict(lambda: [0, 0]) # 置信度桶 → [答对数, 总数]
for (state, truth), resp in zip(samples, resps):
ans = resp.answers["action"]
b = min(int(ans.confidence * 10), 9) / 10 # 0.0, 0.1, …, 0.9
buckets[b][0] += int(ans.choice == truth)
buckets[b][1] += 1
for b in sorted(buckets):
right, total = buckets[b]
print(f"置信度 ≥ {b:.1f}: 准确率 {right / total:.0%}({total} 条)")从保守的阈值开始,上线后持续观察,再逐步调整。
调好阈值后,固定模型版本
别名 xiangxin-latest 会随新版本发布而移动,新模型的概率分布可能与旧版本不同。如果你已经针对某个版本仔细调过阈值,请在请求里写明版本号(例如 model="xiangxin-1.0.0"),等在新版本上重新验证过阈值后再切换。响应中的 model 字段总是报告实际应答的版本号,建议记入日志。见 模型。
近似确定,而非逐位一致
同一个请求重复发送,概率可能在小数点后第二位上有 ±0.01 左右的波动。这来自 GPU 上 bf16 数值计算与批处理组合的差异,不是随机采样。设阈值时不要把线正好画在你样本的某个取值上,并留出一点余量。详见 已知短板。
下一步
- 置信度门控路由:把置信度作为第二个维度来做路由。
- Choice 选择题 与 Score 评分:置信度在两种类型中的具体表现。
- 如何用象信构建:让代码掌控流程,把窄而明确的判断交给模型。

