组合评分
"这条线索值不值得跟进?""这份申请材料靠不靠谱?"——这类问题看起来是一个判断,实际上是好几个判断的加权和。如果把它原封不动地丢给模型,你得到的是一个黑箱数字:不知道它看重了什么、忽略了什么,业务优先级变了也无从调整。
组合评分的做法是:
- 把综合判断拆成若干原子维度,每个维度一个 Score(或 Noul)问题;
- 一次调用同时问完(参见推测式扇出);
- 在代码里把每个答案归一化到 0–1,再按你定义的权重合成总分;
- 业务优先级变化时,改权重常量,而不是改提示词。
一个例子:连锁餐饮招商加盟线索评分
一个连锁茶饮品牌每天从官网、展会、抖音私信收到几百条加盟咨询。招商经理只有精力跟进其中一小部分。我们希望给每条线索打一个 0–100 的优先级分。
招商团队认为,一条好线索取决于四个相对独立的维度:
| 维度 | 问题类型 | 说明 |
|---|---|---|
| 资金实力 | Score(4 档) | 是否明确提到可投入的资金 |
| 行业经验 | Score(4 档) | 是否有餐饮或零售经营经验 |
| 选址明确度 | Score(3 档) | 是否已有意向城市、商圈或铺位 |
| 意向紧迫度 | Score(3 档) | 是计划近期开店,还是随便问问 |
外加一个"一票否决"项:是否是同行打探或代理中介(Noul)。
第一步:一次问完所有维度
python
from xiangxin import XiangxinClient, Noul, Score
client = XiangxinClient()
lead = {
"source": "抖音私信",
"message": (
"你好,我在长沙开了三年奶茶店,现在想换个品牌。"
"手上大概能拿出 60 万,看中了五一广场附近一个 40 平的铺子,"
"房东月底前要答复,想尽快了解一下加盟政策。"
),
}
QUESTIONS = {
"capital": Score(
instructions="咨询者在 `message` 中透露的可投入资金情况",
criteria=[
"完全没有提到资金",
"只是模糊地说'有一些预算'",
"给出了大致金额,但低于 30 万",
"明确给出 30 万及以上的可投入金额",
],
),
"experience": Score(
instructions="咨询者的餐饮或零售经营经验",
criteria=[
"没有提到任何经营经验",
"有其他行业的创业或管理经验",
"有零售或餐饮相关的打工经验",
"自己经营过餐饮或茶饮门店",
],
),
"location": Score(
instructions="咨询者对开店选址的明确程度",
criteria=[
"没有提到任何地点",
"只提到城市",
"已经有具体的商圈或铺位",
],
),
"urgency": Score(
instructions="咨询者计划开店的时间紧迫程度",
criteria=[
"随便了解,没有时间计划",
"计划在半年到一年内",
"希望近期(三个月内)就开店,或有明确的时间节点",
],
),
"is_broker_or_rival": Noul(
instructions="这条咨询是否像是同行打探、加盟代理中介或广告推销,而不是真实的开店意向?",
),
}
resp = client.system_one(state=lead, questions=QUESTIONS)
a = resp.answers第二步:在代码里归一化与加权
Score 的 score 落在 0 到 档位数 − 1 之间(可能介于两档之间)。不同维度档位数不同,先除以最大档位把它们归一到 0–1:
python
# 权重集中定义:招商团队只需要审阅和修改这里
WEIGHTS = {
"capital": 0.35,
"experience": 0.25,
"location": 0.20,
"urgency": 0.20,
}
BROKER_VETO = 0.7 # 疑似中介/同行的概率超过它,直接判 0 分
def normalized(answer) -> float:
"""把 Score 答案映射到 0–1。"""
max_level = len(answer.legend) - 1
return answer.score / max_level
def lead_score(a) -> int:
if a["is_broker_or_rival"].noul > BROKER_VETO:
return 0
total = sum(w * normalized(a[k]) for k, w in WEIGHTS.items())
return round(total * 100)
print(lead_score(a)) # 示意输出:98
for k in WEIGHTS:
print(k, round(normalized(a[k]), 2))每个维度的分数都能单独展示给招商经理:"资金 1.0、经验 1.0、选址 1.0、紧迫度 0.9",他们一眼就能看出这条线索为什么排在前面。
为什么不直接问"这条线索好不好"
| 一个综合问题 | 组合评分 | |
|---|---|---|
| 可解释性 | 只有一个数,不知道依据 | 每个维度都有独立分数 |
| 可调性 | 改优先级要改提示词、重新测试 | 改一个权重常量 |
| 可靠性 | 模型要在一次判断里权衡多个因素,容易不稳定 | 每个问题只判断一件事 |
| 耗时与成本 | 一个问题 | 多几个问题,几乎不增加耗时,只多一点问题 token |
最重要的一点是把权衡交还给业务。模型擅长判断"资金情况属于哪一档",但"资金和经验哪个更重要"是你们团队的商业决策,不应该由模型隐式决定。
调整权重,而不是提示词
下个季度品牌策略变了:要在三四线城市快速铺店,更看重紧迫度,不那么在意经验。只需要:
python
WEIGHTS = {
"capital": 0.30,
"experience": 0.10,
"location": 0.20,
"urgency": 0.40,
}问题定义一个字都不用动,历史线索的原子分数甚至可以直接复用重新计算。
用数据拟合权重
如果你有历史结果(例如哪些线索最终签约),可以把各维度的归一化分数作为特征,用逻辑回归等简单模型拟合权重。象信负责把自由文本变成可靠的数值特征,传统模型负责学习它们的组合方式。
设计维度的要点
- 维度之间尽量独立。 "资金实力"和"是否是有钱人"高度重叠,会让这个因素被重复计分。
- 每一档都写清楚。 档位描述越具体、越可观察,打分越稳定。"明确给出 30 万及以上"好过"资金充足"。
- 档位按从低到高排列。 Score 的第 0 档是最低端,最后一档是最高端,
score的数值依赖这个顺序。 - 否决项用 Noul,放在加权之外。 硬性条件不该被其他维度的高分"平均掉"。
- 不要用 Score 反推精确数值。
score = 2.4不代表"资金约 45 万";分数适合比较和设阈值,不适合插值还原具体数字,详见已知短板。
何时使用 / 何时不用
适合使用:
- 综合判断由几个能说清楚的因素构成;
- 需要向业务方解释分数来源;
- 优先级会随时间变化。
不适合:
- 判断本身就是单一维度("用户是否要求退款"),用一个 Noul 即可;
- 要从一组互斥选项里选一个,用 Choice 更直接;
- 因素无法用文字描述成可观察的档位——那说明问题还没想清楚。
常见陷阱
- 不归一化就相加。 4 档和 3 档的 Score 数值范围不同,直接相加会让档位多的维度天然占优。
- 把权重写进提示词。 "请重点考虑资金"这样的话应该变成代码里的权重。
- 拿
confidence当分数用。 置信度表示模型有多确定,不表示线索有多好。低置信度的维度可以单独标注"信息不足",而不是简单地降分。

