置信度门控路由
每个 Choice 和 Score 答案都带有 confidence。它和答案本身是两个维度:
- 答案告诉你"是什么"——该走哪条分支;
- 置信度告诉你"敢不敢走"——是直接执行、先确认,还是交给别人。
置信度门控路由就是在代码里同时使用这两个维度。它是用象信搭建可靠系统的基础:模型可以说"这题我没把握",而你的程序据此改变行为,而不是硬着头皮执行一个猜出来的答案。
置信度从哪里来
置信度是从 probabilities 算出来的统计量,你也可以用概率自己算:
- Choice:
confidence = (n·peak − 1) / (n − 1),其中n是选项数、peak是最高概率。所有概率集中在一个选项上时为 1,均匀分布时为 0。 - Score:
confidence = max(pᵢ),即概率最高那一档的概率。 - Noul 没有单独的置信度;
noul离 0.5 越远,模型越确定。
详见置信度。
三个区间
最常用的起点是把置信度划成三段,每段对应一种系统行为:
| 区间 | 系统行为 | 例子 |
|---|---|---|
| 高 | 自动执行 | 直接把工单派到对应队列 |
| 中 | 谨慎执行:向用户确认、打标复核、补充信息 | "您是想要转账给张三吗?" |
| 低 | 不执行:转人工、请用户澄清、交给更强的模型 | 转人工坐席 |
区间的边界不是一个全局常数,而应随操作的风险变化。
一个例子:手机银行语音助手
用户对着手机银行说一句话,你需要识别意图。查余额看错了无伤大雅;确认一笔转账看错了则可能造成真金白银的损失。
text
┌─ confidence < 0.55 或 other ───────────▶ 转人工坐席
用户语音转写 ──▶ 象信 ──▶│─ query_balance 且 ≥ 0.55 ──────────────▶ 直接显示余额
intent(Choice) │─ confirm_transfer 且 0.55 ~ 0.9 ───────▶ 二次确认
│─ confirm_transfer 且 ≥ 0.9 ────────────▶ 进入转账确认页
└─ freeze_card(任意置信度) ────────────▶ 先冻结,再人工回访第一步:识别意图
python
from xiangxin import XiangxinClient, Choice
client = XiangxinClient()
INTENT = Choice(
instructions="用户这句话想让手机银行执行什么操作?",
criteria={
"query_balance": "查询账户余额或最近交易",
"confirm_transfer": "确认执行一笔已经填写好的待转账",
"freeze_card": "挂失或冻结银行卡",
"other": "以上都不是,或者是闲聊、咨询",
},
)
utterance = "嗯……那个刚才给我妈转的两千块,就确认了吧"
resp = client.system_one(
state={"utterance": utterance, "pending_transfer": {"to": "王秀兰", "amount_yuan": 2000}},
questions={"intent": INTENT},
)
intent = resp.answers["intent"]第二步:按风险分级门控
python
# 所有阈值集中定义,便于审阅和调整
FLOOR = 0.55 # 低于它,无论什么意图都不自动处理
TRANSFER_AUTO = 0.90 # 转账类高风险操作需要更高的置信度
def handle(intent) -> str:
# 冻结卡片是保护性操作:宁可误冻,也不能漏冻
if intent.choice == "freeze_card":
return "freeze_then_callback"
if intent.confidence < FLOOR or intent.choice == "other":
return "human_agent"
if intent.choice == "query_balance":
return "show_balance"
if intent.choice == "confirm_transfer":
if intent.confidence >= TRANSFER_AUTO:
return "open_transfer_confirm_page"
return "ask_again" # “您是要确认给王秀兰转账 2,000 元吗?”
return "human_agent"
print(intent.choice, intent.confidence, handle(intent))
# 示意输出:confirm_transfer 0.84 ask_again注意三点:
- 地板阈值(
FLOOR)兜住模型真正拿不准的情况。 - 高风险操作有更高的门槛。同样 0.84 的置信度,查余额可以直接做,确认转账就要再问一句。
- 不对称风险要不对称处理。冻结卡片误判的代价远小于漏判,所以它不受置信度门控。风险的方向由你的业务决定,而不是由模型决定。
升级到更强的系统
"低置信度"分支不一定是人工。常见的升级目标:
| 升级目标 | 适合 | 代价 |
|---|---|---|
| 请用户澄清 | 面向用户的交互,歧义来自输入本身 | 多一轮对话 |
| 大语言模型(带推理) | 需要多步推理、查阅长文档的疑难样本 | 慢、贵,但只处理少量样本 |
| 人工审核 | 高风险、需要担责的决定 | 最贵,但最稳 |
这形成一个级联:象信一号以毫秒级延迟和极低成本处理绝大多数"一眼就能判断"的样本,只把少数没把握的样本交给昂贵的环节。你付出的推理成本与难样本的比例成正比,而不是与总量成正比。
python
def classify_with_cascade(text: str) -> tuple[str, str]:
resp = client.system_one(state={"text": text}, questions={"intent": INTENT})
ans = resp.answers["intent"]
if ans.confidence >= 0.8:
return ans.choice, "xiangxin"
# 不确定:交给你自己的大模型或人工流程(此处为示意函数)
return ask_reasoning_llm(text), "llm"
def ask_reasoning_llm(text: str) -> str:
raise NotImplementedError("接入你自己的大模型或人工审核")用标注数据定阈值
阈值不应拍脑袋。推荐做法:
- 从真实流量里抽几百条样本,人工标注正确答案;
- 用象信跑一遍,记录每条的
choice与confidence; - 对一组候选阈值,统计覆盖率(置信度 ≥ 阈值、会被自动处理的比例)和这部分样本上的准确率;
- 按业务可接受的错误率选择阈值。
python
def coverage_table(records, thresholds=(0.3, 0.5, 0.7, 0.8, 0.9)):
"""records: [(predicted, confidence, gold), ...]"""
rows = []
for t in thresholds:
kept = [(p, g) for p, c, g in records if c >= t]
coverage = len(kept) / len(records)
accuracy = sum(p == g for p, g in kept) / len(kept) if kept else float("nan")
rows.append((t, coverage, accuracy))
return rows得到的表大致长这样(示意数据):
| 阈值 | 覆盖率(自动处理比例) | 自动处理部分的准确率 |
|---|---|---|
| 0.3 | 97% | 91% |
| 0.5 | 91% | 95% |
| 0.7 | 82% | 98% |
| 0.8 | 74% | 99% |
| 0.9 | 58% | 99.5% |
读法:如果业务要求自动处理部分的错误率低于 2%,就选 0.7——大约 82% 的流量自动处理,剩下 18% 进入人工或大模型。阈值越高,自动化程度越低、准确率越高,这是一条你可以按需滑动的曲线。
注意
- 阈值和具体模型版本相关。如果你基于某个版本调好了阈值,请在请求中固定版本号
xiangxin-1.0.0,而不是使用会随新版本移动的别名,详见模型。 - 象信一号对中英文都能处理,但不同语言、不同领域的表现并不一致。请务必在你自己的数据上测定阈值。
何时使用 / 何时不用
适合使用:
- 答错的代价明显大于"先确认一下"的代价;
- 存在可靠的兜底路径(人工、澄清、大模型);
- 同一系统里有风险高低不同的操作。
不需要门控的情况:
- 你只需要"选最可能的那一个",比如排序或推荐的第一位——直接用
choice即可; - 你有自己的统计方法(例如对概率做期望、做贝叶斯更新),此时应直接使用
probabilities,而不是压缩后的confidence; - 答错完全可逆且无害。
常见陷阱
- 到处都加阈值。 置信度门控是为"要不要行动"准备的,不是每个答案都需要。
- 把 Noul 的阈值照搬到 Choice。 两种原语的数值含义不同,不能共用阈值。
- 全局只有一个阈值。 风险不同的操作应该有不同的门槛。
- 低置信度就丢弃。 低置信度本身是有用信号:它常常意味着选项定义有歧义、缺少"其他"选项,或者状态里信息不足。定期查看低置信度样本,改进问题定义。

